1 /*
   2  * Copyright (c) 2016, 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 import java.io.File;
  25 import java.io.IOException;
  26 import java.nio.file.Files;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.util.Arrays;
  30 import java.util.Set;
  31 import java.util.spi.ToolProvider;
  32 import java.util.stream.Collectors;
  33 import java.util.stream.Stream;
  34 
  35 import jdk.test.lib.compiler.CompilerUtils;
  36 import jdk.test.lib.util.FileUtils;
  37 
  38 import static jdk.testlibrary.ProcessTools.*;
  39 
  40 import org.testng.annotations.BeforeTest;
  41 import org.testng.annotations.Test;
  42 import static org.testng.Assert.*;
  43 
  44 /**
  45  * @test
  46  * @bug 8142968 8173381 8174740
  47  * @library /lib/testlibrary /test/lib
  48  * @modules jdk.compiler jdk.jlink
  49  * @modules java.base/jdk.internal.module
  50  * @modules java.base/jdk.internal.org.objectweb.asm
  51  * @build jdk.test.lib.compiler.CompilerUtils
  52  *        jdk.test.lib.util.FileUtils
  53  *        jdk.test.lib.Platform
  54  *        ModuleTargetHelper UserModuleTest jdk.testlibrary.ProcessTools
  55  * @run testng UserModuleTest
  56  */
  57 
  58 public class UserModuleTest {
  59     private static final String JAVA_HOME = System.getProperty("java.home");
  60     private static final String TEST_SRC = System.getProperty("test.src");
  61 
  62     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  63     private static final Path MODS_DIR = Paths.get("mods");
  64     private static final Path JMODS_DIR = Paths.get("jmods");
  65 
  66     private static final Path IMAGE = Paths.get("image");
  67     private static final String MAIN_MID = "m1/p1.Main";
  68 
  69     // the names of the modules in this test
  70     private static String[] modules = new String[] {"m1", "m2", "m3", "m4", "m5"};
  71 
  72 
  73     private static boolean hasJmods() {
  74         if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
  75             System.err.println("Test skipped. NO jmods directory");
  76             return false;
  77         }
  78         return true;
  79     }
  80 
  81     /*
  82      * Compiles all modules used by the test
  83      */
  84     @BeforeTest
  85     public void compileAll() throws Throwable {
  86         if (!hasJmods()) return;
  87 
  88         for (String mn : modules) {
  89             Path msrc = SRC_DIR.resolve(mn);
  90             assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
  91                 "--module-source-path", SRC_DIR.toString(),
  92                 "--add-exports", "java.base/jdk.internal.module=" + mn,
  93                 "--add-exports", "java.base/jdk.internal.org.objectweb.asm=" + mn));
  94         }
  95 
  96         if (Files.exists(IMAGE)) {
  97             FileUtils.deleteFileTreeUnchecked(IMAGE);
  98         }
  99 
 100         createImage(IMAGE, "m1", "m3");
 101 
 102         createJmods("m1", "m4");
 103     }
 104 
 105     /*
 106      * Test the image created when linking with a module with
 107      * no Packages attribute
 108      */
 109     @Test
 110     public void testPackagesAttribute() throws Throwable {
 111         if (!hasJmods()) return;
 112 
 113         Path java = IMAGE.resolve("bin").resolve("java");
 114         assertTrue(executeProcess(java.toString(),
 115                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 116                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 117                         "-m", MAIN_MID)
 118                         .outputTo(System.out)
 119                         .errorTo(System.out)
 120                         .getExitValue() == 0);
 121     }
 122 
 123     /*
 124      * Test the image created when linking with an open module
 125     */
 126     @Test
 127     public void testOpenModule() throws Throwable {
 128         if (!hasJmods()) return;
 129 
 130         Path java = IMAGE.resolve("bin").resolve("java");
 131         assertTrue(executeProcess(java.toString(), "-m", "m3/p3.Main")
 132                         .outputTo(System.out)
 133                         .errorTo(System.out)
 134                         .getExitValue() == 0);
 135     }
 136 
 137     /*
 138      * Disable the fast loading of system modules.
 139      * Parsing module-info.class
 140      */
 141     @Test
 142     public void disableSystemModules() throws Throwable {
 143         if (!hasJmods()) return;
 144 
 145         Path java = IMAGE.resolve("bin").resolve("java");
 146         assertTrue(executeProcess(java.toString(),
 147                                   "--add-exports", "java.base/jdk.internal.module=m1,m4",
 148                                   "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 149                                   "-Djdk.system.module.finder.disabledFastPath",
 150                                   "-m", MAIN_MID)
 151                         .outputTo(System.out)
 152                         .errorTo(System.out)
 153                         .getExitValue() == 0);
 154     }
 155 
 156     /*
 157      * Test the optimization that deduplicates Set<String> on targets of exports,
 158      * uses, provides.
 159      */
 160     @Test
 161     public void testDedupSet() throws Throwable {
 162         if (!hasJmods()) return;
 163 
 164         Path dir = Paths.get("dedupSetTest");
 165         createImage(dir, "m1", "m2", "m3", "m4");
 166         Path java = dir.resolve("bin").resolve("java");
 167         assertTrue(executeProcess(java.toString(),
 168                          "--add-exports", "java.base/jdk.internal.module=m1,m4",
 169                          "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 170                          "-m", MAIN_MID)
 171                         .outputTo(System.out)
 172                         .errorTo(System.out)
 173                         .getExitValue() == 0);
 174     }
 175 
 176     @Test
 177     public void testRequiresStatic() throws Throwable {
 178         if (!hasJmods()) return;
 179 
 180         Path dir = Paths.get("requiresStatic");
 181         createImage(dir, "m5");
 182         Path java = dir.resolve("bin").resolve("java");
 183         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 184                         .outputTo(System.out)
 185                         .errorTo(System.out)
 186                         .getExitValue() == 0);
 187 
 188         // run with m3 present
 189         assertTrue(executeProcess(java.toString(),
 190                                   "--module-path", MODS_DIR.toString(),
 191                                   "--add-modules", "m3",
 192                                   "-m", "m5/p5.Main")
 193                         .outputTo(System.out)
 194                         .errorTo(System.out)
 195                         .getExitValue() == 0);
 196     }
 197 
 198     @Test
 199     public void testRequiresStatic2() throws Throwable {
 200         if (!hasJmods()) return;
 201 
 202         Path dir = Paths.get("requiresStatic2");
 203         createImage(dir, "m3", "m5");
 204 
 205         Path java = dir.resolve("bin").resolve("java");
 206         assertTrue(executeProcess(java.toString(), "-m", "m5/p5.Main")
 207                         .outputTo(System.out)
 208                         .errorTo(System.out)
 209                         .getExitValue() == 0);
 210 
 211         // boot layer with m3 and m5
 212         assertTrue(executeProcess(java.toString(),
 213                                   "--add-modules", "m3",
 214                                   "-m", "m5/p5.Main")
 215                         .outputTo(System.out)
 216                         .errorTo(System.out)
 217                         .getExitValue() == 0);
 218     }
 219 
 220     private void createJmods(String... modules) throws IOException {
 221         ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.getJavaBaseTarget();
 222         if (mt == null) {
 223             throw new RuntimeException("ModuleTarget is missing for java.base");
 224         }
 225 
 226         String[] values = mt.targetPlatform().split("-");
 227         String osName = values[0];
 228         String osArch = values[1];
 229 
 230         // create JMOD files
 231         Files.createDirectories(JMODS_DIR);
 232         Stream.of(modules).forEach(mn ->
 233             assertTrue(jmod("create",
 234                 "--class-path", MODS_DIR.resolve(mn).toString(),
 235                 "--target-platform", mt.targetPlatform(),
 236                 "--main-class", mn.replace('m', 'p') + ".Main",
 237                 JMODS_DIR.resolve(mn + ".jmod").toString()) == 0)
 238         );
 239     }
 240 
 241 
 242     /**
 243      * Verify the module descriptor if package p4.dummy is excluded at link time.
 244      */
 245     @Test
 246     public void testModulePackagesAttribute() throws Throwable {
 247         if (!hasJmods()) return;
 248 
 249         // create an image using JMOD files
 250         Path dir = Paths.get("packagesTest");
 251         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 252             File.pathSeparator + JMODS_DIR.toString();
 253 
 254         Set<String> modules = Set.of("m1", "m4");
 255         assertTrue(JLINK_TOOL.run(System.out, System.out,
 256             "--output", dir.toString(),
 257             "--exclude-resources", "m4/p4/dummy/*",
 258             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 259             "--module-path", mp) == 0);
 260 
 261         // verify ModuleDescriptor
 262         Path java = dir.resolve("bin").resolve("java");
 263         assertTrue(executeProcess(java.toString(),
 264                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 265                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 266                         "--add-modules=m1", "-m", "m4")
 267             .outputTo(System.out)
 268             .errorTo(System.out)
 269             .getExitValue() == 0);
 270     }
 271 
 272     /**
 273      * Verify the plugin to retain ModuleTarget attribute
 274      */
 275     @Test
 276     public void testRetainModuleTarget() throws Throwable {
 277         if (!hasJmods()) return;
 278 
 279         // create an image using JMOD files
 280         Path dir = Paths.get("retainModuleTargetTest");
 281         String mp = Paths.get(JAVA_HOME, "jmods").toString() +
 282             File.pathSeparator + JMODS_DIR.toString();
 283 
 284         Set<String> modules = Set.of("m1", "m4");
 285         assertTrue(JLINK_TOOL.run(System.out, System.out,
 286             "--output", dir.toString(),
 287             "--exclude-resources", "m4/p4/dummy/*",
 288             "--add-modules", modules.stream().collect(Collectors.joining(",")),
 289             "--module-path", mp) == 0);
 290 
 291         // verify ModuleDescriptor
 292         Path java = dir.resolve("bin").resolve("java");
 293         assertTrue(executeProcess(java.toString(),
 294                         "--add-exports", "java.base/jdk.internal.module=m1,m4",
 295                         "--add-exports", "java.base/jdk.internal.org.objectweb.asm=m1,m4",
 296                         "--add-modules=m1", "-m", "m4", "retainModuleTarget")
 297             .outputTo(System.out)
 298             .errorTo(System.out)
 299             .getExitValue() == 0);
 300     }
 301 
 302     static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 303         .orElseThrow(() ->
 304             new RuntimeException("jlink tool not found")
 305         );
 306 
 307     static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
 308         .orElseThrow(() ->
 309             new RuntimeException("jmod tool not found")
 310         );
 311 
 312     static final String MODULE_PATH = Paths.get(JAVA_HOME, "jmods").toString()
 313         + File.pathSeparator + MODS_DIR.toString();
 314 
 315     private void createImage(Path outputDir, String... modules) throws Throwable {
 316         assertTrue(JLINK_TOOL.run(System.out, System.out,
 317             "--output", outputDir.toString(),
 318             "--add-modules", Arrays.stream(modules).collect(Collectors.joining(",")),
 319             "--module-path", MODULE_PATH) == 0);
 320     }
 321 
 322     private static int jmod(String... options) {
 323         System.out.println("jmod " + Arrays.asList(options));
 324         return JMOD_TOOL.run(System.out, System.out, options);
 325     }
 326 }