EdgeCases.java revision 3792:d516975e8110
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8154283 8167320
27 * @summary tests for multi-module mode compilation
28 * @library /tools/lib
29 * @modules
30 *      jdk.compiler/com.sun.tools.javac.api
31 *      jdk.compiler/com.sun.tools.javac.code
32 *      jdk.compiler/com.sun.tools.javac.main
33 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase
34 * @run main EdgeCases
35 */
36
37import java.io.Writer;
38import java.nio.file.Files;
39import java.nio.file.Path;
40import java.nio.file.Paths;
41import java.util.Arrays;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Objects;
45import java.util.Set;
46
47import javax.lang.model.element.Element;
48import javax.tools.JavaCompiler;
49import javax.tools.JavaFileObject;
50import javax.tools.StandardJavaFileManager;
51import javax.tools.ToolProvider;
52
53import com.sun.source.tree.CompilationUnitTree;
54//import com.sun.source.util.JavacTask; // conflicts with toolbox.JavacTask
55import com.sun.tools.javac.api.JavacTaskImpl;
56import com.sun.tools.javac.code.Symbol.ModuleSymbol;
57import com.sun.tools.javac.code.Symtab;
58
59import toolbox.JarTask;
60import toolbox.JavacTask;
61import toolbox.Task;
62import toolbox.Task.Expect;
63import toolbox.Task.OutputKind;
64
65public class EdgeCases extends ModuleTestBase {
66
67    public static void main(String... args) throws Exception {
68        new EdgeCases().runTests();
69    }
70
71    @Test
72    public void testAddExportUndefinedModule(Path base) throws Exception {
73        Path src = base.resolve("src");
74        tb.writeJavaFiles(src, "package test; import undefPackage.Any; public class Test {}");
75        Path classes = base.resolve("classes");
76        tb.createDirectories(classes);
77
78        List<String> log = new JavacTask(tb)
79                .options("--add-exports", "undefModule/undefPackage=ALL-UNNAMED",
80                         "-XDrawDiagnostics")
81                .outdir(classes)
82                .files(findJavaFiles(src))
83                .run(Task.Expect.FAIL)
84                .writeAll()
85                .getOutputLines(Task.OutputKind.DIRECT);
86
87        List<String> expected = Arrays.asList("- compiler.warn.module.for.option.not.found: --add-exports, undefModule",
88                                              "Test.java:1:34: compiler.err.doesnt.exist: undefPackage",
89                                              "1 error", "1 warning");
90
91        if (!expected.equals(log))
92            throw new Exception("expected output not found: " + log);
93    }
94
95    @Test
96    public void testModuleSymbolOutterMostClass(Path base) throws Exception {
97        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
98        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
99            Path moduleSrc = base.resolve("module-src");
100            Path m1 = moduleSrc.resolve("m1");
101
102            tb.writeJavaFiles(m1, "module m1 { }");
103
104            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
105            com.sun.source.util.JavacTask task =
106                (com.sun.source.util.JavacTask) compiler.getTask(null, fm, null, null, null, files);
107
108            task.analyze();
109
110            ModuleSymbol msym = (ModuleSymbol) task.getElements().getModuleElement("m1");
111
112            msym.outermostClass();
113        }
114    }
115
116    @Test
117    public void testParseEnterAnalyze(Path base) throws Exception {
118        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
119        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
120            Path moduleSrc = base.resolve("module-src");
121            Path m1 = moduleSrc.resolve("m1");
122
123            tb.writeJavaFiles(m1, "module m1 { }",
124                                  "package p;",
125                                  "package p; class T { }");
126
127            Path classes = base.resolve("classes");
128            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
129            List<String> options = Arrays.asList("-d", classes.toString(), "-Xpkginfo:always");
130            JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, null, options, null, files);
131
132            Iterable<? extends CompilationUnitTree> parsed = task.parse();
133            Iterable<? extends Element> entered = task.enter(parsed);
134            Iterable<? extends Element> analyzed = task.analyze(entered);
135            Iterable<? extends JavaFileObject> generatedFiles = task.generate(analyzed);
136
137            Set<String> generated = new HashSet<>();
138
139            for (JavaFileObject jfo : generatedFiles) {
140                generated.add(jfo.getName());
141            }
142
143            Set<String> expected = new HashSet<>(
144                    Arrays.asList(Paths.get("testParseEnterAnalyze", "classes", "p", "package-info.class").toString(),
145                                  Paths.get("testParseEnterAnalyze", "classes", "module-info.class").toString(),
146                                  Paths.get("testParseEnterAnalyze", "classes", "p", "T.class").toString())
147            );
148
149            if (!Objects.equals(expected, generated))
150                throw new AssertionError("Incorrect generated files: " + generated);
151        }
152    }
153
154    @Test
155    public void testModuleImplicitModuleBoundaries(Path base) throws Exception {
156        Path src = base.resolve("src");
157        Path src_m1 = src.resolve("m1");
158        tb.writeJavaFiles(src_m1,
159                          "module m1 { exports api1; }",
160                          "package api1; public class Api1 { public void call() { } }");
161        Path src_m2 = src.resolve("m2");
162        tb.writeJavaFiles(src_m2,
163                          "module m2 { requires m1; exports api2; }",
164                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
165        Path src_m3 = src.resolve("m3");
166        tb.writeJavaFiles(src_m3,
167                          "module m3 { requires m2; }",
168                          "package test; public class Test { { api2.Api2.get().call(); api2.Api2.get().toString(); } }");
169        Path classes = base.resolve("classes");
170        tb.createDirectories(classes);
171
172        String log = new JavacTask(tb)
173                .options("-XDrawDiagnostics",
174                         "--module-source-path", src.toString())
175                .outdir(classes)
176                .files(findJavaFiles(src))
177                .run(Task.Expect.FAIL)
178                .writeAll()
179                .getOutput(Task.OutputKind.DIRECT);
180
181        if (!log.contains("Test.java:1:52: compiler.err.not.def.access.class.intf.cant.access: call(), api1.Api1") ||
182            !log.contains("Test.java:1:76: compiler.err.not.def.access.class.intf.cant.access: toString(), java.lang.Object"))
183            throw new Exception("expected output not found");
184    }
185
186    @Test
187    public void testAssignClassToAutomaticModule(Path base) throws Exception {
188        //check that if a ClassSymbol belongs to an automatic module, it is properly assigned and not
189        //duplicated when being accessed through a classfile.
190        Path automaticSrc = base.resolve("automaticSrc");
191        tb.writeJavaFiles(automaticSrc, "package api1; public class Api1 {}");
192        Path automaticClasses = base.resolve("automaticClasses");
193        tb.createDirectories(automaticClasses);
194
195        String automaticLog = new JavacTask(tb)
196                                .outdir(automaticClasses)
197                                .files(findJavaFiles(automaticSrc))
198                                .run()
199                                .writeAll()
200                                .getOutput(Task.OutputKind.DIRECT);
201
202        if (!automaticLog.isEmpty())
203            throw new Exception("expected output not found: " + automaticLog);
204
205        Path modulePath = base.resolve("module-path");
206
207        Files.createDirectories(modulePath);
208
209        Path automaticJar = modulePath.resolve("a-1.0.jar");
210
211        new JarTask(tb, automaticJar)
212          .baseDir(automaticClasses)
213          .files("api1/Api1.class")
214          .run();
215
216        Path src = base.resolve("src");
217        Path src_m2 = src.resolve("m2");
218        tb.writeJavaFiles(src_m2,
219                          "module m2 { requires a; exports api2; }",
220                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
221        Path src_m3 = src.resolve("m3");
222        tb.writeJavaFiles(src_m3,
223                          "module m3 { requires a; requires m2; }",
224                          "package test; public class Test { { api2.Api2.get(); api1.Api1 a1; } }");
225        Path classes = base.resolve("classes");
226        tb.createDirectories(classes);
227
228        new JavacTask(tb)
229                .options("--module-path", modulePath.toString(),
230                         "--module-source-path", src.toString())
231                .outdir(classes)
232                .files(findJavaFiles(src_m2))
233                .run()
234                .writeAll();
235
236        new JavacTask(tb)
237                .options("--module-path", modulePath.toString(),
238                         "--module-source-path", src.toString())
239                .outdir(classes)
240                .files(findJavaFiles(src_m3))
241                .run()
242                .writeAll();
243    }
244
245    @Test
246    public void testEmptyImplicitModuleInfo(Path base) throws Exception {
247        Path src = base.resolve("src");
248        Path src_m1 = src.resolve("m1");
249        Files.createDirectories(src_m1);
250        try (Writer w = Files.newBufferedWriter(src_m1.resolve("module-info.java"))) {}
251        tb.writeJavaFiles(src_m1,
252                          "package test; public class Test {}");
253        Path classes = base.resolve("classes");
254        tb.createDirectories(classes);
255
256        new JavacTask(tb)
257                .options("--source-path", src_m1.toString(),
258                         "-XDrawDiagnostics")
259                .outdir(classes)
260                .files(findJavaFiles(src_m1.resolve("test")))
261                .run(Task.Expect.FAIL)
262                .writeAll();
263
264        tb.writeJavaFiles(src_m1,
265                          "module m1 {}");
266
267        new JavacTask(tb)
268                .options("--source-path", src_m1.toString())
269                .outdir(classes)
270                .files(findJavaFiles(src_m1.resolve("test")))
271                .run()
272                .writeAll();
273
274    }
275
276    @Test
277    public void testClassPackageClash(Path base) throws Exception {
278        Path src = base.resolve("src");
279        Path src_m1 = src.resolve("m1");
280        tb.writeJavaFiles(src_m1,
281                          "module m1 { exports test.m1; }",
282                          "package test.m1;\n" +
283                          "public class Test {}\n");
284        Path src_m2 = src.resolve("m2");
285        tb.writeJavaFiles(src_m2,
286                          "module m2 { requires m1; }",
287                          "package test;\n" +
288                          "public class m1 {}\n");
289        Path classes = base.resolve("classes");
290        tb.createDirectories(classes);
291
292        List<String> log = new JavacTask(tb)
293                .options("--module-source-path", src.toString(),
294                         "-XDrawDiagnostics")
295                .outdir(classes)
296                .files(findJavaFiles(src))
297                .run(Task.Expect.FAIL)
298                .writeAll()
299                .getOutputLines(Task.OutputKind.DIRECT);
300
301        List<String> expected = Arrays.asList(
302            "m1.java:2:8: compiler.err.clash.with.pkg.of.same.name: kindname.class, test.m1",
303            "1 error"
304        );
305
306        if (!expected.equals(log)) {
307            throw new IllegalStateException(log.toString());
308        }
309    }
310
311    @Test
312    public void testImplicitJavaBase(Path base) throws Exception {
313        Path src = base.resolve("src");
314        Path src_java_base = src.resolve("java.base");
315        Files.createDirectories(src_java_base);
316        tb.writeJavaFiles(src_java_base, "module java.base { exports java.lang; }");
317        tb.writeJavaFiles(src_java_base,
318                          "package java.lang; public class Object {}");
319        Path classes = base.resolve("classes");
320        tb.createDirectories(classes);
321
322        //module-info from source:
323        new JavacTask(tb)
324            .options("-sourcepath", src_java_base.toString())
325            .outdir(classes)
326            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
327            .run()
328            .writeAll();
329
330        //module-info from class:
331        if (!Files.exists(classes.resolve("module-info.class"))) {
332            throw new AssertionError("module-info.class not created!");
333        }
334
335        new JavacTask(tb)
336            .outdir(classes)
337            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
338            .run()
339            .writeAll();
340
341        //broken module-info.class:
342        Files.newOutputStream(classes.resolve("module-info.class")).close();
343
344        List<String> log = new JavacTask(tb)
345            .options("-XDrawDiagnostics")
346            .outdir(classes)
347            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
348            .run(Expect.FAIL)
349            .writeAll()
350            .getOutputLines(OutputKind.DIRECT);
351
352        List<String> expected = Arrays.asList(
353                "- compiler.err.cant.access: <error>.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.illegal.start.of.class.file))",
354                "1 error");
355
356        if (!expected.equals(log)) {
357            throw new AssertionError("Unexpected output: " + log);
358        }
359
360        //broken module-info.java:
361        Files.delete(classes.resolve("module-info.class"));
362
363        try (Writer out = Files.newBufferedWriter(src_java_base.resolve("module-info.java"))) {
364            out.write("class Broken {}");
365        }
366
367        log = new JavacTask(tb)
368            .options("-sourcepath", src_java_base.toString(),
369                                "-XDrawDiagnostics")
370            .outdir(classes)
371            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
372            .run(Expect.FAIL)
373            .writeAll()
374            .getOutputLines(OutputKind.DIRECT);
375
376        expected = Arrays.asList("X");
377
378        if (expected.equals(log)) {
379            throw new AssertionError("Unexpected output: " + log);
380        }
381    }
382
383    @Test
384    public void testModuleInfoNameMismatchSource(Path base) throws Exception {
385        Path src = base.resolve("src");
386        Path m1 = src.resolve("m1");
387        Files.createDirectories(m1);
388        tb.writeJavaFiles(m1, "module other { }",
389                              "package test; public class Test {}");
390        Path classes = base.resolve("classes");
391        tb.createDirectories(classes);
392
393        List<String> log = new JavacTask(tb)
394            .options("--module-source-path", src.toString(),
395                     "-XDrawDiagnostics")
396            .outdir(classes)
397            .files(findJavaFiles(m1.resolve("test").resolve("Test.java")))
398            .run(Expect.FAIL)
399            .writeAll()
400            .getOutputLines(OutputKind.DIRECT);
401
402        List<String> expected = Arrays.asList(
403                "module-info.java:1:1: compiler.err.module.name.mismatch: other, m1",
404                "- compiler.err.cant.access: m1.module-info, (compiler.misc.cant.resolve.modules)",
405                "2 errors");
406
407        if (!expected.equals(log)) {
408            throw new AssertionError("Unexpected output: " + log);
409        }
410    }
411
412    @Test
413    public void testModuleInfoNameMismatchClass(Path base) throws Exception {
414        Path src = base.resolve("src");
415        Files.createDirectories(src);
416        tb.writeJavaFiles(src, "module other { }",
417                               "package test; public class Test {}");
418        Path classes = base.resolve("classes");
419        Path m1Classes = classes.resolve("m1");
420        tb.createDirectories(m1Classes);
421
422        new JavacTask(tb)
423            .outdir(m1Classes)
424            .files(findJavaFiles(src))
425            .run()
426            .writeAll()
427            .getOutputLines(OutputKind.DIRECT);
428
429        Path src2 = base.resolve("src2");
430        Files.createDirectories(src2);
431        tb.writeJavaFiles(src2, "module use { requires m1; }");
432
433        Path classes2 = base.resolve("classes2");
434        tb.createDirectories(classes2);
435
436        List<String> log = new JavacTask(tb)
437            .options("--module-path", classes.toString(),
438                     "-XDrawDiagnostics")
439            .outdir(classes2)
440            .files(findJavaFiles(src2))
441            .run(Expect.FAIL)
442            .writeAll()
443            .getOutputLines(OutputKind.DIRECT);
444
445        List<String> expected = Arrays.asList(
446                "- compiler.err.cant.access: m1.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.name.mismatch: other, m1))",
447                "1 error");
448
449        if (!expected.equals(log)) {
450            throw new AssertionError("Unexpected output: " + log);
451        }
452    }
453
454    @Test
455    public void testGetDirectivesComplete(Path base) throws Exception {
456        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
457        JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, null);
458        Symtab syms = Symtab.instance(task.getContext());
459
460        syms.java_base.getDirectives();
461    }
462
463    @Test
464    public void testPackageInModuleInfo(Path base) throws Exception {
465        Path src = base.resolve("src");
466        Files.createDirectories(src);
467        tb.writeJavaFiles(src, "package p; module foo { }");
468        Path classes = base.resolve("classes");
469        tb.createDirectories(classes);
470
471        List<String> log = new JavacTask(tb)
472            .options("-XDrawDiagnostics", "-XDshould-stop.ifError=FLOW")
473            .outdir(classes)
474            .files(findJavaFiles(src))
475            .run(Expect.FAIL)
476            .writeAll()
477            .getOutputLines(OutputKind.DIRECT);
478
479        List<String> expected = Arrays.asList(
480                "module-info.java:1:1: compiler.err.no.pkg.in.module-info.java",
481                "1 error");
482
483        if (!expected.equals(log)) {
484            throw new AssertionError("Unexpected output: " + log);
485        }
486    }
487}
488