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