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