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