1 /*
   2  * Copyright (c) 2014, 2015, 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 8003562
  27  * @summary Basic tests for jdeps -dotoutput option
  28  * @modules java.management
  29  *          jdk.dev/com.sun.tools.jdeps
  30  * @build Test p.Foo p.Bar javax.activity.NotCompactProfile
  31  * @run main DotFileTest
  32  */
  33 
  34 import java.io.File;
  35 import java.io.IOException;
  36 import java.io.PrintWriter;
  37 import java.io.StringWriter;
  38 import java.nio.file.DirectoryStream;
  39 import java.nio.file.Files;
  40 import java.nio.file.Path;
  41 import java.nio.file.Paths;
  42 import java.util.*;
  43 import java.util.regex.*;
  44 
  45 public class DotFileTest {
  46     public static void main(String... args) throws Exception {
  47         int errors = 0;
  48         errors += new DotFileTest().run();
  49         if (errors > 0)
  50             throw new Exception(errors + " errors found");
  51     }
  52 
  53     final Path dir;
  54     final Path dotoutput;
  55     DotFileTest() {
  56         this.dir = Paths.get(System.getProperty("test.classes", "."));
  57         this.dotoutput = dir.resolve("dots");
  58     }
  59 
  60     int run() throws IOException {
  61         File testDir = dir.toFile();
  62         // test a .class file
  63         test(new File(testDir, "Test.class"),
  64              new String[] {"java.lang", "p"},
  65              new String[] {"compact1", "not found"});
  66         // test a directory
  67         // also test non-SE javax.activity class dependency
  68         test(new File(testDir, "p"),
  69              new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
  70              new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
  71              new String[] {"-classpath", testDir.getPath()});
  72         // test class-level dependency output
  73         test(new File(testDir, "Test.class"),
  74              new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
  75              new String[] {"compact1", "compact1", "not found", "not found"},
  76              new String[] {"-verbose:class"});
  77         // test -filter:none option
  78         test(new File(testDir, "p"),
  79              new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"},
  80              new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"},
  81              new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"});
  82         // test -filter:archive option
  83         test(new File(testDir, "p"),
  84              new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
  85              new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
  86              new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"});
  87         // test -p option
  88         test(new File(testDir, "Test.class"),
  89              new String[] {"p.Foo", "p.Bar"},
  90              new String[] {"not found", "not found"},
  91              new String[] {"-verbose:class", "-p", "p"});
  92         // test -e option
  93         test(new File(testDir, "Test.class"),
  94              new String[] {"p.Foo", "p.Bar"},
  95              new String[] {"not found", "not found"},
  96              new String[] {"-verbose:class", "-e", "p\\..*"});
  97         test(new File(testDir, "Test.class"),
  98              new String[] {"java.lang"},
  99              new String[] {"compact1"},
 100              new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
 101         // test -classpath options
 102         test(new File(testDir, "Test.class"),
 103              new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
 104              new String[] {"compact1", "compact1", testDir.getName(), testDir.getName()},
 105              new String[] {"-v", "-classpath", testDir.getPath()});
 106 
 107         testSummary(new File(testDir, "Test.class"),
 108              new String[] {"java.base", testDir.getName()},
 109              new String[] {"compact1", ""},
 110              new String[] {"-classpath", testDir.getPath()});
 111         testSummary(new File(testDir, "Test.class"),
 112              new String[] {"java.lang", "p"},
 113              new String[] {"compact1", testDir.getName()},
 114              new String[] {"-v", "-classpath", testDir.getPath()});
 115         return errors;
 116     }
 117 
 118     void test(File file, String[] expect, String[] profiles) throws IOException {
 119         test(file, expect, profiles, new String[0]);
 120     }
 121 
 122     void test(File file, String[] expect, String[] profiles, String[] options)
 123         throws IOException
 124     {
 125         Path dotfile = dotoutput.resolve(file.toPath().getFileName().toString() + ".dot");
 126 
 127         List<String> args = new ArrayList<>(Arrays.asList(options));
 128         args.add("-dotoutput");
 129         args.add(dotoutput.toString());
 130         if (file != null) {
 131             args.add(file.getPath());
 132         }
 133 
 134         Map<String,String> result = jdeps(args, dotfile);
 135         checkResult("dependencies", expect, result.keySet());
 136 
 137         // with -P option
 138         List<String> argsWithDashP = new ArrayList<>();
 139         argsWithDashP.add("-dotoutput");
 140         argsWithDashP.add(dotoutput.toString());
 141         argsWithDashP.add("-P");
 142         argsWithDashP.addAll(args);
 143 
 144         result = jdeps(argsWithDashP, dotfile);
 145         checkResult("profiles", expect, profiles, result);
 146     }
 147 
 148     void testSummary(File file, String[] expect, String[] profiles, String[] options)
 149         throws IOException
 150     {
 151         Path dotfile = dotoutput.resolve("summary.dot");
 152 
 153         List<String> args = new ArrayList<>(Arrays.asList(options));
 154         args.add("-dotoutput");
 155         args.add(dotoutput.toString());
 156         if (file != null) {
 157             args.add(file.getPath());
 158         }
 159 
 160         Map<String,String> result = jdeps(args, dotfile);
 161         checkResult("dependencies", expect, result.keySet());
 162 
 163         // with -P option
 164         List<String> argsWithDashP = new ArrayList<>();
 165         argsWithDashP.add("-dotoutput");
 166         argsWithDashP.add(dotoutput.toString());
 167         argsWithDashP.add("-P");
 168         argsWithDashP.addAll(args);
 169 
 170         result = jdeps(argsWithDashP, dotfile);
 171         checkResult("profiles", expect, profiles, result);
 172     }
 173 
 174     Map<String,String> jdeps(List<String> args, Path dotfile) throws IOException {
 175         if (Files.exists(dotoutput)) {
 176             try (DirectoryStream<Path> stream = Files.newDirectoryStream(dotoutput)) {
 177                 for (Path p : stream) {
 178                     Files.delete(p);
 179                 }
 180             }
 181             Files.delete(dotoutput);
 182         }
 183         // invoke jdeps
 184         StringWriter sw = new StringWriter();
 185         PrintWriter pw = new PrintWriter(sw);
 186         System.err.println("jdeps " + args);
 187         int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw);
 188         pw.close();
 189         String out = sw.toString();
 190         if (!out.isEmpty())
 191             System.err.println(out);
 192         if (rc != 0)
 193             throw new Error("jdeps failed: rc=" + rc);
 194 
 195         // check output files
 196         if (Files.notExists(dotfile)) {
 197             throw new RuntimeException(dotfile + " doesn't exist");
 198         }
 199         return parse(dotfile);
 200     }
 201     private static Pattern pattern = Pattern.compile("(.*) -> +([^ ]*) (.*)");
 202     private Map<String,String> parse(Path outfile) throws IOException {
 203         Map<String,String> result = new LinkedHashMap<>();
 204         for (String line : Files.readAllLines(outfile)) {
 205             line = line.replace('"', ' ').replace(';', ' ');
 206             Matcher pm = pattern.matcher(line);
 207             if (pm.find()) {
 208                 String origin = pm.group(1).trim();
 209                 String target = pm.group(2).trim();
 210                 String module = pm.group(3).replace('(', ' ').replace(')', ' ').trim();
 211                 result.put(target, module);
 212             }
 213         }
 214         return result;
 215     }
 216 
 217     void checkResult(String label, String[] expect, Collection<String> found) {
 218         List<String> list = Arrays.asList(expect);
 219         if (!isEqual(list, found))
 220             error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'");
 221     }
 222 
 223     void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) {
 224         if (expect.length != profiles.length)
 225             error("Invalid expected names and profiles");
 226 
 227         // check the dependencies
 228         checkResult(label, expect, result.keySet());
 229         // check profile information
 230         checkResult(label, profiles, result.values());
 231         for (int i=0; i < expect.length; i++) {
 232             String profile = result.get(expect[i]);
 233             if (!profile.equals(profiles[i]))
 234                 error("Unexpected profile: '" + profile + "', expected: '" + profiles[i] + "'");
 235         }
 236     }
 237 
 238     boolean isEqual(List<String> expected, Collection<String> found) {
 239         if (expected.size() != found.size())
 240             return false;
 241 
 242         List<String> list = new ArrayList<>(found);
 243         list.removeAll(expected);
 244         return list.isEmpty();
 245     }
 246 
 247     void error(String msg) {
 248         System.err.println("Error: " + msg);
 249         errors++;
 250     }
 251 
 252     int errors;
 253 }