1 /* 2 * Copyright (c) 2016, 2018, 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 split packages 27 * @library ../lib 28 * @build CompilerUtils JdepsUtil 29 * @modules java.logging 30 * jdk.jdeps/com.sun.tools.jdeps 31 * jdk.unsupported 32 * @run testng InverseDeps 33 */ 34 35 import java.io.File; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.util.Arrays; 40 import java.util.LinkedHashSet; 41 import java.util.List; 42 import java.util.Set; 43 import java.util.stream.Collectors; 44 import java.util.stream.Stream; 45 46 import com.sun.tools.jdeps.Archive; 47 import com.sun.tools.jdeps.InverseDepsAnalyzer; 48 import org.testng.annotations.BeforeTest; 49 import org.testng.annotations.DataProvider; 50 import org.testng.annotations.Test; 51 52 import static org.testng.Assert.assertTrue; 53 import static org.testng.Assert.assertFalse; 54 import static org.testng.Assert.assertEquals; 55 56 57 public class InverseDeps { 58 private static final String TEST_SRC = System.getProperty("test.src"); 59 private static final String TEST_CLASSES = System.getProperty("test.classes"); 60 61 private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); 62 private static final Path MODS_DIR = Paths.get("mods"); 63 private static final Path LIBS_DIR = Paths.get("libs"); 64 65 private static final Set<String> modules = new LinkedHashSet( 66 List.of("unsafe", "mIV", "mV", "mVI", "mVII") 67 ); 68 69 /** 70 * Compiles classes used by the test 71 */ 72 @BeforeTest 73 public void compileAll() throws Exception { 74 CompilerUtils.cleanDir(MODS_DIR); 75 76 for (String mn : modules) { 77 // compile a module 78 assertTrue(CompilerUtils.compileModule(SRC_DIR, MODS_DIR, mn)); 79 80 // create JAR files with no module-info.class 81 Path root = MODS_DIR.resolve(mn); 82 83 try (Stream<Path> stream = Files.walk(root, Integer.MAX_VALUE)) { 84 Stream<Path> entries = stream.filter(f -> { 85 String fn = f.getFileName().toString(); 86 return fn.endsWith(".class") && !fn.equals("module-info.class"); 87 }); 88 JdepsUtil.createJar(LIBS_DIR.resolve(mn + ".jar"), root, entries); 89 } 90 } 91 } 92 @DataProvider(name = "jdkModules") 93 public Object[][] jdkModules() { 94 return new Object[][]{ 95 // --require and a subset of dependences 96 { "jdk.compiler", new String[][] { 97 new String[] {"jdk.compiler", "jdk.jshell"}, 98 new String[] {"jdk.compiler", "jdk.rmic"}, 99 new String[] {"jdk.compiler", "jdk.javadoc", "jdk.rmic"}, 100 } 101 }, 102 { "java.compiler", new String[][] { 103 new String[] {"java.compiler", "jdk.jshell"}, 104 new String[] {"java.compiler", "jdk.compiler", "jdk.jshell"}, 105 new String[] {"java.compiler", "jdk.compiler", "jdk.rmic"}, 106 new String[] {"java.compiler", "jdk.compiler", "jdk.javadoc", "jdk.rmic"}, 107 new String[] {"java.compiler", "java.se"}, 108 } 109 }, 110 }; 111 } 112 113 @Test(dataProvider = "jdkModules") 114 public void testJDKModule(String moduleName, String[][] expected) throws Exception { 115 // this invokes the jdeps launcher so that all system modules are observable 116 JdepsRunner jdeps = JdepsRunner.run( 117 "--inverse", "--require", moduleName 118 ); 119 List<String> output = Arrays.stream(jdeps.output()) 120 .map(s -> s.trim()) 121 .collect(Collectors.toList()); 122 123 // verify the dependences 124 assertTrue(Arrays.stream(expected) 125 .map(path -> Arrays.stream(path) 126 .collect(Collectors.joining(" <- "))) 127 .anyMatch(output::contains)); 128 } 129 130 131 @DataProvider(name = "testrequires") 132 public Object[][] expected1() { 133 return new Object[][] { 134 // --require and result 135 { "java.sql", new String[][] { 136 new String[] { "java.sql", "mV" }, 137 } 138 }, 139 { "java.compiler", new String[][] { 140 new String[] { "java.compiler", "mV" }, 141 new String[] { "java.compiler", "mIV", "mV" }, 142 } 143 }, 144 { "java.logging", new String[][]{ 145 new String[] {"java.logging", "mV"}, 146 new String[] {"java.logging", "mIV", "mV"}, 147 new String[] {"java.logging", "java.sql", "mV"}, 148 } 149 }, 150 { "jdk.unsupported", new String[][] { 151 new String[] {"jdk.unsupported", "unsafe", "mVI", "mVII"}, 152 new String[] {"jdk.unsupported", "unsafe", "mVII"} 153 } 154 }, 155 }; 156 } 157 158 @Test(dataProvider = "testrequires") 159 public void testrequires(String name, String[][] expected) throws Exception { 160 String cmd1 = String.format("jdeps --inverse --module-path %s --require %s --add-modules %s%n", 161 MODS_DIR, name, modules.stream().collect(Collectors.joining(","))); 162 163 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd1)) { 164 jdeps.appModulePath(MODS_DIR.toString()) 165 .addmods(modules) 166 .requires(Set.of(name)); 167 168 runJdeps(jdeps, expected); 169 } 170 171 String cmd2 = String.format("jdeps --inverse --module-path %s --require %s" + 172 " --add-modules ALL-MODULE-PATH%n", LIBS_DIR, name); 173 174 // automatic module 175 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd2)) { 176 jdeps.appModulePath(MODS_DIR.toString()) 177 .addmods(Set.of("ALL-MODULE-PATH")) 178 .requires(Set.of(name)); 179 180 runJdeps(jdeps, expected); 181 } 182 } 183 184 @DataProvider(name = "testpackage") 185 public Object[][] expected2() { 186 return new Object[][] { 187 // -package and result 188 { "p4", new String[][] { 189 new String[] { "mIV", "mV"}, 190 } 191 }, 192 { "javax.tools", new String[][] { 193 new String[] {"java.compiler", "mV"}, 194 new String[] {"java.compiler", "mIV", "mV"}, 195 } 196 }, 197 { "sun.misc", new String[][] { 198 new String[] {"jdk.unsupported", "unsafe", "mVI", "mVII"}, 199 new String[] {"jdk.unsupported", "unsafe", "mVII"} 200 } 201 } 202 }; 203 } 204 205 @Test(dataProvider = "testpackage") 206 public void testpackage(String name, String[][] expected) throws Exception { 207 String cmd = String.format("jdeps --inverse --module-path %s -package %s --add-modules %s%n", 208 MODS_DIR, name, modules.stream().collect(Collectors.joining(","))); 209 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) { 210 jdeps.appModulePath(MODS_DIR.toString()) 211 .addmods(modules) 212 .matchPackages(Set.of(name)); 213 214 runJdeps(jdeps, expected); 215 } 216 } 217 218 @DataProvider(name = "testregex") 219 public Object[][] expected3() { 220 return new Object[][] { 221 // -regex and result 222 { "org.safe.Lib", new String[][] { 223 new String[] { "unsafe", "mVII"}, 224 new String[] { "unsafe", "mVI", "mVII"}, 225 } 226 }, 227 { "java.util.logging.*|org.safe.Lib", new String[][] { 228 new String[] { "unsafe", "mVII"}, 229 new String[] { "unsafe", "mVI", "mVII"}, 230 new String[] { "java.logging", "mV"}, 231 } 232 } 233 }; 234 } 235 236 @Test(dataProvider = "testregex") 237 public void testregex(String name, String[][] expected) throws Exception { 238 String cmd = String.format("jdeps --inverse --module-path %s -regex %s --add-modules %s%n", 239 MODS_DIR, name, modules.stream().collect(Collectors.joining(","))); 240 241 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd)) { 242 jdeps.appModulePath(MODS_DIR.toString()) 243 .addmods(modules) 244 .regex(name); 245 246 runJdeps(jdeps, expected); 247 } 248 } 249 250 @DataProvider(name = "classpath") 251 public Object[][] expected4() { 252 return new Object[][] { 253 // -regex and result 254 { "sun.misc.Unsafe", new String[][] { 255 new String[] {"jdk.unsupported", "unsafe.jar", "mVI.jar", "mVII.jar"}, 256 new String[] {"jdk.unsupported", "unsafe.jar", "mVII.jar"} 257 } 258 }, 259 { "org.safe.Lib", new String[][] { 260 new String[] { "unsafe.jar", "mVII.jar"}, 261 new String[] { "unsafe.jar", "mVI.jar", "mVII.jar"}, 262 } 263 }, 264 { "java.util.logging.*|org.safe.Lib", new String[][] { 265 new String[] { "unsafe.jar", "mVII.jar"}, 266 new String[] { "unsafe.jar", "mVI.jar", "mVII.jar"}, 267 new String[] { "java.logging", "mV.jar"}, 268 } 269 } 270 }; 271 } 272 273 @Test(dataProvider = "classpath") 274 public void testClassPath(String name, String[][] expected) throws Exception { 275 // -classpath 276 String cpath = modules.stream() 277 .filter(mn -> !mn.equals("mVII")) 278 .map(mn -> LIBS_DIR.resolve(mn + ".jar").toString()) 279 .collect(Collectors.joining(File.pathSeparator)); 280 281 Path jarfile = LIBS_DIR.resolve("mVII.jar"); 282 283 String cmd1 = String.format("jdeps --inverse -classpath %s -regex %s %s%n", 284 cpath, name, jarfile); 285 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd1)) { 286 jdeps.verbose("-verbose:class") 287 .addClassPath(cpath) 288 .regex(name).addRoot(jarfile); 289 runJdeps(jdeps, expected); 290 } 291 292 // all JAR files on the command-line arguments 293 Set<Path> paths = modules.stream() 294 .map(mn -> LIBS_DIR.resolve(mn + ".jar")) 295 .collect(Collectors.toSet()); 296 String cmd2 = String.format("jdeps --inverse -regex %s %s%n", name, paths); 297 try (JdepsUtil.Command jdeps = JdepsUtil.newCommand(cmd2)) { 298 jdeps.verbose("-verbose:class").regex(name); 299 paths.forEach(jdeps::addRoot); 300 runJdeps(jdeps, expected); 301 } 302 } 303 304 private void runJdeps(JdepsUtil.Command jdeps, String[][] expected) throws Exception { 305 InverseDepsAnalyzer analyzer = jdeps.getInverseDepsAnalyzer(); 306 307 assertTrue(analyzer.run()); 308 309 // get the inverse transitive dependences 310 List<String[]> paths = analyzer.inverseDependences().stream() 311 .map(deque -> deque.stream() 312 .map(Archive::getName) 313 .collect(Collectors.toList()).toArray(new String[0])) 314 .collect(Collectors.toList()); 315 316 jdeps.dumpOutput(System.err); 317 paths.forEach(path -> System.err.println(Arrays.stream(path) 318 .collect(Collectors.joining(" <- ")))); 319 320 // verify the dependences 321 assertEquals(paths.size(), expected.length); 322 323 for (int i=0; i < paths.size(); i++) { 324 String[] path = paths.get(i); 325 boolean noneMatched = Arrays.stream(expected) 326 .filter(array -> array.length == path.length) 327 .noneMatch(array -> Arrays.equals(array, path)); 328 if (noneMatched) 329 System.err.format("Expected: %s found: %s%n", 330 Arrays.stream(expected) 331 .map(Arrays::toString) 332 .collect(Collectors.joining(", ")), 333 Arrays.toString(path)); 334 335 assertFalse(noneMatched); 336 } 337 } 338 339 }