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