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