ModuleInfoTest.java revision 3822:d8766c39123a
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 8158123 8161906 8162713
27 * @summary tests for module declarations
28 * @library /tools/lib
29 * @modules
30 *      jdk.compiler/com.sun.tools.javac.api
31 *      jdk.compiler/com.sun.tools.javac.main
32 *      jdk.jdeps/com.sun.tools.javap
33 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
34 * @run main ModuleInfoTest
35 */
36
37import java.nio.file.Files;
38import java.nio.file.Path;
39import java.util.Arrays;
40
41import toolbox.JavacTask;
42import toolbox.Task;
43
44public class ModuleInfoTest extends ModuleTestBase {
45
46    public static void main(String... args) throws Exception {
47        ModuleInfoTest t = new ModuleInfoTest();
48        t.runTests();
49    }
50
51    /**
52     * Check error message if module declaration not in module-info.java.
53     */
54    @Test
55    public void testModuleDeclNotInModuleJava(Path base) throws Exception {
56        Path src = base.resolve("src");
57        tb.writeFile(src.resolve("M.java"), "module M { }");
58        String log = new JavacTask(tb)
59                .options("-XDrawDiagnostics")
60                .files(findJavaFiles(src))
61                .run(Task.Expect.FAIL)
62                .writeAll()
63                .getOutput(Task.OutputKind.DIRECT);
64
65        if (!log.contains("M.java:1:1: compiler.err.module.decl.sb.in.module-info.java"))
66            throw new Exception("expected output not found");
67    }
68
69    /**
70     * Verify that a package private class can be put in module-info.java.
71     */
72    @Test
73    public void testNotModuleDeclInModuleJava_1(Path base) throws Exception {
74        Path src = base.resolve("src");
75        tb.writeFile(src.resolve("module-info.java"), "class C { }");
76        new JavacTask(tb)
77                .options("-XDrawDiagnostics")
78                .files(findJavaFiles(src))
79                .run()
80                .writeAll();
81    }
82
83    /**
84     * Verify that a public class cannot be put in module-info.java.
85     */
86    @Test
87    public void testNotModuleDeclInModuleJava_2(Path base) throws Exception {
88        Path src = base.resolve("src");
89        tb.writeFile(src.resolve("module-info.java"), "public class C { }");
90        String log = new JavacTask(tb)
91                .options("-XDrawDiagnostics")
92                .files(findJavaFiles(src))
93                .run(Task.Expect.FAIL)
94                .writeAll()
95                .getOutput(Task.OutputKind.DIRECT);
96
97        if (!log.contains("module-info.java:1:8: compiler.err.class.public.should.be.in.file: C"))
98            throw new Exception("expected output not found");
99    }
100
101    /**
102     * Verify that only one module decl can be put in module-info.java.
103     */
104    @Test
105    public void testSingleModuleDecl(Path base) throws Exception {
106        Path src = base.resolve("src");
107        tb.writeJavaFiles(src, "module M1 { } /*...*/ module M2 { }");
108        String log = new JavacTask(tb)
109                .options("-XDrawDiagnostics")
110                .files(findJavaFiles(src))
111                .run(Task.Expect.FAIL)
112                .writeAll()
113                .getOutput(Task.OutputKind.DIRECT);
114
115        if (!log.contains("module-info.java:1:14: compiler.err.expected: token.end-of-input"))
116            throw new Exception("expected output not found");
117    }
118
119    /**
120     * Verify that missing requires are reported.
121     */
122    @Test
123    public void testRequiresNotFound(Path base) throws Exception {
124        Path src = base.resolve("src");
125        tb.writeJavaFiles(src, "module M1 { requires M2; }");
126        String log = new JavacTask(tb)
127                .options("-XDrawDiagnostics")
128                .files(findJavaFiles(src))
129                .run(Task.Expect.FAIL)
130                .writeAll()
131                .getOutput(Task.OutputKind.DIRECT);
132
133        if (!log.contains("module-info.java:1:22: compiler.err.module.not.found: M2"))
134            throw new Exception("expected output not found");
135    }
136
137    /**
138     * Verify that missing exports targets are reported.
139     */
140    @Test
141    public void testExportsNotFound(Path base) throws Exception {
142        Path src = base.resolve("src");
143        tb.writeJavaFiles(src,
144                          "module M { exports p to N; }",
145                          "package p; public class C {}");
146        String log = new JavacTask(tb)
147                .options("-XDrawDiagnostics",
148                         "-Xlint:module")
149                .files(findJavaFiles(src))
150                .run()
151                .writeAll()
152                .getOutput(Task.OutputKind.DIRECT);
153
154        if (!log.contains("module-info.java:1:25: compiler.warn.module.not.found: N"))
155            throw new Exception("expected output not found, actual output: " + log);
156    }
157
158    /**
159     * Verify that duplicated qualified missing exports targets are reported.
160     */
161    @Test
162    public void testExportsNotFoundDuplicated(Path base) throws Exception {
163        Path src = base.resolve("src");
164        tb.writeJavaFiles(src,
165                          "module M { exports p to N, N; }",
166                          "package p; public class C {}");
167        String log = new JavacTask(tb)
168                .options("-XDrawDiagnostics",
169                         "-Xlint:module")
170                .files(findJavaFiles(src))
171                .run(Task.Expect.FAIL)
172                .writeAll()
173                .getOutput(Task.OutputKind.DIRECT);
174
175        if (!log.contains("module-info.java:1:28: compiler.err.conflicting.exports.to.module: N"))
176            throw new Exception("expected output not found, actual output: " + log);
177    }
178
179    /**
180     * Verify that missing exports target warning can be suppressed.
181     */
182    @Test
183    public void testExportsNotFoundSuppress(Path base) throws Exception {
184        Path src = base.resolve("src");
185        tb.writeJavaFiles(src,
186                          "@SuppressWarnings(\"module\") module M { exports p to N; }",
187                          "package p; public class C {}");
188        String log = new JavacTask(tb)
189                .options("-XDrawDiagnostics",
190                         "-Xlint:module")
191                .files(findJavaFiles(src))
192                .run()
193                .writeAll()
194                .getOutput(Task.OutputKind.DIRECT);
195
196        if (!log.isEmpty())
197            throw new Exception("expected output not found, actual output: " + log);
198    }
199
200    /**
201     * Verify that missing opens targets are reported.
202     */
203    @Test
204    public void testOpensNotFound(Path base) throws Exception {
205        Path src = base.resolve("src");
206        tb.writeJavaFiles(src,
207                          "module M { opens p to N; }",
208                          "package p; public class C {}");
209        String log = new JavacTask(tb)
210                .options("-XDrawDiagnostics",
211                         "-Xlint:module")
212                .files(findJavaFiles(src))
213                .run()
214                .writeAll()
215                .getOutput(Task.OutputKind.DIRECT);
216
217        if (!log.contains("module-info.java:1:23: compiler.warn.module.not.found: N"))
218            throw new Exception("expected output not found, actual output: " + log);
219    }
220
221    /**
222     * Verify that duplicated qualified missing opens targets are reported.
223     */
224    @Test
225    public void testOpensNotFoundDuplicated(Path base) throws Exception {
226        Path src = base.resolve("src");
227        tb.writeJavaFiles(src,
228                          "module M { opens p to N, N; }",
229                          "package p; public class C {}");
230        String log = new JavacTask(tb)
231                .options("-XDrawDiagnostics",
232                         "-Xlint:module")
233                .files(findJavaFiles(src))
234                .run(Task.Expect.FAIL)
235                .writeAll()
236                .getOutput(Task.OutputKind.DIRECT);
237
238        if (!log.contains("module-info.java:1:26: compiler.err.conflicting.opens.to.module: N"))
239            throw new Exception("expected output not found, actual output: " + log);
240    }
241
242    /**
243     * Verify that missing opens target warning can be suppressed.
244     */
245    @Test
246    public void testOpensNotFoundSuppress(Path base) throws Exception {
247        Path src = base.resolve("src");
248        tb.writeJavaFiles(src,
249                          "@SuppressWarnings(\"module\") module M { opens p to N; }",
250                          "package p; public class C {}");
251        String log = new JavacTask(tb)
252                .options("-XDrawDiagnostics",
253                         "-Xlint:module")
254                .files(findJavaFiles(src))
255                .run()
256                .writeAll()
257                .getOutput(Task.OutputKind.DIRECT);
258
259        if (!log.isEmpty())
260            throw new Exception("expected output not found, actual output: " + log);
261    }
262
263    /**
264     * Verify that a simple loop is detected.
265     */
266    @Test
267    public void testRequiresSelf(Path base) throws Exception {
268        Path src = base.resolve("src");
269        tb.writeJavaFiles(src, "module M { requires M; }");
270        String log = new JavacTask(tb)
271                .options("-XDrawDiagnostics")
272                .files(findJavaFiles(src))
273                .run(Task.Expect.FAIL)
274                .writeAll()
275                .getOutput(Task.OutputKind.DIRECT);
276
277        if (!log.contains("module-info.java:1:21: compiler.err.cyclic.requires: M"))
278            throw new Exception("expected output not found");
279    }
280
281    /**
282     * Verify that a multi-module loop is detected.
283     */
284    @Test
285    public void testRequiresLoop(Path base) throws Exception {
286        Path src = base.resolve("src");
287        Path src_m1 = src.resolve("m1x");
288        tb.writeFile(src_m1.resolve("module-info.java"), "module m1x { requires m2x; }");
289        Path src_m2 = src.resolve("m2x");
290        tb.writeFile(src_m2.resolve("module-info.java"), "module m2x { requires m3x; }");
291        Path src_m3 = src.resolve("m3x");
292        tb.writeFile(src_m3.resolve("module-info.java"), "module m3x { requires m1x; }");
293
294        Path classes = base.resolve("classes");
295        Files.createDirectories(classes);
296
297        String log = new JavacTask(tb)
298                .options("-XDrawDiagnostics", "--module-source-path", src.toString())
299                .outdir(classes)
300                .files(findJavaFiles(src))
301                .run(Task.Expect.FAIL)
302                .writeAll()
303                .getOutput(Task.OutputKind.DIRECT);
304
305        if (!log.contains("module-info.java:1:23: compiler.err.cyclic.requires: m3x"))
306            throw new Exception("expected output not found");
307    }
308
309    /**
310     * Verify that a multi-module loop is detected.
311     */
312    @Test
313    public void testRequiresTransitiveLoop(Path base) throws Exception {
314        Path src = base.resolve("src");
315        Path src_m1 = src.resolve("m1x");
316        tb.writeFile(src_m1.resolve("module-info.java"), "module m1x { requires m2x; }");
317        Path src_m2 = src.resolve("m2x");
318        tb.writeFile(src_m2.resolve("module-info.java"), "module m2x { requires transitive m3x; }");
319        Path src_m3 = src.resolve("m3x");
320        tb.writeFile(src_m3.resolve("module-info.java"), "module m3x { requires m1x; }");
321
322        Path classes = base.resolve("classes");
323        Files.createDirectories(classes);
324
325        String log = new JavacTask(tb)
326                .options("-XDrawDiagnostics", "--module-source-path", src.toString())
327                .outdir(classes)
328                .files(findJavaFiles(src))
329                .run(Task.Expect.FAIL)
330                .writeAll()
331                .getOutput(Task.OutputKind.DIRECT);
332
333        if (!log.contains("module-info.java:1:34: compiler.err.cyclic.requires: m3x"))
334            throw new Exception("expected output not found");
335    }
336
337    /**
338     * Verify that duplicate requires are detected.
339     */
340    @Test
341    public void testDuplicateRequires(Path base) throws Exception {
342        Path src = base.resolve("src");
343        Path src_m1 = src.resolve("m1x");
344        tb.writeFile(src_m1.resolve("module-info.java"), "module m1x { }");
345        Path src_m2 = src.resolve("m2x");
346        tb.writeFile(src_m2.resolve("module-info.java"), "module m2x { requires m1x; requires m1x; }");
347
348        Path classes = base.resolve("classes");
349        Files.createDirectories(classes);
350
351        String log = new JavacTask(tb)
352                .options("-XDrawDiagnostics", "--module-source-path", src.toString())
353                .outdir(classes)
354                .files(findJavaFiles(src))
355                .run(Task.Expect.FAIL)
356                .writeAll()
357                .getOutput(Task.OutputKind.DIRECT);
358
359        if (!log.contains("module-info.java:1:37: compiler.err.duplicate.requires: m1x"))
360            throw new Exception("expected output not found");
361    }
362
363    /**
364     * Verify that duplicate requires are detected.
365     */
366    @Test
367    public void testDuplicateRequiresTransitiveStatic(Path base) throws Exception {
368        Path src = base.resolve("src");
369        Path src_m1 = src.resolve("m1x");
370        tb.writeFile(src_m1.resolve("module-info.java"), "module m1x { }");
371        Path src_m2 = src.resolve("m2x");
372        tb.writeFile(src_m2.resolve("module-info.java"), "module m2x { requires transitive m1x; requires static m1x; }");
373
374        Path classes = base.resolve("classes");
375        Files.createDirectories(classes);
376
377        String log = new JavacTask(tb)
378                .options("-XDrawDiagnostics", "--module-source-path", src.toString())
379                .outdir(classes)
380                .files(findJavaFiles(src))
381                .run(Task.Expect.FAIL)
382                .writeAll()
383                .getOutput(Task.OutputKind.DIRECT);
384
385        if (!log.contains("module-info.java:1:55: compiler.err.duplicate.requires: m1x"))
386            throw new Exception("expected output not found");
387    }
388
389    /**
390     * Verify that duplicate exported packages are detected correctly.
391     */
392    @Test
393    public void testConflictingExports_packages(Path base) throws Exception {
394        verifyConflictingExports_packages(base,
395                                          "exports p; exports q;",
396                                          null);
397        verifyConflictingExports_packages(base,
398                                          "exports p; exports p;",
399                                          "module-info.java:1:33: compiler.err.conflicting.exports: p");
400        verifyConflictingExports_packages(base,
401                                          "exports p; opens p;",
402                                          null);
403        verifyConflictingExports_packages(base,
404                                          "exports p; exports p to m2x;",
405                                          "module-info.java:1:33: compiler.err.conflicting.exports: p");
406        verifyConflictingExports_packages(base,
407                                          "exports p; opens p to m2x;",
408                                          null);
409        verifyConflictingExports_packages(base,
410                                          "opens p; exports p;",
411                                          null);
412        verifyConflictingExports_packages(base,
413                                          "opens p; opens p;",
414                                          "module-info.java:1:29: compiler.err.conflicting.opens: p");
415        verifyConflictingExports_packages(base,
416                                          "opens p; exports p to m2x;",
417                                          null);
418        verifyConflictingExports_packages(base,
419                                          "opens p; opens p to m2x;",
420                                          "module-info.java:1:29: compiler.err.conflicting.opens: p");
421        verifyConflictingExports_packages(base,
422                                          "exports p to m2x; exports p;",
423                                          "module-info.java:1:40: compiler.err.conflicting.exports: p");
424        verifyConflictingExports_packages(base,
425                                          "exports p to m2x; opens p;",
426                                          null);
427        verifyConflictingExports_packages(base,
428                                          "exports p to m2x; exports p to m2x;",
429                                          "module-info.java:1:45: compiler.err.conflicting.exports.to.module: m2x");
430        verifyConflictingExports_packages(base,
431                                          "exports p to m2x; opens p to m2x;",
432                                          null);
433        verifyConflictingExports_packages(base,
434                                          "opens p to m2x; exports p;",
435                                          null);
436        verifyConflictingExports_packages(base,
437                                          "opens p to m2x; opens p;",
438                                          "module-info.java:1:36: compiler.err.conflicting.opens: p");
439        verifyConflictingExports_packages(base,
440                                          "opens p to m2x; exports p to m2x;",
441                                          null);
442        verifyConflictingExports_packages(base,
443                                          "opens p to m2x; opens p to m2x;",
444                                          "module-info.java:1:36: compiler.err.conflicting.opens: p");
445        verifyConflictingExports_packages(base,
446                                          "exports p to m2x; exports p to m3x;",
447                                          "module-info.java:1:40: compiler.err.conflicting.exports: p");
448        verifyConflictingExports_packages(base,
449                                          "exports p to m2x; opens p to m3x;",
450                                          null);
451        verifyConflictingExports_packages(base,
452                                          "opens p to m2x; exports p to m3x;",
453                                          null);
454        verifyConflictingExports_packages(base,
455                                          "opens p to m2x; opens p to m3x;",
456                                          "module-info.java:1:36: compiler.err.conflicting.opens: p");
457    }
458
459    private void verifyConflictingExports_packages(Path base, String code, String expected) throws Exception {
460        Files.createDirectories(base);
461        tb.cleanDirectory(base);
462
463        Path src = base.resolve("src");
464        tb.writeJavaFiles(src.resolve("m1x"),
465                          "module m1x { " + code + " }",
466                          "package p; public class P {}",
467                          "package q; public class Q {}");
468        tb.writeJavaFiles(src.resolve("m2x"),
469                          "module m2x { requires m1x; }");
470        tb.writeJavaFiles(src.resolve("m3x"),
471                          "module m3x { requires m1x; }");
472
473        Path classes = base.resolve("classes");
474        Files.createDirectories(classes);
475
476        String log = new JavacTask(tb)
477                .options("-XDrawDiagnostics",
478                         "--module-source-path", src.toString())
479                .outdir(classes)
480                .files(findJavaFiles(src))
481                .run(expected != null ? Task.Expect.FAIL : Task.Expect.SUCCESS)
482                .writeAll()
483                .getOutput(Task.OutputKind.DIRECT);
484
485        if (expected != null && !log.contains(expected))
486            throw new Exception("expected output not found, actual output: " + log);
487    }
488
489    /**
490     * Verify that duplicate exported packages are detected.
491     */
492    @Test
493    public void testConflictingExports_modules(Path base) throws Exception {
494        Path src = base.resolve("src");
495        Path src_m1 = src.resolve("m1x");
496        tb.writeFile(src_m1.resolve("module-info.java"), "module m1x { }");
497        Path src_m2 = src.resolve("m2x");
498        tb.writeFile(src_m2.resolve("module-info.java"), "module m2x { exports p to m1x, m1x; }");
499
500        Path classes = base.resolve("classes");
501        Files.createDirectories(classes);
502
503        String log = new JavacTask(tb)
504                .options("-XDrawDiagnostics", "--module-source-path", src.toString())
505                .outdir(classes)
506                .files(findJavaFiles(src))
507                .run(Task.Expect.FAIL)
508                .writeAll()
509                .getOutput(Task.OutputKind.DIRECT);
510
511        if (!log.contains("module-info.java:1:32: compiler.err.conflicting.exports.to.module: m1x"))
512            throw new Exception("expected output not found");
513    }
514
515    /**
516     * Verify that annotations are not permitted at
517     * any of the module names or the package names.
518     */
519    @Test
520    public void testAnnotations(Path base) throws Exception {
521        Path src = base.resolve("src");
522        Path src_m1 = src.resolve("m1x.sub");
523        Path classes = base.resolve("classes");
524        Files.createDirectories(classes);
525
526        String code = "module @m1.@sub { " +
527                "requires @p1.@p2; " +
528                "exports @p1.@p2; " +
529                "exports @p1.@p2 to @m2.@sub; " +
530                "exports @p1.@p2 to @m2.@sub, @m3.@sub; " +
531                "uses @p1.@Interface; " +
532                "provides @p1.@Interface with @p2.@Concrete; " +
533                "}";
534        String[] splittedCode = code.split("@");
535        int length = splittedCode.length;
536        String anno = "@Anno ";
537
538        for (int i = 1; i < length; i++) {
539            String preAnno = String.join("", Arrays.copyOfRange(splittedCode, 0, i));
540            String postAnno = String.join("", Arrays.copyOfRange(splittedCode, i, length));
541            String moduleInfo = preAnno + anno + postAnno;
542            tb.writeFile(src_m1.resolve("module-info.java"), moduleInfo);
543
544            String log = new JavacTask(tb)
545                    .options("-XDrawDiagnostics", "--module-source-path", src.toString())
546                    .outdir(classes)
547                    .files(findJavaFiles(src))
548                    .run(Task.Expect.FAIL)
549                    .writeAll()
550                    .getOutput(Task.OutputKind.DIRECT);
551
552            String expect_prefix = "(?s)^module\\-info\\.java:\\d+:\\d+: ";
553            String expect_message = "compiler\\.err\\.expected: token\\.identifier";
554            String expect_suffix = ".*";
555            String expect = expect_prefix + expect_message + expect_suffix;
556            if (!log.matches(expect))
557                throw new Exception("expected output not found for: " + moduleInfo + "; actual: " + log);
558        }
559    }
560}
561