1 /*
   2  * Copyright (c) 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  * @summary Tests jdeps -m and -mp options on named modules and unnamed modules
  27  * @library ../lib
  28  * @build CompilerUtils JdepsUtil
  29  * @modules jdk.jdeps/com.sun.tools.jdeps
  30  * @run testng TransitiveDeps
  31  */
  32 
  33 import java.io.File;
  34 import java.io.IOException;
  35 
  36 import java.nio.file.Files;
  37 import java.nio.file.Path;
  38 import java.nio.file.Paths;
  39 import java.util.*;
  40 import java.util.function.Function;
  41 import java.util.stream.Collectors;
  42 import java.util.stream.Stream;
  43 
  44 
  45 import com.sun.tools.jdeps.DepsAnalyzer;
  46 import com.sun.tools.jdeps.Graph;
  47 import org.testng.annotations.DataProvider;
  48 import org.testng.annotations.BeforeTest;
  49 import org.testng.annotations.Test;
  50 
  51 import static org.testng.Assert.assertTrue;
  52 
  53 public class TransitiveDeps {
  54     private static final String TEST_SRC = System.getProperty("test.src");
  55 
  56     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  57     private static final Path MODS_DIR = Paths.get("mods");
  58     private static final Path LIBS_DIR = Paths.get("libs");
  59 
  60     // the names of the modules in this test
  61     private static String[] modules = new String[] {"unsafe", "m6", "m7"};
  62     /**
  63      * Compiles all modules used by the test
  64      */
  65     @BeforeTest
  66     public void compileAll() throws Exception {
  67         CompilerUtils.cleanDir(MODS_DIR);
  68         CompilerUtils.cleanDir(LIBS_DIR);
  69 
  70         for (String mn : modules) {
  71             // compile a module
  72             assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn));
  73 
  74             // create JAR files with no module-info.class
  75             Path root = MODS_DIR.resolve(mn);
  76             try (Stream<Path> stream = Files.walk(root, Integer.MAX_VALUE)) {
  77                 Stream<Path> entries = stream.filter(f -> {
  78                     String fn = f.getFileName().toString();
  79                     return fn.endsWith(".class") && !fn.equals("module-info.class");
  80                 });
  81                 JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root, entries);
  82             }
  83         }
  84     }
  85 
  86     @DataProvider(name = "modules")
  87     public Object[][] expected1() {
  88         return new Object[][]{
  89             { "m7",
  90                List.of(new ModuleMetaData("m7")
  91                             .requires("m6")
  92                             .requires("unsafe")
  93                             .reference("p7.Main", "java.lang.Object", "java.base")
  94                             .reference("p7.Main", "java.lang.String", "java.base")
  95                             .reference("p7.Main", "org.safe.Lib", "unsafe")
  96                             .reference("p7.Main", "p6.safe.Lib", "m6"),
  97                         new ModuleMetaData("m6")
  98                             .requires("unsafe")
  99                             .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
 100                             .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe")
 101                             .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
 102                             .reference("p6.safe.Lib", "java.lang.Class", "java.base")
 103                             .reference("p6.safe.Lib", "java.lang.Object", "java.base")
 104                             .reference("p6.safe.Lib", "java.lang.String", "java.base")
 105                             .reference("p6.safe.Lib", "java.lang.System", "java.base")
 106                             .reference("p6.safe.Lib", "org.safe.Lib", "unsafe"),
 107                         new ModuleMetaData("unsafe")
 108                             .requires("jdk.unsupported")
 109                             .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
 110                             .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
 111                             .reference("org.safe.Lib", "java.lang.Class", "java.base")
 112                             .reference("org.safe.Lib", "java.lang.Object", "java.base")
 113                             .reference("org.safe.Lib", "java.lang.String", "java.base")
 114                             .reference("org.safe.Lib", "java.lang.System", "java.base")
 115                             .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
 116                             .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
 117                         )
 118             },
 119         };
 120     }
 121 
 122     @Test(dataProvider = "modules")
 123     public void testModulePath(String name, List<ModuleMetaData> data) throws IOException {
 124         Set<String> roots = Set.of("m6", "unsafe");
 125 
 126         String cmd1 = String.format("jdeps --module-path %s --add-modules %s -m %s%n", MODS_DIR,
 127             roots.stream().collect(Collectors.joining(",")), name);
 128         try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd1)) {
 129             jdeps.verbose("-verbose:class")
 130                 .appModulePath(MODS_DIR.toString())
 131                 .addmods(roots)
 132                 .addmods(Set.of(name));
 133 
 134             runJdeps(jdeps, data);
 135         }
 136         // run automatic modules
 137         roots = Set.of("ALL-MODULE-PATH", "jdk.unsupported");
 138 
 139         String cmd2 = String.format("jdeps --module-path %s --add-modules %s -m %s%n", LIBS_DIR,
 140             roots.stream().collect(Collectors.joining(",")), name);
 141 
 142         try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd2)) {
 143             jdeps.verbose("-verbose:class")
 144                 .appModulePath(LIBS_DIR.toString())
 145                 .addmods(roots)
 146                 .addmods(Set.of(name));
 147 
 148             runJdeps(jdeps, data);
 149         }
 150     }
 151 
 152     @DataProvider(name = "jars")
 153     public Object[][] expected2() {
 154         return new Object[][]{
 155             { "m7", List.of(new ModuleMetaData("m7.jar")
 156                                 .requires("m6.jar")
 157                                 .requires("unsafe.jar")
 158                                 .reference("p7.Main", "java.lang.Object", "java.base")
 159                                 .reference("p7.Main", "java.lang.String", "java.base")
 160                                 .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
 161                                 .reference("p7.Main", "p6.safe.Lib", "m6.jar"))
 162             },
 163         };
 164     }
 165 
 166     @Test(dataProvider = "jars")
 167     public void testClassPath(String name, List<ModuleMetaData> data) throws IOException {
 168         String cpath = Arrays.stream(modules)
 169             .filter(mn -> !mn.equals(name))
 170             .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
 171             .collect(Collectors.joining(File.pathSeparator));
 172 
 173         Path jarfile = LIBS_DIR.resolve(name + ".jar");
 174 
 175         String cmd = String.format("jdeps -classpath %s %s%n", cpath, jarfile);
 176         try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
 177             jdeps.verbose("-verbose:class")
 178                 .addClassPath(cpath)
 179                 .addRoot(jarfile);
 180 
 181             runJdeps(jdeps, data);
 182         }
 183     }
 184 
 185     @DataProvider(name = "compileTimeView")
 186     public Object[][] expected3() {
 187         return new Object[][] {
 188             {"m7",
 189              List.of(new ModuleMetaData("m7.jar")
 190                         .requires("m6.jar")
 191                         .requires("unsafe.jar")
 192                         .reference("p7.Main", "java.lang.Object", "java.base")
 193                         .reference("p7.Main", "java.lang.String", "java.base")
 194                         .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
 195                         .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
 196                     new ModuleMetaData("m6.jar")
 197                         .requires("unsafe.jar")
 198                         .reference("p6.indirect.UnsafeRef", "java.lang.Object", "java.base")
 199                         .reference("p6.indirect.UnsafeRef", "org.unsafe.UseUnsafe ", "unsafe.jar")
 200                         .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
 201                         .reference("p6.safe.Lib", "java.lang.Class", "java.base")
 202                         .reference("p6.safe.Lib", "java.lang.Object", "java.base")
 203                         .reference("p6.safe.Lib", "java.lang.String", "java.base")
 204                         .reference("p6.safe.Lib", "java.lang.System", "java.base")
 205                         .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
 206                     new ModuleMetaData("unsafe.jar")
 207                         .requires("jdk.unsupported")
 208                         .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
 209                         .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
 210                         .reference("org.safe.Lib", "java.lang.Class", "java.base")
 211                         .reference("org.safe.Lib", "java.lang.Object", "java.base")
 212                         .reference("org.safe.Lib", "java.lang.String", "java.base")
 213                         .reference("org.safe.Lib", "java.lang.System", "java.base")
 214                         .reference("org.unsafe.UseUnsafe", "java.lang.Object", "java.base")
 215                         .jdkInternal("org.unsafe.UseUnsafe", "sun.misc.Unsafe", "java.base")
 216                 )
 217             },
 218         };
 219     }
 220 
 221     @Test(dataProvider = "compileTimeView")
 222     public void compileTimeView(String name, List<ModuleMetaData> data) throws IOException {
 223         String cpath = Arrays.stream(modules)
 224                 .filter(mn -> !mn.equals(name))
 225                 .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
 226                 .collect(Collectors.joining(File.pathSeparator));
 227 
 228         Path jarfile = LIBS_DIR.resolve(name + ".jar");
 229 
 230         String cmd = String.format("jdeps -ct -classpath %s %s%n", cpath, jarfile);
 231         try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
 232             jdeps.verbose("-verbose:class")
 233                 .addClassPath(cpath)
 234                 .addRoot(jarfile);
 235 
 236             runJdeps(jdeps, data, true, 0 /* -recursive */);
 237         }
 238     }
 239 
 240     @DataProvider(name = "recursiveDeps")
 241     public Object[][] expected4() {
 242         return new Object[][] {
 243             {"m7",
 244                 List.of(new ModuleMetaData("m7.jar")
 245                         .requires("m6.jar")
 246                         .requires("unsafe.jar")
 247                         .reference("p7.Main", "java.lang.Object", "java.base")
 248                         .reference("p7.Main", "java.lang.String", "java.base")
 249                         .reference("p7.Main", "org.safe.Lib", "unsafe.jar")
 250                         .reference("p7.Main", "p6.safe.Lib", "m6.jar"),
 251                     new ModuleMetaData("m6.jar")
 252                         .requires("unsafe.jar")
 253                         .reference("p6.safe.Lib", "java.io.PrintStream", "java.base")
 254                         .reference("p6.safe.Lib", "java.lang.Class", "java.base")
 255                         .reference("p6.safe.Lib", "java.lang.Object", "java.base")
 256                         .reference("p6.safe.Lib", "java.lang.String", "java.base")
 257                         .reference("p6.safe.Lib", "java.lang.System", "java.base")
 258                         .reference("p6.safe.Lib", "org.safe.Lib", "unsafe.jar"),
 259                     new ModuleMetaData("unsafe.jar")
 260                         .requires("jdk.unsupported")
 261                         .reference("org.indirect.UnsafeRef", "java.lang.Object", "java.base")
 262                         .reference("org.safe.Lib", "java.io.PrintStream", "java.base")
 263                         .reference("org.safe.Lib", "java.lang.Class", "java.base")
 264                         .reference("org.safe.Lib", "java.lang.Object", "java.base")
 265                         .reference("org.safe.Lib", "java.lang.String", "java.base")
 266                         .reference("org.safe.Lib", "java.lang.System", "java.base")
 267                 )
 268             },
 269         };
 270     }
 271     @Test(dataProvider = "recursiveDeps")
 272     public void recursiveDeps(String name, List<ModuleMetaData> data) throws IOException {
 273         String cpath = Arrays.stream(modules)
 274             .filter(mn -> !mn.equals(name))
 275             .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString())
 276             .collect(Collectors.joining(File.pathSeparator));
 277 
 278         Path jarfile = LIBS_DIR.resolve(name + ".jar");
 279 
 280         String cmd = String.format("jdeps -R -classpath %s %s%n", cpath, jarfile);
 281         try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) {
 282             jdeps.verbose("-verbose:class").filter("-filter:archive")
 283                 .addClassPath(cpath)
 284                 .addRoot(jarfile);
 285 
 286             runJdeps(jdeps, data, true, 0 /* -recursive */);
 287         }
 288     }
 289 
 290     private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data)
 291         throws IOException
 292     {
 293         runJdeps(jdeps, data, false, 1 /* depth */);
 294     }
 295 
 296     private void runJdeps(JdepsUtil.Command jdeps, List<ModuleMetaData> data,
 297                           boolean compileTimeView, int depth)
 298         throws IOException
 299     {
 300         // run the analyzer
 301         DepsAnalyzer analyzer = jdeps.getDepsAnalyzer();
 302         assertTrue(analyzer.run(compileTimeView, depth));
 303         jdeps.dumpOutput(System.err);
 304 
 305         // analyze result
 306         Graph<DepsAnalyzer.Node> g1 = analyzer.moduleGraph();
 307         Map<String, ModuleMetaData> dataMap = data.stream()
 308             .collect(Collectors.toMap(ModuleMetaData::name, Function.identity()));
 309 
 310         // the returned graph contains all nodes such as java.base and jdk.unsupported
 311         g1.nodes().stream()
 312             .filter(u -> dataMap.containsKey(u.name))
 313             .forEach(u -> {
 314                 ModuleMetaData md = dataMap.get(u.name);
 315                 md.checkRequires(u.name, g1.adjacentNodes(u));
 316             });
 317 
 318         Graph<DepsAnalyzer.Node> g2 = analyzer.dependenceGraph();
 319 
 320         g2.nodes().stream()
 321             .filter(u -> dataMap.containsKey(u.name))
 322             .forEach(u -> {
 323                 ModuleMetaData md = dataMap.get(u.name);
 324                 md.checkDependences(u.name, g2.adjacentNodes(u));
 325             });
 326     }
 327 }