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