EdgeCases.java revision 4055:d457e90d4906
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
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                "1 error");
471
472        if (!expected.equals(log)) {
473            throw new AssertionError("Unexpected output: " + log);
474        }
475    }
476
477    @Test
478    public void testGetDirectivesComplete(Path base) throws Exception {
479        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
480        JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, null, null, null, null, null);
481        Symtab syms = Symtab.instance(task.getContext());
482
483        syms.java_base.getDirectives();
484    }
485
486    @Test
487    public void testPackageInModuleInfo(Path base) throws Exception {
488        Path src = base.resolve("src");
489        Files.createDirectories(src);
490        tb.writeJavaFiles(src, "package p; module foo { }");
491        Path classes = base.resolve("classes");
492        tb.createDirectories(classes);
493
494        List<String> log = new JavacTask(tb)
495            .options("-XDrawDiagnostics", "-XDshould-stop.ifError=FLOW")
496            .outdir(classes)
497            .files(findJavaFiles(src))
498            .run(Expect.FAIL)
499            .writeAll()
500            .getOutputLines(OutputKind.DIRECT);
501
502        List<String> expected = Arrays.asList(
503                "module-info.java:1:1: compiler.err.no.pkg.in.module-info.java",
504                "1 error");
505
506        if (!expected.equals(log)) {
507            throw new AssertionError("Unexpected output: " + log);
508        }
509    }
510
511    @Test
512    public void testInvisibleClassVisiblePackageClash(Path base) throws Exception {
513        Path src = base.resolve("src");
514        Path src_m1 = src.resolve("m1x");
515        tb.writeJavaFiles(src_m1,
516                          "module m1x { }",
517                          "package m1x;\n" +
518                          "import m1x.a.*; public class Test { A a; }\n",
519                          "package m1x.a;\n" +
520                          "public class A { }\n");
521        Path src_m2 = src.resolve("m2x");
522        tb.writeJavaFiles(src_m2,
523                          "module m2x { }",
524                          "package m1x;\n" +
525                          "public class a { public static class A { } }\n");
526        Path classes = base.resolve("classes");
527        tb.createDirectories(classes);
528
529        new JavacTask(tb)
530            .options("--module-source-path", src.toString(),
531                     "-XDrawDiagnostics")
532            .outdir(classes)
533            .files(findJavaFiles(src))
534            .run()
535            .writeAll();
536    }
537
538    @Test
539    public void testStripUnknownRequired(Path base) throws Exception {
540        Path src = base.resolve("src");
541        Path src_m1 = src.resolve("m1x");
542        tb.writeJavaFiles(src_m1,
543                          "module m1x { }");
544        Path src_m2 = src.resolve("m2x");
545        tb.writeJavaFiles(src_m2,
546                          "module m2x { }");
547        Path src_m3 = src.resolve("m3x");
548        tb.writeJavaFiles(src_m3,
549                          "module m3x { }");
550        Path src_m4 = src.resolve("m4x");
551        tb.writeJavaFiles(src_m4,
552                          "module m4x { }");
553        Path src_test = src.resolve("test");
554        tb.writeJavaFiles(src_test,
555                          "module test { requires m1x; requires m2x; requires java.base; requires m3x; requires m4x; }");
556        Path src_compile = src.resolve("compile");
557        tb.writeJavaFiles(src_compile,
558                          "module compile { exports p to test; }",
559                          "package p; public class Test { }");
560        Path classes = base.resolve("classes");
561        tb.createDirectories(classes);
562
563        List<String> log = new JavacTask(tb)
564                .options("-processor", ListRequires.class.getName(),
565                         "--module-source-path", src.toString(),
566                         "--limit-modules", "compile",
567                         "-XDaccessInternalAPI=true")
568                .outdir(classes)
569                .files(findJavaFiles(src_compile))
570                .run(Expect.FAIL)
571                .writeAll()
572                .getOutputLines(Task.OutputKind.STDOUT);
573
574        List<String> expected = Arrays.asList(
575                "from directives:",
576                "java.base",
577                "from requires:",
578                "java.base"
579        );
580        if (!Objects.equals(log, expected))
581            throw new AssertionError("Unexpected output: " + log);
582    }
583
584    @SupportedAnnotationTypes("*")
585    @SupportedOptions("expectedEnclosedElements")
586    public static final class ListRequires extends AbstractProcessor {
587
588        private int round;
589
590        @Override
591        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
592            if (round++ == 0) {
593                ModuleElement compileE = processingEnv.getElementUtils().getModuleElement("compile");
594                ModuleElement testE = ElementFilter.exportsIn(compileE.getDirectives()).get(0).getTargetModules().get(0);
595
596                System.out.println("from directives:");
597                for (RequiresDirective rd : ElementFilter.requiresIn(testE.getDirectives())) {
598                    System.out.println(rd.getDependency().getSimpleName());
599                }
600
601                System.out.println("from requires:");
602                for (RequiresDirective rd : ((ModuleSymbol) testE).requires) {
603                    System.out.println(rd.getDependency().getSimpleName());
604                }
605            }
606
607            return false;
608        }
609
610        @Override
611        public SourceVersion getSupportedSourceVersion() {
612            return SourceVersion.latest();
613        }
614
615    }
616
617    @Test
618    public void testOnDemandCompletionModuleInfoJava(Path base) throws Exception {
619        Path src = base.resolve("src");
620        Path src_m1 = src.resolve("m1x");
621        tb.writeJavaFiles(src_m1,
622                          "@Deprecated module m1x { }");
623        Path src_m2 = src.resolve("m2x");
624        tb.writeJavaFiles(src_m2,
625                          "module m2x { requires m1x; }");
626        Path src_m3 = src.resolve("m3x");
627        tb.writeJavaFiles(src_m3,
628                          "module m3x { requires m2x; requires m1x; }");
629        Path classes = base.resolve("classes");
630        tb.createDirectories(classes);
631
632        List<String> log;
633        List<String> expected;
634
635        log = new JavacTask(tb)
636                .options("--module-source-path", src.toString())
637                .outdir(classes)
638                .files(findJavaFiles(src_m1))
639                .run()
640                .writeAll()
641                .getOutputLines(Task.OutputKind.DIRECT);
642
643        expected = Arrays.asList("");
644
645        if (!expected.equals(log)) {
646            throw new IllegalStateException(log.toString());
647        }
648
649        log = new JavacTask(tb)
650                .options("--module-source-path", src.toString(),
651                         "-XDrawDiagnostics",
652                         "-Xlint:deprecation")
653                .outdir(classes)
654                .files(findJavaFiles(src_m3))
655                .run()
656                .writeAll()
657                .getOutputLines(Task.OutputKind.DIRECT);
658
659        expected = Arrays.asList(
660                "module-info.java:1:23: compiler.warn.has.been.deprecated.module: m1x",
661                "module-info.java:1:37: compiler.warn.has.been.deprecated.module: m1x",
662                "2 warnings"
663        );
664
665        if (!expected.equals(log)) {
666            throw new IllegalStateException(log.toString());
667        }
668    }
669
670    @Test
671    public void testUnnamedPackage(Path base) throws Exception {
672        List<String> out;
673        List<String> expected;
674
675        //-source 8:
676        Path src8 = base.resolve("src8");
677        Files.createDirectories(src8);
678        tb.writeJavaFiles(src8,
679                          "package test; public class Test {}");
680        Path classes = base.resolve("classes");
681        tb.createDirectories(classes);
682
683        out = new JavacTask(tb)
684                .options("--source-path", src8.toString(),
685                         "-processor", UnnamedPackageProcessor.class.getName(),
686                         "-source", "8")
687                .outdir(classes)
688                .files(findJavaFiles(src8))
689                .run()
690                .writeAll()
691                .getOutputLines(OutputKind.STDOUT);
692
693        expected = Arrays.asList("noModule");
694
695        if (!expected.equals(out)) {
696            throw new AssertionError("Unexpected output: " + out);
697        }
698
699        //-source 9, unnamed:
700        Path srcUnnamed = base.resolve("srcUnnamed");
701        Files.createDirectories(srcUnnamed);
702        tb.writeJavaFiles(srcUnnamed,
703                          "public class Test {}");
704        Path classesUnnamed = base.resolve("classesUnnamed");
705        tb.createDirectories(classesUnnamed);
706
707        out = new JavacTask(tb)
708                .options("--source-path", srcUnnamed.toString(),
709                         "-processor", UnnamedPackageProcessor.class.getName())
710                .outdir(classesUnnamed)
711                .files(findJavaFiles(srcUnnamed))
712                .run()
713                .writeAll()
714                .getOutputLines(OutputKind.STDOUT);
715
716        expected = Arrays.asList("unnamedModule");
717
718        if (!expected.equals(out)) {
719            throw new AssertionError("Unexpected output: " + out);
720        }
721
722        //-source 9, named:
723        Path srcNamed = base.resolve("srcNamed");
724        Files.createDirectories(srcNamed);
725        tb.writeJavaFiles(srcNamed,
726                          "module m {}",
727                          "public class Test {}");
728        Path classesNamed = base.resolve("classesNamed");
729        tb.createDirectories(classesNamed);
730
731        out = new JavacTask(tb)
732                .options("--source-path", srcNamed.toString(),
733                         "-classpath", "",
734                         "-processorpath", System.getProperty("test.class.path"),
735                         "-processor", UnnamedPackageProcessor.class.getName())
736                .outdir(classesNamed)
737                .files(findJavaFiles(srcNamed))
738                .run()
739                .writeAll()
740                .getOutputLines(OutputKind.STDOUT);
741
742        expected = Arrays.asList("m");
743
744        if (!expected.equals(out)) {
745            throw new AssertionError("Unexpected output: " + out);
746        }
747
748        //-source 9, conflict:
749        Path srcNamed2 = base.resolve("srcNamed2");
750        Path srcNamed2m1 = srcNamed2.resolve("m1x");
751        Files.createDirectories(srcNamed2m1);
752        tb.writeJavaFiles(srcNamed2m1,
753                          "module m1x {}",
754                          "public class Test {}");
755        Path srcNamed2m2 = srcNamed2.resolve("m2x");
756        Files.createDirectories(srcNamed2m2);
757        tb.writeJavaFiles(srcNamed2m2,
758                          "module m2x {}",
759                          "public class Test {}");
760        Path classesNamed2 = base.resolve("classesNamed2");
761        tb.createDirectories(classesNamed2);
762
763        out = new JavacTask(tb)
764                .options("--module-source-path", srcNamed2.toString(),
765                         "-classpath", "",
766                         "-processorpath", System.getProperty("test.class.path"),
767                         "-processor", UnnamedPackageProcessor.class.getName(),
768                         "-XDshould-stop.ifError=FLOW")
769                .outdir(classesNamed2)
770                .files(findJavaFiles(srcNamed2))
771                .run(Expect.FAIL)
772                .writeAll()
773                .getOutputLines(OutputKind.STDOUT);
774
775        expected = Arrays.asList("null",
776                                 "m1x: true",
777                                 "m2x: true");
778
779        if (!expected.equals(out)) {
780            throw new AssertionError("Unexpected output: " + out);
781        }
782    }
783
784    @SupportedAnnotationTypes("*")
785    public static final class UnnamedPackageProcessor extends AbstractProcessor {
786
787        int round = 0;
788
789        @Override
790        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
791            if (round++ != 0)
792                return false;
793
794            Elements elements = processingEnv.getElementUtils();
795            PackageElement pe = elements.getPackageElement("");
796
797            if (pe == null) {
798                System.out.println("null");
799            } else {
800                ModuleElement mod = (ModuleElement) pe.getEnclosingElement();
801                if (mod == null) {
802                    System.out.println("noModule");
803                } else if (mod.isUnnamed()) {
804                    System.out.println("unnamedModule");
805                } else {
806                    System.out.println(mod);
807                }
808            }
809
810            ModuleElement m1x = elements.getModuleElement("m1x");
811            ModuleElement m2x = elements.getModuleElement("m2x");
812
813            if (m1x != null && m2x != null) {
814                System.out.println("m1x: " + (elements.getPackageElement(m1x, "") != null));
815                System.out.println("m2x: " + (elements.getPackageElement(m2x, "") != null));
816            }
817
818            return false;
819        }
820
821    }
822
823    @Test
824    public void testEmptyInExportedPackage(Path base) throws Exception {
825        Path src = base.resolve("src");
826        Path m = src.resolve("m");
827        tb.writeJavaFiles(m,
828                          "module m { exports api; }");
829        Path apiFile = m.resolve("api").resolve("Api.java");
830        Files.createDirectories(apiFile.getParent());
831        try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
832            w.write("//no package decl");
833        }
834        Path classes = base.resolve("classes");
835        tb.createDirectories(classes);
836
837        List<String> log;
838        List<String> expected =
839                Arrays.asList("module-info.java:1:20: compiler.err.package.empty.or.not.found: api",
840                              "1 error");
841
842        System.err.println("file explicitly specified:");
843
844        log = new JavacTask(tb)
845            .options("-XDrawDiagnostics",
846                     "--module-source-path", src.toString())
847            .outdir(classes)
848            .files(findJavaFiles(src))
849            .run(Task.Expect.FAIL)
850            .writeAll()
851            .getOutputLines(Task.OutputKind.DIRECT);
852
853        if (!expected.equals(log))
854            throw new Exception("expected output not found: " + log);
855
856        System.err.println("file not specified:");
857
858        tb.cleanDirectory(classes);
859
860        log = new JavacTask(tb)
861            .options("-XDrawDiagnostics",
862                     "--module-source-path", src.toString())
863            .outdir(classes)
864            .files(findJavaFiles(m.resolve("module-info.java")))
865            .run(Task.Expect.FAIL)
866            .writeAll()
867            .getOutputLines(Task.OutputKind.DIRECT);
868
869        if (!expected.equals(log))
870            throw new Exception("expected output not found: " + log);
871    }
872
873    @Test
874    public void testJustPackageInExportedPackage(Path base) throws Exception {
875        Path src = base.resolve("src");
876        Path m = src.resolve("m");
877        tb.writeJavaFiles(m,
878                          "module m { exports api; }");
879        Path apiFile = m.resolve("api").resolve("Api.java");
880        Files.createDirectories(apiFile.getParent());
881        try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
882            w.write("package api;");
883        }
884        Path classes = base.resolve("classes");
885        tb.createDirectories(classes);
886
887        System.err.println("file explicitly specified:");
888
889        new JavacTask(tb)
890            .options("-XDrawDiagnostics",
891                     "--module-source-path", src.toString())
892            .outdir(classes)
893            .files(findJavaFiles(src))
894            .run()
895            .writeAll();
896
897        System.err.println("file not specified:");
898
899        tb.cleanDirectory(classes);
900
901        new JavacTask(tb)
902            .options("-XDrawDiagnostics",
903                     "--module-source-path", src.toString())
904            .outdir(classes)
905            .files(findJavaFiles(m.resolve("module-info.java")))
906            .run()
907            .writeAll();
908    }
909
910    @Test
911    public void testWrongPackageInExportedPackage(Path base) throws Exception {
912        Path src = base.resolve("src");
913        Path m = src.resolve("m");
914        tb.writeJavaFiles(m,
915                          "module m { exports api; }");
916        Path apiFile = m.resolve("api").resolve("Api.java");
917        Files.createDirectories(apiFile.getParent());
918        try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
919            w.write("package impl; public class Api { }");
920        }
921        Path classes = base.resolve("classes");
922        tb.createDirectories(classes);
923
924        List<String> log;
925
926        List<String> expected =
927                Arrays.asList("module-info.java:1:20: compiler.err.package.empty.or.not.found: api",
928                              "1 error");
929
930        System.err.println("file explicitly specified:");
931
932        log = new JavacTask(tb)
933                .options("-XDrawDiagnostics",
934                         "--module-source-path", src.toString())
935                .outdir(classes)
936                .files(findJavaFiles(src))
937                .run(Task.Expect.FAIL)
938                .writeAll()
939                .getOutputLines(Task.OutputKind.DIRECT);
940
941        if (!expected.equals(log))
942            throw new Exception("expected output not found: " + log);
943
944        System.err.println("file not specified:");
945
946        tb.cleanDirectory(classes);
947
948        log = new JavacTask(tb)
949                .options("-XDrawDiagnostics",
950                         "--module-source-path", src.toString())
951                .outdir(classes)
952                .files(findJavaFiles(m.resolve("module-info.java")))
953                .run(Task.Expect.FAIL)
954                .writeAll()
955                .getOutputLines(Task.OutputKind.DIRECT);
956
957        if (!expected.equals(log))
958            throw new Exception("expected output not found: " + log);
959    }
960
961}
962