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.reflect.annotation",
  98                                 "java.compiler",
  99                              }
 100             },
 101         };
 102     }
 103 
 104     @Test(dataProvider = "jdkModules")
 105     public void testJDKModule(String moduleName, String[] expected) {
 106         JdepsRunner jdeps = JdepsRunner.run(
 107             "--list-deps", "-m", moduleName
 108         );
 109         String[] output = Arrays.stream(jdeps.output())
 110                                 .map(s -> s.trim())
 111                                 .toArray(String[]::new);
 112         assertEquals(output, expected);
 113     }
 114 
 115     @Test(dataProvider = "listdeps")
 116     public void testListDeps(Path classes, String[] expected) {
 117         JdepsRunner jdeps = JdepsRunner.run(
 118             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 119             "--list-deps", classes.toString()
 120         );
 121         String[] output = Arrays.stream(jdeps.output())
 122                                 .map(s -> s.trim())
 123                                 .toArray(String[]::new);
 124         assertEquals(output, expected);
 125     }
 126 
 127     @Test(dataProvider = "reduceddeps")
 128     public void testListReducedDeps(Path classes, String[]  expected) {
 129         JdepsRunner jdeps = JdepsRunner.run(
 130             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 131             "--list-reduced-deps", classes.toString()
 132         );
 133         String[] output = Arrays.stream(jdeps.output())
 134                                 .map(s -> s.trim())
 135                                 .toArray(String[]::new);
 136         assertEquals(output, expected);
 137     }
 138 
 139 
 140     @DataProvider(name = "listdeps")
 141     public Object[][] listdeps() {
 142         return new Object[][] {
 143             { CLASSES_DIR,  new String[] {
 144                                 "java.base/jdk.internal.misc",
 145                                 "java.base/sun.security.util",
 146                                 "java.logging",
 147                                 "java.management",
 148                                 "java.sql",
 149                                 "java.xml/jdk.xml.internal",
 150                                 "jdk.unsupported"
 151                             }
 152             },
 153 
 154             { HI_CLASS,     new String[] {
 155                                 "java.base"
 156                             }
 157             },
 158 
 159             { FOO_CLASS,    new String[] {
 160                                 "java.base",
 161                                 "java.logging",
 162                                 "java.management",
 163                                 "java.sql",
 164                                 "java.xml"
 165                             }
 166             },
 167 
 168             { BAR_CLASS,    new String[] {
 169                                 "java.base/sun.security.util",
 170                                 "java.xml/jdk.xml.internal",
 171                             }
 172             },
 173 
 174             { UNSAFE_CLASS, new String[] {
 175                                 "java.base/jdk.internal.misc",
 176                                 "jdk.unsupported"
 177                             }
 178             },
 179         };
 180     }
 181 
 182     @DataProvider(name = "reduceddeps")
 183     public Object[][] reduceddeps() {
 184         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 185 
 186         return new Object[][] {
 187             { CLASSES_DIR,  new String[] {
 188                                 "java.base/jdk.internal.misc",
 189                                 "java.base/sun.security.util",
 190                                 "java.management",
 191                                 "java.sql",
 192                                 "java.xml/jdk.xml.internal",
 193                                 "jdk.unsupported"
 194                             }
 195             },
 196 
 197             { HI_CLASS,     new String[] {
 198                                 "java.base"
 199                             }
 200             },
 201 
 202             { FOO_CLASS,    new String[] {
 203                                 "java.base",
 204                                 "java.management",
 205                                 "java.sql"
 206                             }
 207             },
 208 
 209             { BAR_CLASS,    new String[] {
 210                                 "java.base/sun.security.util",
 211                                 "java.xml/jdk.xml.internal",
 212                             }
 213             },
 214 
 215             { UNSAFE_CLASS, new String[] {
 216                                 "java.base/jdk.internal.misc",
 217                                 "jdk.unsupported"
 218                             }
 219             },
 220         };
 221     }
 222 
 223     @Test(dataProvider = "moduledeps")
 224     public void testPrintModuleDeps(Path classes, String expected) {
 225         JdepsRunner jdeps = JdepsRunner.run(
 226             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 227             "--print-module-deps", classes.toString()
 228         );
 229         String output = Arrays.stream(jdeps.output())
 230             .map(s -> s.trim())
 231             .collect(Collectors.joining(","));
 232         assertEquals(output, expected);
 233     }
 234 
 235 
 236     @DataProvider(name = "moduledeps")
 237     public Object[][] moduledeps() {
 238         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 239 
 240         return new Object[][] {
 241             // java.xml is an implied reads edge from java.sql
 242             { CLASSES_DIR,  "java.base,java.management,java.sql,jdk.unsupported"},
 243             { HI_CLASS,     "java.base"},
 244             { FOO_CLASS,    "java.base,java.management,java.sql"},
 245             { BAR_CLASS,    "java.base,java.xml"},
 246             { UNSAFE_CLASS, "java.base,jdk.unsupported"},
 247         };
 248     }
 249 
 250     @Test(dataProvider = "noRecursiveModuledeps")
 251     public void testNoRecursiveModuleDeps(Path classes, String expected) {
 252         JdepsRunner jdeps = JdepsRunner.run(
 253             "--class-path", LIB_DIR.toString() + File.pathSeparator + LIB2_DIR.toString(),
 254             "--print-module-deps", "--no-recursive", classes.toString()
 255         );
 256         String output = Arrays.stream(jdeps.output())
 257             .map(s -> s.trim())
 258             .collect(Collectors.joining(","));
 259         assertEquals(output, expected);
 260     }
 261 
 262     @DataProvider(name = "noRecursiveModuledeps")
 263     public Object[][] noRecursiveModuledeps() {
 264         Path barClass = CLASSES_DIR.resolve("z").resolve("Bar.class");
 265 
 266         return new Object[][] {
 267             // java.xml is an implied reads edge from java.sql
 268             { CLASSES_DIR,  "java.base,java.sql,jdk.unsupported"},
 269             { HI_CLASS,     "java.base"},
 270             { FOO_CLASS,    "java.base,java.sql"},
 271             { BAR_CLASS,    "java.base,java.xml"},
 272             { UNSAFE_CLASS, "java.base,jdk.unsupported"},
 273         };
 274     }
 275 
 276     @DataProvider(name = "recursiveDeps")
 277     public Object[][] recursiveDeps() {
 278         return new Object[][] {
 279             {   // lib2 is classpath but not analyzed because lib.Lib is not present
 280                 // but it is the only class depending on lib2.Lib2
 281                 List.of("--list-deps", "--class-path", LIB2_DIR.toString(),
 282                         "--ignore-missing-deps", CLASSES_DIR.toString()),
 283                 new String[] {
 284                     "java.base/jdk.internal.misc",
 285                     "java.base/sun.security.util",
 286                     "java.logging",
 287                     "java.sql",
 288                     "java.xml/jdk.xml.internal",
 289                     "jdk.unsupported"
 290                 }
 291             },
 292             {   // lib2 is classpath but not analyzed because lib.Lib is not present
 293                 // but it is the only class depending on lib2.Lib2
 294                 List.of("--print-module-deps", "--class-path", LIB2_DIR.toString(),
 295                         "--ignore-missing-deps", CLASSES_DIR.toString()),
 296                 new String[] {
 297                     "java.base,java.sql,jdk.unsupported"
 298                 }
 299             },
 300             {   // Foo depends on lib.Lib which depends on lib2.Libs
 301                 List.of("--print-module-deps",
 302                         "--ignore-missing-deps", FOO_CLASS.toString()),
 303                 new String[] {
 304                     "java.base,java.sql"
 305                 }
 306             },
 307         };
 308     }
 309 
 310     @Test(dataProvider = "recursiveDeps")
 311     public void testRecursiveDeps(List<String> options, String[] expected) {
 312         JdepsRunner jdeps = JdepsRunner.run(options.toArray(new String[0]));
 313         String[] output = Arrays.stream(jdeps.output())
 314                                 .map(s -> s.trim())
 315                                 .toArray(String[]::new);
 316         assertEquals(output, expected);
 317     }
 318 }