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