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  * @bug 8167057
  27  * @summary Tests --list-deps, --list-reduced-deps, --print-module-deps options
  28  * @modules java.logging
  29  *          java.xml
  30  *          jdk.compiler
  31  *          jdk.jdeps
  32  *          jdk.unsupported
  33  * @library ../lib
  34  * @build CompilerUtils JdepsRunner
  35  * @run testng ListModuleDeps
  36  */
  37 
  38 import java.io.File;
  39 import java.nio.file.Path;
  40 import java.nio.file.Paths;
  41 import java.util.Arrays;
  42 import java.util.List;
  43 import java.util.stream.Collectors;
  44 
  45 import org.testng.annotations.BeforeTest;
  46 import org.testng.annotations.DataProvider;
  47 import org.testng.annotations.Test;
  48 
  49 import static org.testng.Assert.assertEquals;
  50 import static org.testng.Assert.assertTrue;
  51 
  52 public class ListModuleDeps {
  53     private static final String TEST_SRC = System.getProperty("test.src");
  54 
  55     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  56     private static final Path CLASSES_DIR = Paths.get("classes");
  57     private static final Path LIB_DIR = Paths.get("lib");
  58     private static final Path LIB2_DIR = Paths.get("lib2");
  59 
  60     private static final Path HI_CLASS =
  61         CLASSES_DIR.resolve("hi").resolve("Hi.class");
  62     private static final Path FOO_CLASS =
  63         CLASSES_DIR.resolve("z").resolve("Foo.class");
  64     private static final Path BAR_CLASS =
  65         CLASSES_DIR.resolve("z").resolve("Bar.class");
  66     private static final Path UNSAFE_CLASS =
  67         CLASSES_DIR.resolve("z").resolve("UseUnsafe.class");
  68 
  69     /**
  70      * Compiles classes used by the test
  71      */
  72     @BeforeTest
  73     public void compileAll() throws Exception {
  74         // compile library
  75         assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "lib2"), LIB2_DIR));
  76         assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "lib"), LIB_DIR, "-cp", LIB2_DIR.toString()));
  77 
  78         // simple program depends only on java.base
  79         assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "hi"), CLASSES_DIR));
  80 
  81         // compile classes in unnamed module
  82         assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "z"),
  83             CLASSES_DIR,
  84             "-cp", LIB_DIR.toString(),
  85             "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
  86             "--add-exports=java.base/sun.security.util=ALL-UNNAMED",
  87             "--add-exports=java.xml/jdk.xml.internal=ALL-UNNAMED"
  88         ));
  89     }
  90 
  91     @DataProvider(name = "jdkModules")
  92     public Object[][] jdkModules() {
  93         return new Object[][]{
  94             {"jdk.compiler", new String[]{
  95                                 "java.base/jdk.internal.jmod",
  96                                 "java.base/jdk.internal.misc",
  97                                 "java.base/sun.invoke.util",
  98                                 "java.base/sun.reflect.annotation",
  99                                 "java.compiler",
 100                              }
 101             },
 102         };
 103     }
 104 
 105     @Test(dataProvider = "jdkModules")
 106     public void testJDKModule(String moduleName, String[] expected) {
 107         JdepsRunner jdeps = JdepsRunner.run(
 108             "--list-deps", "-m", moduleName
 109         );
 110         String[] output = Arrays.stream(jdeps.output())
 111                                 .map(s -> s.trim())
 112                                 .toArray(String[]::new);
 113         assertEquals(output, expected);
 114     }
 115 
 116     @Test(dataProvider = "listdeps")
 117     public void testListDeps(Path classes, String[] expected) {
 118         JdepsRunner jdeps = JdepsRunner.run(
 119             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 120             "--list-deps", classes.toString()
 121         );
 122         String[] output = Arrays.stream(jdeps.output())
 123                                 .map(s -> s.trim())
 124                                 .toArray(String[]::new);
 125         assertEquals(output, expected);
 126     }
 127 
 128     @Test(dataProvider = "reduceddeps")
 129     public void testListReducedDeps(Path classes, String[]  expected) {
 130         JdepsRunner jdeps = JdepsRunner.run(
 131             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 132             "--list-reduced-deps", classes.toString()
 133         );
 134         String[] output = Arrays.stream(jdeps.output())
 135                                 .map(s -> s.trim())
 136                                 .toArray(String[]::new);
 137         assertEquals(output, expected);
 138     }
 139 
 140 
 141     @DataProvider(name = "listdeps")
 142     public Object[][] listdeps() {
 143         return new Object[][] {
 144             { CLASSES_DIR,  new String[] {
 145                                 "java.base/jdk.internal.misc",
 146                                 "java.base/sun.security.util",
 147                                 "java.logging",
 148                                 "java.management",
 149                                 "java.sql",
 150                                 "java.xml/jdk.xml.internal",
 151                                 "jdk.unsupported"
 152                             }
 153             },
 154 
 155             { HI_CLASS,     new String[] {
 156                                 "java.base"
 157                             }
 158             },
 159 
 160             { FOO_CLASS,    new String[] {
 161                                 "java.base",
 162                                 "java.logging",
 163                                 "java.management",
 164                                 "java.sql",
 165                                 "java.xml"
 166                             }
 167             },
 168 
 169             { BAR_CLASS,    new String[] {
 170                                 "java.base/sun.security.util",
 171                                 "java.xml/jdk.xml.internal",
 172                             }
 173             },
 174 
 175             { UNSAFE_CLASS, new String[] {
 176                                 "java.base/jdk.internal.misc",
 177                                 "jdk.unsupported"
 178                             }
 179             },
 180         };
 181     }
 182 
 183     @DataProvider(name = "reduceddeps")
 184     public Object[][] reduceddeps() {
 185         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 186 
 187         return new Object[][] {
 188             { CLASSES_DIR,  new String[] {
 189                                 "java.base/jdk.internal.misc",
 190                                 "java.base/sun.security.util",
 191                                 "java.management",
 192                                 "java.sql",
 193                                 "java.xml/jdk.xml.internal",
 194                                 "jdk.unsupported"
 195                             }
 196             },
 197 
 198             { HI_CLASS,     new String[] {
 199                                 "java.base"
 200                             }
 201             },
 202 
 203             { FOO_CLASS,    new String[] {
 204                                 "java.base",
 205                                 "java.management",
 206                                 "java.sql"
 207                             }
 208             },
 209 
 210             { BAR_CLASS,    new String[] {
 211                                 "java.base/sun.security.util",
 212                                 "java.xml/jdk.xml.internal",
 213                             }
 214             },
 215 
 216             { UNSAFE_CLASS, new String[] {
 217                                 "java.base/jdk.internal.misc",
 218                                 "jdk.unsupported"
 219                             }
 220             },
 221         };
 222     }
 223 
 224     @Test(dataProvider = "moduledeps")
 225     public void testPrintModuleDeps(Path classes, String expected) {
 226         JdepsRunner jdeps = JdepsRunner.run(
 227             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 228             "--print-module-deps", classes.toString()
 229         );
 230         String output = Arrays.stream(jdeps.output())
 231             .map(s -> s.trim())
 232             .collect(Collectors.joining(","));
 233         assertEquals(output, expected);
 234     }
 235 
 236 
 237     @DataProvider(name = "moduledeps")
 238     public Object[][] moduledeps() {
 239         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 240 
 241         return new Object[][] {
 242             // java.xml is an implied reads edge from java.sql
 243             { CLASSES_DIR,  "java.base,java.management,java.sql,jdk.unsupported"},
 244             { HI_CLASS,     "java.base"},
 245             { FOO_CLASS,    "java.base,java.management,java.sql"},
 246             { BAR_CLASS,    "java.base,java.xml"},
 247             { UNSAFE_CLASS, "java.base,jdk.unsupported"},
 248         };
 249     }
 250 
 251     @Test(dataProvider = "noRecursiveModuledeps")
 252     public void testNoRecursiveModuleDeps(Path classes, String expected) {
 253         JdepsRunner jdeps = JdepsRunner.run(
 254             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 255             "--print-module-deps", "--no-recursive", classes.toString()
 256         );
 257         String output = Arrays.stream(jdeps.output())
 258             .map(s -> s.trim())
 259             .collect(Collectors.joining(","));
 260         assertEquals(output, expected);
 261     }
 262 
 263     @DataProvider(name = "noRecursiveModuledeps")
 264     public Object[][] noRecursiveModuledeps() {
 265         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 266 
 267         return new Object[][] {
 268             // java.xml is an implied reads edge from java.sql
 269             { CLASSES_DIR,  "java.base,java.sql,jdk.unsupported"},
 270             { HI_CLASS,     "java.base"},
 271             { FOO_CLASS,    "java.base,java.sql"},
 272             { BAR_CLASS,    "java.base,java.xml"},
 273             { UNSAFE_CLASS, "java.base,jdk.unsupported"},
 274         };
 275     }
 276 
 277     @DataProvider(name = "recursiveDeps")
 278     public Object[][] recursiveDeps() {
 279         return new Object[][] {
 280             {   // lib2 is classpath but not analyzed because lib.Lib is not present
 281                 // but it is the only class depending on lib2.Lib2
 282                 List.of("--list-deps", "--class-path", LIB2_DIR.toString(),
 283                         "--ignore-missing-deps", CLASSES_DIR.toString()),
 284                 new String[] {
 285                     "java.base/jdk.internal.misc",
 286                     "java.base/sun.security.util",
 287                     "java.logging",
 288                     "java.sql",
 289                     "java.xml/jdk.xml.internal",
 290                     "jdk.unsupported"
 291                 }
 292             },
 293             {   // lib2 is classpath but not analyzed because lib.Lib is not present
 294                 // but it is the only class depending on lib2.Lib2
 295                 List.of("--print-module-deps", "--class-path", LIB2_DIR.toString(),
 296                         "--ignore-missing-deps", CLASSES_DIR.toString()),
 297                 new String[] {
 298                     "java.base,java.sql,jdk.unsupported"
 299                 }
 300             },
 301             {   // Foo depends on lib.Lib which depends on lib2.Libs
 302                 List.of("--print-module-deps",
 303                         "--ignore-missing-deps", FOO_CLASS.toString()),
 304                 new String[] {
 305                     "java.base,java.sql"
 306                 }
 307             },
 308         };
 309     }
 310 
 311     @Test(dataProvider = "recursiveDeps")
 312     public void testRecursiveDeps(List<String> options, String[] expected) {
 313         JdepsRunner jdeps = JdepsRunner.run(options.toArray(new String[0]));
 314         String[] output = Arrays.stream(jdeps.output())
 315                                 .map(s -> s.trim())
 316                                 .toArray(String[]::new);
 317         assertEquals(output, expected);
 318     }
 319 }