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 }