aoqi@0: /* aoqi@0: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * @test aoqi@0: * @bug 8015912 8029216 aoqi@0: * @summary Test -apionly and -jdkinternals options aoqi@0: * @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G aoqi@0: * @run main APIDeps aoqi@0: */ aoqi@0: aoqi@0: import java.io.File; aoqi@0: import java.io.IOException; aoqi@0: import java.io.PrintWriter; aoqi@0: import java.io.StringWriter; aoqi@0: import java.nio.file.Path; aoqi@0: import java.nio.file.Paths; aoqi@0: import java.util.*; aoqi@0: import java.util.regex.*; aoqi@0: aoqi@0: public class APIDeps { aoqi@0: private static boolean symbolFileExist = initProfiles(); aoqi@0: private static boolean initProfiles() { aoqi@0: // check if ct.sym exists; if not use the profiles.properties file aoqi@0: Path home = Paths.get(System.getProperty("java.home")); aoqi@0: if (home.endsWith("jre")) { aoqi@0: home = home.getParent(); aoqi@0: } aoqi@0: Path ctsym = home.resolve("lib").resolve("ct.sym"); aoqi@0: boolean symbolExists = ctsym.toFile().exists(); aoqi@0: if (!symbolExists) { aoqi@0: Path testSrcProfiles = aoqi@0: Paths.get(System.getProperty("test.src", "."), "profiles.properties"); aoqi@0: if (!testSrcProfiles.toFile().exists()) aoqi@0: throw new Error(testSrcProfiles + " does not exist"); aoqi@0: System.out.format("%s doesn't exist.%nUse %s to initialize profiles info%n", aoqi@0: ctsym, testSrcProfiles); aoqi@0: System.setProperty("jdeps.profiles", testSrcProfiles.toString()); aoqi@0: } aoqi@0: return symbolExists; aoqi@0: } aoqi@0: aoqi@0: public static void main(String... args) throws Exception { aoqi@0: int errors = 0; aoqi@0: errors += new APIDeps().run(); aoqi@0: if (errors > 0) aoqi@0: throw new Exception(errors + " errors found"); aoqi@0: } aoqi@0: aoqi@0: int run() throws IOException { aoqi@0: File testDir = new File(System.getProperty("test.classes", ".")); aoqi@0: String testDirBasename = testDir.toPath().getFileName().toString(); aoqi@0: File mDir = new File(testDir, "m"); aoqi@0: // all dependencies aoqi@0: test(new File(mDir, "Bar.class"), aoqi@0: new String[] {"java.lang.Object", "java.lang.String", aoqi@0: "java.util.Set", "java.util.HashSet", aoqi@0: "java.lang.management.ManagementFactory", aoqi@0: "java.lang.management.RuntimeMXBean", aoqi@0: "b.B", "c.C", "d.D", "f.F", "g.G"}, aoqi@0: new String[] {"compact1", "compact3", testDirBasename}, aoqi@0: new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); aoqi@0: test(new File(mDir, "Foo.class"), aoqi@0: new String[] {"c.I", "e.E", "f.F", "m.Bar"}, aoqi@0: new String[] {testDirBasename}, aoqi@0: new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); aoqi@0: test(new File(mDir, "Gee.class"), aoqi@0: new String[] {"g.G", "sun.misc.Lock"}, aoqi@0: new String[] {testDirBasename, "JDK internal API"}, aoqi@0: new String[] {"-classpath", testDir.getPath(), "-verbose"}); aoqi@0: aoqi@0: // -jdkinternals aoqi@0: test(new File(mDir, "Gee.class"), aoqi@0: new String[] {"sun.misc.Lock"}, aoqi@0: new String[] {"JDK internal API"}, aoqi@0: new String[] {"-jdkinternals"}); aoqi@0: // -jdkinternals parses all classes on -classpath and the input arguments aoqi@0: test(new File(mDir, "Gee.class"), aoqi@0: new String[] {"sun.misc.Lock", "sun.misc.Unsafe"}, aoqi@0: new String[] {"JDK internal API"}, aoqi@0: new String[] {"-classpath", testDir.getPath(), "-jdkinternals"}); aoqi@0: aoqi@0: // parse only APIs aoqi@0: // parse only APIs aoqi@0: test(mDir, aoqi@0: new String[] {"java.lang.Object", "java.lang.String", aoqi@0: "java.util.Set", aoqi@0: "c.C", "d.D", "c.I", "e.E", "m.Bar"}, aoqi@0: new String[] {"compact1", testDirBasename, mDir.getName()}, aoqi@0: new String[] {"-classpath", testDir.getPath(), "-verbose", "-P", "-apionly"}); aoqi@0: return errors; aoqi@0: } aoqi@0: aoqi@0: void test(File file, String[] expect, String[] profiles) { aoqi@0: test(file, expect, profiles, new String[0]); aoqi@0: } aoqi@0: aoqi@0: void test(File file, String[] expect, String[] profiles, String[] options) { aoqi@0: List args = new ArrayList<>(Arrays.asList(options)); aoqi@0: if (file != null) { aoqi@0: args.add(file.getPath()); aoqi@0: } aoqi@0: checkResult("api-dependencies", expect, profiles, aoqi@0: jdeps(args.toArray(new String[0]))); aoqi@0: } aoqi@0: aoqi@0: Map jdeps(String... args) { aoqi@0: StringWriter sw = new StringWriter(); aoqi@0: PrintWriter pw = new PrintWriter(sw); aoqi@0: System.err.println("jdeps " + Arrays.toString(args)); aoqi@0: int rc = com.sun.tools.jdeps.Main.run(args, pw); aoqi@0: pw.close(); aoqi@0: String out = sw.toString(); aoqi@0: if (!out.isEmpty()) aoqi@0: System.err.println(out); aoqi@0: if (rc != 0) aoqi@0: throw new Error("jdeps failed: rc=" + rc); aoqi@0: return findDeps(out); aoqi@0: } aoqi@0: aoqi@0: // Pattern used to parse lines aoqi@0: private static Pattern linePattern = Pattern.compile(".*\r?\n"); aoqi@0: private static Pattern pattern = Pattern.compile("\\s+ -> (\\S+) +(.*)"); aoqi@0: aoqi@0: // Use the linePattern to break the given String into lines, applying aoqi@0: // the pattern to each line to see if we have a match aoqi@0: private static Map findDeps(String out) { aoqi@0: Map result = new HashMap<>(); aoqi@0: Matcher lm = linePattern.matcher(out); // Line matcher aoqi@0: Matcher pm = null; // Pattern matcher aoqi@0: int lines = 0; aoqi@0: while (lm.find()) { aoqi@0: lines++; aoqi@0: CharSequence cs = lm.group(); // The current line aoqi@0: if (pm == null) aoqi@0: pm = pattern.matcher(cs); aoqi@0: else aoqi@0: pm.reset(cs); aoqi@0: if (pm.find()) aoqi@0: result.put(pm.group(1), pm.group(2).trim()); aoqi@0: if (lm.end() == out.length()) aoqi@0: break; aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: void checkResult(String label, String[] expect, Collection found) { aoqi@0: List list = Arrays.asList(expect); aoqi@0: if (!isEqual(list, found)) aoqi@0: error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'"); aoqi@0: } aoqi@0: aoqi@0: void checkResult(String label, String[] expect, String[] profiles, Map result) { aoqi@0: // check the dependencies aoqi@0: checkResult(label, expect, result.keySet()); aoqi@0: // check profile information aoqi@0: Set values = new TreeSet<>(); aoqi@0: String internal = "JDK internal API"; aoqi@0: for (String s: result.values()) { aoqi@0: if (s.startsWith(internal)){ aoqi@0: values.add(internal); aoqi@0: } else { aoqi@0: values.add(s); aoqi@0: } aoqi@0: } aoqi@0: checkResult(label, profiles, values); aoqi@0: } aoqi@0: aoqi@0: boolean isEqual(List expected, Collection found) { aoqi@0: if (expected.size() != found.size()) aoqi@0: return false; aoqi@0: aoqi@0: List list = new ArrayList<>(found); aoqi@0: list.removeAll(expected); aoqi@0: return list.isEmpty(); aoqi@0: } aoqi@0: aoqi@0: void error(String msg) { aoqi@0: System.err.println("Error: " + msg); aoqi@0: errors++; aoqi@0: } aoqi@0: aoqi@0: int errors; aoqi@0: }