EdgeCases.java revision 4054:24fa5d195595
1/*
2 * Copyright (c) 2015, 2017, 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 8171098 8172809 8173068 8173117
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 *      jdk.compiler/com.sun.tools.javac.processing
34 *      jdk.compiler/com.sun.tools.javac.util
35 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase
36 * @run main EdgeCases
37 */
38
39import java.io.Writer;
40import java.nio.file.Files;
41import java.nio.file.Path;
42import java.nio.file.Paths;
43import java.util.Arrays;
44import java.util.HashSet;
45import java.util.List;
46import java.util.Objects;
47import java.util.Set;
48
49import javax.annotation.processing.AbstractProcessor;
50import javax.annotation.processing.RoundEnvironment;
51import javax.annotation.processing.SupportedAnnotationTypes;
52import javax.annotation.processing.SupportedOptions;
53import javax.lang.model.SourceVersion;
54import javax.lang.model.element.Element;
55import javax.lang.model.element.ModuleElement;
56import javax.lang.model.element.ModuleElement.RequiresDirective;
57import javax.lang.model.element.PackageElement;
58import javax.lang.model.element.TypeElement;
59import javax.lang.model.util.ElementFilter;
60import javax.lang.model.util.Elements;
61import javax.tools.JavaCompiler;
62import javax.tools.JavaFileObject;
63import javax.tools.StandardJavaFileManager;
64import javax.tools.ToolProvider;
65
66import com.sun.source.tree.CompilationUnitTree;
67//import com.sun.source.util.JavacTask; // conflicts with toolbox.JavacTask
68import com.sun.tools.javac.api.JavacTaskImpl;
69import com.sun.tools.javac.code.Symbol.ModuleSymbol;
70import com.sun.tools.javac.code.Symbol.PackageSymbol;
71import com.sun.tools.javac.code.Symtab;
72import com.sun.tools.javac.processing.JavacProcessingEnvironment;
73import com.sun.tools.javac.util.Context;
74
75import toolbox.JarTask;
76import toolbox.JavacTask;
77import toolbox.Task;
78import toolbox.Task.Expect;
79import toolbox.Task.OutputKind;
80
81public class EdgeCases extends ModuleTestBase {
82
83    public static void main(String... args) throws Exception {
84        new EdgeCases().runTests();
85    }
86
87    @Test
88    public void testAddExportUndefinedModule(Path base) throws Exception {
89        Path src = base.resolve("src");
90        tb.writeJavaFiles(src, "package test; import undefPackage.Any; public class Test {}");
91        Path classes = base.resolve("classes");
92        tb.createDirectories(classes);
93
94        List<String> log = new JavacTask(tb)
95                .options("--add-exports", "undefModule/undefPackage=ALL-UNNAMED",
96                         "-XDrawDiagnostics")
97                .outdir(classes)
98                .files(findJavaFiles(src))
99                .run(Task.Expect.FAIL)
100                .writeAll()
101                .getOutputLines(Task.OutputKind.DIRECT);
102
103        List<String> expected = Arrays.asList("- compiler.warn.module.for.option.not.found: --add-exports, undefModule",
104                                              "Test.java:1:34: compiler.err.doesnt.exist: undefPackage",
105                                              "1 error", "1 warning");
106
107        if (!expected.equals(log))
108            throw new Exception("expected output not found: " + log);
109    }
110
111    @Test
112    public void testModuleSymbolOutterMostClass(Path base) throws Exception {
113        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
114        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
115            Path moduleSrc = base.resolve("module-src");
116            Path m1 = moduleSrc.resolve("m1x");
117
118            tb.writeJavaFiles(m1, "module m1x { }");
119
120            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
121            com.sun.source.util.JavacTask task =
122                (com.sun.source.util.JavacTask) compiler.getTask(null, fm, null, null, null, files);
123
124            task.analyze();
125
126            ModuleSymbol msym = (ModuleSymbol) task.getElements().getModuleElement("m1x");
127
128            msym.outermostClass();
129        }
130    }
131
132    @Test
133    public void testParseEnterAnalyze(Path base) throws Exception {
134        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
135        try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
136            Path moduleSrc = base.resolve("module-src");
137            Path m1 = moduleSrc.resolve("m1x");
138
139            tb.writeJavaFiles(m1, "module m1x { }",
140                                  "package p;",
141                                  "package p; class T { }");
142
143            Path classes = base.resolve("classes");
144            Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(findJavaFiles(moduleSrc));
145            List<String> options = Arrays.asList("-d", classes.toString(), "-Xpkginfo:always");
146            JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, null, options, null, files);
147
148            Iterable<? extends CompilationUnitTree> parsed = task.parse();
149            Iterable<? extends Element> entered = task.enter(parsed);
150            Iterable<? extends Element> analyzed = task.analyze(entered);
151            Iterable<? extends JavaFileObject> generatedFiles = task.generate(analyzed);
152
153            Set<String> generated = new HashSet<>();
154
155            for (JavaFileObject jfo : generatedFiles) {
156                generated.add(jfo.getName());
157            }
158
159            Set<String> expected = new HashSet<>(
160                    Arrays.asList(Paths.get("testParseEnterAnalyze", "classes", "p", "package-info.class").toString(),
161                                  Paths.get("testParseEnterAnalyze", "classes", "module-info.class").toString(),
162                                  Paths.get("testParseEnterAnalyze", "classes", "p", "T.class").toString())
163            );
164
165            if (!Objects.equals(expected, generated))
166                throw new AssertionError("Incorrect generated files: " + generated);
167        }
168    }
169
170    @Test
171    public void testModuleImplicitModuleBoundaries(Path base) throws Exception {
172        Path src = base.resolve("src");
173        Path src_m1 = src.resolve("m1x");
174        tb.writeJavaFiles(src_m1,
175                          "module m1x { exports api1; }",
176                          "package api1; public class Api1 { public void call() { } }");
177        Path src_m2 = src.resolve("m2x");
178        tb.writeJavaFiles(src_m2,
179                          "module m2x { requires m1x; exports api2; }",
180                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
181        Path src_m3 = src.resolve("m3x");
182        tb.writeJavaFiles(src_m3,
183                          "module m3x { requires m2x; }",
184                          "package test; public class Test { { api2.Api2.get().call(); api2.Api2.get().toString(); } }");
185        Path classes = base.resolve("classes");
186        tb.createDirectories(classes);
187
188        String log = new JavacTask(tb)
189                .options("-XDrawDiagnostics",
190                         "--module-source-path", src.toString())
191                .outdir(classes)
192                .files(findJavaFiles(src))
193                .run(Task.Expect.FAIL)
194                .writeAll()
195                .getOutput(Task.OutputKind.DIRECT);
196
197        if (!log.contains("Test.java:1:52: compiler.err.not.def.access.class.intf.cant.access.reason: call(), api1.Api1, api1, (compiler.misc.not.def.access.does.not.read: m3x, api1, m1x)") ||
198            !log.contains("Test.java:1:76: compiler.err.not.def.access.class.intf.cant.access: toString(), java.lang.Object"))
199            throw new Exception("expected output not found");
200    }
201
202    @Test
203    public void testAssignClassToAutomaticModule(Path base) throws Exception {
204        //check that if a ClassSymbol belongs to an automatic module, it is properly assigned and not
205        //duplicated when being accessed through a classfile.
206        Path automaticSrc = base.resolve("automaticSrc");
207        tb.writeJavaFiles(automaticSrc, "package api1; public class Api1 {}");
208        Path automaticClasses = base.resolve("automaticClasses");
209        tb.createDirectories(automaticClasses);
210
211        String automaticLog = new JavacTask(tb)
212                                .outdir(automaticClasses)
213                                .files(findJavaFiles(automaticSrc))
214                                .run()
215                                .writeAll()
216                                .getOutput(Task.OutputKind.DIRECT);
217
218        if (!automaticLog.isEmpty())
219            throw new Exception("expected output not found: " + automaticLog);
220
221        Path modulePath = base.resolve("module-path");
222
223        Files.createDirectories(modulePath);
224
225        Path automaticJar = modulePath.resolve("a-1.0.jar");
226
227        new JarTask(tb, automaticJar)
228          .baseDir(automaticClasses)
229          .files("api1/Api1.class")
230          .run();
231
232        Path src = base.resolve("src");
233        Path src_m2 = src.resolve("m2x");
234        tb.writeJavaFiles(src_m2,
235                          "module m2x { requires a; exports api2; }",
236                          "package api2; public class Api2 { public static api1.Api1 get() { return null; } }");
237        Path src_m3 = src.resolve("m3x");
238        tb.writeJavaFiles(src_m3,
239                          "module m3x { requires a; requires m2x; }",
240                          "package test; public class Test { { api2.Api2.get(); api1.Api1 a1; } }");
241        Path classes = base.resolve("classes");
242        tb.createDirectories(classes);
243
244        new JavacTask(tb)
245                .options("--module-path", modulePath.toString(),
246                         "--module-source-path", src.toString())
247                .outdir(classes)
248                .files(findJavaFiles(src_m2))
249                .run()
250                .writeAll();
251
252        new JavacTask(tb)
253                .options("--module-path", modulePath.toString(),
254                         "--module-source-path", src.toString())
255                .outdir(classes)
256                .files(findJavaFiles(src_m3))
257                .run()
258                .writeAll();
259    }
260
261    @Test
262    public void testEmptyImplicitModuleInfo(Path base) throws Exception {
263        Path src = base.resolve("src");
264        Path src_m1 = src.resolve("m1x");
265        Files.createDirectories(src_m1);
266        try (Writer w = Files.newBufferedWriter(src_m1.resolve("module-info.java"))) {}
267        tb.writeJavaFiles(src_m1,
268                          "package test; public class Test {}");
269        Path classes = base.resolve("classes");
270        tb.createDirectories(classes);
271
272        List<String> log = new JavacTask(tb)
273                .options("--source-path", src_m1.toString(),
274                         "-XDrawDiagnostics")
275                .outdir(classes)
276                .files(findJavaFiles(src_m1.resolve("test")))
277                .run(Task.Expect.FAIL)
278                .writeAll()
279                .getOutputLines(OutputKind.DIRECT);
280
281        List<String> expected = Arrays.asList(
282                "- compiler.err.cant.access: module-info, (compiler.misc.bad.source.file.header: module-info.java, (compiler.misc.file.does.not.contain.module))",
283                "1 error");
284
285        if (!expected.equals(log)) {
286            throw new AssertionError("Unexpected output: " + log);
287        }
288
289        tb.writeJavaFiles(src_m1,
290                          "module m1x {}");
291
292        new JavacTask(tb)
293                .options("--source-path", src_m1.toString())
294                .outdir(classes)
295                .files(findJavaFiles(src_m1.resolve("test")))
296                .run()
297                .writeAll();
298
299    }
300
301    @Test
302    public void testClassPackageClash(Path base) throws Exception {
303        Path src = base.resolve("src");
304        Path src_m1 = src.resolve("m1x");
305        tb.writeJavaFiles(src_m1,
306                          "module m1x { exports test.m1x; }",
307                          "package test.m1x;\n" +
308                          "public class Test {}\n");
309        Path src_m2 = src.resolve("m2x");
310        tb.writeJavaFiles(src_m2,
311                          "module m2x { requires m1x; }",
312                          "package test;\n" +
313                          "public class m1x {}\n");
314        Path classes = base.resolve("classes");
315        tb.createDirectories(classes);
316
317        List<String> log = new JavacTask(tb)
318                .options("--module-source-path", src.toString(),
319                         "-XDrawDiagnostics")
320                .outdir(classes)
321                .files(findJavaFiles(src))
322                .run(Task.Expect.FAIL)
323                .writeAll()
324                .getOutputLines(Task.OutputKind.DIRECT);
325
326        List<String> expected = Arrays.asList(
327            "m1x.java:2:8: compiler.err.clash.with.pkg.of.same.name: kindname.class, test.m1x",
328            "1 error"
329        );
330
331        if (!expected.equals(log)) {
332            throw new IllegalStateException(log.toString());
333        }
334    }
335
336    @Test
337    public void testImplicitJavaBase(Path base) throws Exception {
338        Path src = base.resolve("src");
339        Path src_java_base = src.resolve("java.base");
340        Files.createDirectories(src_java_base);
341        tb.writeJavaFiles(src_java_base, "module java.base { exports java.lang; }");
342        tb.writeJavaFiles(src_java_base,
343                          "package java.lang; public class Object {}");
344        Path classes = base.resolve("classes");
345        tb.createDirectories(classes);
346
347        //module-info from source:
348        new JavacTask(tb)
349            .options("-sourcepath", src_java_base.toString())
350            .outdir(classes)
351            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
352            .run()
353            .writeAll();
354
355        //module-info from class:
356        if (!Files.exists(classes.resolve("module-info.class"))) {
357            throw new AssertionError("module-info.class not created!");
358        }
359
360        new JavacTask(tb)
361            .outdir(classes)
362            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
363            .run()
364            .writeAll();
365
366        //broken module-info.class:
367        Files.newOutputStream(classes.resolve("module-info.class")).close();
368
369        List<String> log = new JavacTask(tb)
370            .options("-XDrawDiagnostics")
371            .outdir(classes)
372            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
373            .run(Expect.FAIL)
374            .writeAll()
375            .getOutputLines(OutputKind.DIRECT);
376
377        List<String> expected = Arrays.asList(
378                "- compiler.err.cant.access: <error>.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.illegal.start.of.class.file))",
379                "1 error");
380
381        if (!expected.equals(log)) {
382            throw new AssertionError("Unexpected output: " + log);
383        }
384
385        //broken module-info.java:
386        Files.delete(classes.resolve("module-info.class"));
387
388        try (Writer out = Files.newBufferedWriter(src_java_base.resolve("module-info.java"))) {
389            out.write("class Broken {}");
390        }
391
392        log = new JavacTask(tb)
393            .options("-sourcepath", src_java_base.toString(),
394                                "-XDrawDiagnostics")
395            .outdir(classes)
396            .files(findJavaFiles(src_java_base.resolve("java").resolve("lang").resolve("Object.java")))
397            .run(Expect.FAIL)
398            .writeAll()
399            .getOutputLines(OutputKind.DIRECT);
400
401        expected = Arrays.asList("X");
402
403        if (expected.equals(log)) {
404            throw new AssertionError("Unexpected output: " + log);
405        }
406    }
407
408    @Test
409    public void testModuleInfoNameMismatchSource(Path base) throws Exception {
410        Path src = base.resolve("src");
411        Path m1 = src.resolve("m1x");
412        Files.createDirectories(m1);
413        tb.writeJavaFiles(m1, "module other { }",
414                              "package test; public class Test {}");
415        Path classes = base.resolve("classes");
416        tb.createDirectories(classes);
417
418        List<String> log = new JavacTask(tb)
419            .options("--module-source-path", src.toString(),
420                     "-XDrawDiagnostics")
421            .outdir(classes)
422            .files(findJavaFiles(m1.resolve("test").resolve("Test.java")))
423            .run(Expect.FAIL)
424            .writeAll()
425            .getOutputLines(OutputKind.DIRECT);
426
427        List<String> expected = Arrays.asList(
428                "module-info.java:1:1: compiler.err.module.name.mismatch: other, m1x",
429                "- compiler.err.cant.access: m1x.module-info, (compiler.misc.cant.resolve.modules)",
430                "2 errors");
431
432        if (!expected.equals(log)) {
433            throw new AssertionError("Unexpected output: " + log);
434        }
435    }
436
437    @Test
438    public void testModuleInfoNameMismatchClass(Path base) throws Exception {
439        Path src = base.resolve("src");
440        Files.createDirectories(src);
441        tb.writeJavaFiles(src, "module other { }",
442                               "package test; public class Test {}");
443        Path classes = base.resolve("classes");
444        Path m1Classes = classes.resolve("m1x");
445        tb.createDirectories(m1Classes);
446
447        new JavacTask(tb)
448            .outdir(m1Classes)
449            .files(findJavaFiles(src))
450            .run()
451            .writeAll()
452            .getOutputLines(OutputKind.DIRECT);
453
454        Path src2 = base.resolve("src2");
455        Files.createDirectories(src2);
456        tb.writeJavaFiles(src2, "module use { requires m1x; }");
457
458        Path classes2 = base.resolve("classes2");
459        tb.createDirectories(classes2);
460
461        List<String> log = new JavacTask(tb)
462            .options("--module-path", classes.toString(),
463                     "-XDrawDiagnostics")
464            .outdir(classes2)
465            .files(findJavaFiles(src2))
466            .run(Expect.FAIL)
467            .writeAll()
468            .getOutputLines(OutputKind.DIRECT);
469
470        List<String> expected = Arrays.asList(
471                "- compiler.err.cant.access: m1x.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.name.mismatch: other, m1x))",
472                "1 error");
473
474        if (!expected.equals(log)) {
475            throw new AssertionError("Unexpected output: " + log);
476        }
477    }
478
479    @Test
480    public void testGetDirectivesComplete(Path base) throws Exception {
481        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
482        JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, null);
483        Symtab syms = Symtab.instance(task.getContext());
484
485        syms.java_base.getDirectives();
486    }
487
488    @Test
489    public void testPackageInModuleInfo(Path base) throws Exception {
490        Path src = base.resolve("src");
491        Files.createDirectories(src);
492        tb.writeJavaFiles(src, "package p; module foo { }");
493        Path classes = base.resolve("classes");
494        tb.createDirectories(classes);
495
496        List<String> log = new JavacTask(tb)
497            .options("-XDrawDiagnostics", "-XDshould-stop.ifError=FLOW")
498            .outdir(classes)
499            .files(findJavaFiles(src))
500            .run(Expect.FAIL)
501            .writeAll()
502            .getOutputLines(OutputKind.DIRECT);
503
504        List<String> expected = Arrays.asList(
505                "module-info.java:1:1: compiler.err.no.pkg.in.module-info.java",
506                "1 error");
507
508        if (!expected.equals(log)) {
509            throw new AssertionError("Unexpected output: " + log);
510        }
511    }
512
513    @Test
514    public void testInvisibleClassVisiblePackageClash(Path base) throws Exception {
515        Path src = base.resolve("src");
516        Path src_m1 = src.resolve("m1x");
517        tb.writeJavaFiles(src_m1,
518                          "module m1x { }",
519                          "package m1x;\n" +
520                          "import m1x.a.*; public class Test { A a; }\n",
521                          "package m1x.a;\n" +
522                          "public class A { }\n");
523        Path src_m2 = src.resolve("m2x");
524        tb.writeJavaFiles(src_m2,
525                          "module m2x { }",
526                          "package m1x;\n" +
527                          "public class a { public static class A { } }\n");
528        Path classes = base.resolve("classes");
529        tb.createDirectories(classes);
530
531        new JavacTask(tb)
532            .options("--module-source-path", src.toString(),
533                     "-XDrawDiagnostics")
534            .outdir(classes)
535            .files(findJavaFiles(src))
536            .run()
537            .writeAll();
538    }
539
540    @Test
541    public void testStripUnknownRequired(Path base) throws Exception {
542        Path src = base.resolve("src");
543        Path src_m1 = src.resolve("m1x");
544        tb.writeJavaFiles(src_m1,
545                          "module m1x { }");
546        Path src_m2 = src.resolve("m2x");
547        tb.writeJavaFiles(src_m2,
548                          "module m2x { }");
549        Path src_m3 = src.resolve("m3x");
550        tb.writeJavaFiles(src_m3,
551                          "module m3x { }");
552        Path src_m4 = src.resolve("m4x");
553        tb.writeJavaFiles(src_m4,
554                          "module m4x { }");
555        Path src_test = src.resolve("test");
556        tb.writeJavaFiles(src_test,
557                          "module test { requires m1x; requires m2x; requires java.base; requires m3x; requires m4x; }");
558        Path src_compile = src.resolve("compile");
559        tb.writeJavaFiles(src_compile,
560                          "module compile { exports p to test; }",
561                          "package p; public class Test { }");
562        Path classes = base.resolve("classes");
563        tb.createDirectories(classes);
564
565        List<String> log = new JavacTask(tb)
566                .options("-processor", ListRequires.class.getName(),
567                         "--module-source-path", src.toString(),
568                         "--limit-modules", "compile",
569                         "-XDaccessInternalAPI=true")
570                .outdir(classes)
571                .files(findJavaFiles(src_compile))
572                .run(Expect.FAIL)
573                .writeAll()
574                .getOutputLines(Task.OutputKind.STDOUT);
575
576        List<String> expected = Arrays.asList(
577                "from directives:",
578                "java.base",
579                "from requires:",
580                "java.base"
581        );
582        if (!Objects.equals(log, expected))
583            throw new AssertionError("Unexpected output: " + log);
584    }
585
586    @SupportedAnnotationTypes("*")
587    @SupportedOptions("expectedEnclosedElements")
588    public static final class ListRequires extends AbstractProcessor {
589
590        private int round;
591
592        @Override
593        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
594            if (round++ == 0) {
595                ModuleElement compileE = processingEnv.getElementUtils().getModuleElement("compile");
596                ModuleElement testE = ElementFilter.exportsIn(compileE.getDirectives()).get(0).getTargetModules().get(0);
597
598                System.out.println("from directives:");
599                for (RequiresDirective rd : ElementFilter.requiresIn(testE.getDirectives())) {
600                    System.out.println(rd.getDependency().getSimpleName());
601                }
602
603                System.out.println("from requires:");
604                for (RequiresDirective rd : ((ModuleSymbol) testE).requires) {
605                    System.out.println(rd.getDependency().getSimpleName());
606                }
607            }
608
609            return false;
610        }
611
612        @Override
613        public SourceVersion getSupportedSourceVersion() {
614            return SourceVersion.latest();
615        }
616
617    }
618
619    @Test
620    public void testOnDemandCompletionModuleInfoJava(Path base) throws Exception {
621        Path src = base.resolve("src");
622        Path src_m1 = src.resolve("m1x");
623        tb.writeJavaFiles(src_m1,
624                          "@Deprecated module m1x { }");
625        Path src_m2 = src.resolve("m2x");
626        tb.writeJavaFiles(src_m2,
627                          "module m2x { requires m1x; }");
628        Path src_m3 = src.resolve("m3x");
629        tb.writeJavaFiles(src_m3,
630                          "module m3x { requires m2x; requires m1x; }");
631        Path classes = base.resolve("classes");
632        tb.createDirectories(classes);
633
634        List<String> log;
635        List<String> expected;
636
637        log = new JavacTask(tb)
638                .options("--module-source-path", src.toString())
639                .outdir(classes)
640                .files(findJavaFiles(src_m1))
641                .run()
642                .writeAll()
643                .getOutputLines(Task.OutputKind.DIRECT);
644
645        expected = Arrays.asList("");
646
647        if (!expected.equals(log)) {
648            throw new IllegalStateException(log.toString());
649        }
650
651        log = new JavacTask(tb)
652                .options("--module-source-path", src.toString(),
653                         "-XDrawDiagnostics",
654                         "-Xlint:deprecation")
655                .outdir(classes)
656                .files(findJavaFiles(src_m3))
657                .run()
658                .writeAll()
659                .getOutputLines(Task.OutputKind.DIRECT);
660
661        expected = Arrays.asList(
662                "module-info.java:1:23: compiler.warn.has.been.deprecated.module: m1x",
663                "module-info.java:1:37: compiler.warn.has.been.deprecated.module: m1x",
664                "2 warnings"
665        );
666
667        if (!expected.equals(log)) {
668            throw new IllegalStateException(log.toString());
669        }
670    }
671
672    @Test
673    public void testUnnamedPackage(Path base) throws Exception {
674        List<String> out;
675        List<String> expected;
676
677        //-source 8:
678        Path src8 = base.resolve("src8");
679        Files.createDirectories(src8);
680        tb.writeJavaFiles(src8,
681                          "package test; public class Test {}");
682        Path classes = base.resolve("classes");
683        tb.createDirectories(classes);
684
685        out = new JavacTask(tb)
686                .options("--source-path", src8.toString(),
687                         "-processor", UnnamedPackageProcessor.class.getName(),
688                         "-source", "8")
689                .outdir(classes)
690                .files(findJavaFiles(src8))
691                .run()
692                .writeAll()
693                .getOutputLines(OutputKind.STDOUT);
694
695        expected = Arrays.asList("noModule");
696
697        if (!expected.equals(out)) {
698            throw new AssertionError("Unexpected output: " + out);
699        }
700
701        //-source 9, unnamed:
702        Path srcUnnamed = base.resolve("srcUnnamed");
703        Files.createDirectories(srcUnnamed);
704        tb.writeJavaFiles(srcUnnamed,
705                          "public class Test {}");
706        Path classesUnnamed = base.resolve("classesUnnamed");
707        tb.createDirectories(classesUnnamed);
708
709        out = new JavacTask(tb)
710                .options("--source-path", srcUnnamed.toString(),
711                         "-processor", UnnamedPackageProcessor.class.getName())
712                .outdir(classesUnnamed)
713                .files(findJavaFiles(srcUnnamed))
714                .run()
715                .writeAll()
716                .getOutputLines(OutputKind.STDOUT);
717
718        expected = Arrays.asList("unnamedModule");
719
720        if (!expected.equals(out)) {
721            throw new AssertionError("Unexpected output: " + out);
722        }
723
724        //-source 9, named:
725        Path srcNamed = base.resolve("srcNamed");
726        Files.createDirectories(srcNamed);
727        tb.writeJavaFiles(srcNamed,
728                          "module m {}",
729                          "public class Test {}");
730        Path classesNamed = base.resolve("classesNamed");
731        tb.createDirectories(classesNamed);
732
733        out = new JavacTask(tb)
734                .options("--source-path", srcNamed.toString(),
735                         "-classpath", "",
736                         "-processorpath", System.getProperty("test.class.path"),
737                         "-processor", UnnamedPackageProcessor.class.getName())
738                .outdir(classesNamed)
739                .files(findJavaFiles(srcNamed))
740                .run()
741                .writeAll()
742                .getOutputLines(OutputKind.STDOUT);
743
744        expected = Arrays.asList("m");
745
746        if (!expected.equals(out)) {
747            throw new AssertionError("Unexpected output: " + out);
748        }
749
750        //-source 9, conflict:
751        Path srcNamed2 = base.resolve("srcNamed2");
752        Path srcNamed2m1 = srcNamed2.resolve("m1x");
753        Files.createDirectories(srcNamed2m1);
754        tb.writeJavaFiles(srcNamed2m1,
755                          "module m1x {}",
756                          "public class Test {}");
757        Path srcNamed2m2 = srcNamed2.resolve("m2x");
758        Files.createDirectories(srcNamed2m2);
759        tb.writeJavaFiles(srcNamed2m2,
760                          "module m2x {}",
761                          "public class Test {}");
762        Path classesNamed2 = base.resolve("classesNamed2");
763        tb.createDirectories(classesNamed2);
764
765        out = new JavacTask(tb)
766                .options("--module-source-path", srcNamed2.toString(),
767                         "-classpath", "",
768                         "-processorpath", System.getProperty("test.class.path"),
769                         "-processor", UnnamedPackageProcessor.class.getName(),
770                         "-XDshould-stop.ifError=FLOW")
771                .outdir(classesNamed2)
772                .files(findJavaFiles(srcNamed2))
773                .run(Expect.FAIL)
774                .writeAll()
775                .getOutputLines(OutputKind.STDOUT);
776
777        expected = Arrays.asList("null",
778                                 "m1x: true",
779                                 "m2x: true");
780
781        if (!expected.equals(out)) {
782            throw new AssertionError("Unexpected output: " + out);
783        }
784    }
785
786    @SupportedAnnotationTypes("*")
787    public static final class UnnamedPackageProcessor extends AbstractProcessor {
788
789        int round = 0;
790
791        @Override
792        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
793            if (round++ != 0)
794                return false;
795
796            Elements elements = processingEnv.getElementUtils();
797            PackageElement pe = elements.getPackageElement("");
798
799            if (pe == null) {
800                System.out.println("null");
801            } else {
802                ModuleElement mod = (ModuleElement) pe.getEnclosingElement();
803                if (mod == null) {
804                    System.out.println("noModule");
805                } else if (mod.isUnnamed()) {
806                    System.out.println("unnamedModule");
807                } else {
808                    System.out.println(mod);
809                }
810            }
811
812            ModuleElement m1x = elements.getModuleElement("m1x");
813            ModuleElement m2x = elements.getModuleElement("m2x");
814
815            if (m1x != null && m2x != null) {
816                System.out.println("m1x: " + (elements.getPackageElement(m1x, "") != null));
817                System.out.println("m2x: " + (elements.getPackageElement(m2x, "") != null));
818            }
819
820            return false;
821        }
822
823    }
824}
825