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