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