APIDeps.java revision 2942:08092deced3f
1189251Ssam/*
2189251Ssam * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3189251Ssam * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4189251Ssam *
5252726Srpaulo * This code is free software; you can redistribute it and/or modify it
6252726Srpaulo * under the terms of the GNU General Public License version 2 only, as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * This code is distributed in the hope that it will be useful, but WITHOUT
10189251Ssam * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11189251Ssam * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12189251Ssam * version 2 for more details (a copy is included in the LICENSE file that
13189251Ssam * accompanied this code).
14189251Ssam *
15189251Ssam * You should have received a copy of the GNU General Public License version
16281806Srpaulo * 2 along with this work; if not, write to the Free Software Foundation,
17189251Ssam * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18189251Ssam *
19189251Ssam * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20189251Ssam * or visit www.oracle.com if you need additional information or have any
21189251Ssam * questions.
22189251Ssam */
23189251Ssam
24189251Ssam/*
25189251Ssam * @test
26189251Ssam * @bug 8015912 8029216 8048063 8050804
27189251Ssam * @summary Test -apionly and -jdkinternals options
28189251Ssam * @modules java.base/sun.misc
29281806Srpaulo *          java.management
30189251Ssam *          jdk.jdeps/com.sun.tools.classfile
31281806Srpaulo *          jdk.jdeps/com.sun.tools.jdeps
32189251Ssam * @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G
33189251Ssam * @run main APIDeps
34189251Ssam */
35189251Ssam
36189251Ssamimport java.io.File;
37189251Ssamimport java.io.IOException;
38189251Ssamimport java.io.PrintWriter;
39189251Ssamimport java.io.StringWriter;
40189251Ssamimport java.nio.file.Path;
41189251Ssamimport java.nio.file.Paths;
42189251Ssamimport java.util.*;
43189251Ssamimport java.util.regex.*;
44189251Ssam
45189251Ssampublic class APIDeps {
46189251Ssam    public static void main(String... args) throws Exception {
47189251Ssam        int errors = 0;
48189251Ssam        errors += new APIDeps().run();
49189251Ssam        if (errors > 0)
50189251Ssam            throw new Exception(errors + " errors found");
51189251Ssam    }
52189251Ssam
53189251Ssam    int run() throws IOException {
54189251Ssam        File testDir = new File(System.getProperty("test.classes", "."));
55189251Ssam        String testDirBasename = testDir.toPath().getFileName().toString();
56189251Ssam        File mDir = new File(testDir, "m");
57189251Ssam        // all dependencies
58189251Ssam        test(new File(mDir, "Bar.class"),
59189251Ssam             new String[] {"java.lang.Object", "java.lang.String",
60189251Ssam                           "java.util.Set", "java.util.HashSet",
61189251Ssam                           "java.lang.management.ManagementFactory",
62189251Ssam                           "java.lang.management.RuntimeMXBean",
63189251Ssam                           "b.B", "c.C", "d.D", "f.F", "g.G"},
64189251Ssam             new String[] {"compact1", "compact3", testDirBasename},
65189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
66189251Ssam        test(new File(mDir, "Foo.class"),
67189251Ssam             new String[] {"c.I", "e.E", "f.F"},
68189251Ssam             new String[] {testDirBasename},
69189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P"});
70189251Ssam        test(new File(mDir, "Foo.class"),
71189251Ssam             new String[] {"c.I", "e.E", "f.F", "m.Bar"},
72189251Ssam             new String[] {testDirBasename},
73189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-filter:none", "-P"});
74189251Ssam        test(new File(mDir, "Gee.class"),
75189251Ssam             new String[] {"g.G", "sun.misc.Lock", "com.sun.tools.classfile.ClassFile",
76189251Ssam                           "com.sun.management.ThreadMXBean", "com.sun.source.tree.BinaryTree"},
77189251Ssam             new String[] {testDirBasename, "JDK internal API", "compact3", ""},
78189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
79189251Ssam
80189251Ssam        // -jdkinternals
81189251Ssam        test(new File(mDir, "Gee.class"),
82189251Ssam             new String[] {"sun.misc.Lock", "com.sun.tools.classfile.ClassFile"},
83189251Ssam             new String[] {"JDK internal API"},
84189251Ssam             new String[] {"-jdkinternals"});
85189251Ssam        // -jdkinternals parses all classes on -classpath and the input arguments
86189251Ssam        test(new File(mDir, "Gee.class"),
87189251Ssam             new String[] {"com.sun.tools.jdeps.Main", "com.sun.tools.classfile.ClassFile",
88189251Ssam                           "sun.misc.Lock", "sun.misc.Unsafe"},
89189251Ssam             new String[] {"JDK internal API"},
90189251Ssam             new String[] {"-classpath", testDir.getPath(), "-jdkinternals"});
91189251Ssam
92189251Ssam        // parse only APIs
93189251Ssam        test(mDir,
94189251Ssam             new String[] {"java.lang.Object", "java.lang.String",
95189251Ssam                           "java.util.Set",
96189251Ssam                           "c.C", "d.D", "c.I", "e.E"},
97189251Ssam             new String[] {"compact1", testDirBasename},
98189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P", "-apionly"});
99189251Ssam
100189251Ssam        test(mDir,
101189251Ssam             new String[] {"java.lang.Object", "java.lang.String",
102189251Ssam                           "java.util.Set",
103189251Ssam                           "c.C", "d.D", "c.I", "e.E", "m.Bar"},
104189251Ssam             new String[] {"compact1", testDirBasename, mDir.getName()},
105189251Ssam             new String[] {"-classpath", testDir.getPath(), "-verbose", "-P", "-apionly"});
106189251Ssam        return errors;
107337817Scy    }
108189251Ssam
109189251Ssam    void test(File file, String[] expect, String[] profiles) {
110189251Ssam        test(file, expect, profiles, new String[0]);
111189251Ssam    }
112189251Ssam
113189251Ssam    void test(File file, String[] expect, String[] profiles, String[] options) {
114189251Ssam        List<String> args = new ArrayList<>(Arrays.asList(options));
115189251Ssam        if (file != null) {
116189251Ssam            args.add(file.getPath());
117189251Ssam        }
118189251Ssam        checkResult("api-dependencies", expect, profiles,
119189251Ssam                    jdeps(args.toArray(new String[0])));
120189251Ssam    }
121189251Ssam
122189251Ssam    Map<String,String> jdeps(String... args) {
123189251Ssam        StringWriter sw = new StringWriter();
124189251Ssam        PrintWriter pw = new PrintWriter(sw);
125189251Ssam        System.err.println("jdeps " + Arrays.toString(args));
126189251Ssam        int rc = com.sun.tools.jdeps.Main.run(args, pw);
127189251Ssam        pw.close();
128189251Ssam        String out = sw.toString();
129346981Scy        if (!out.isEmpty())
130346981Scy            System.err.println(out);
131189251Ssam        if (rc != 0)
132189251Ssam            throw new Error("jdeps failed: rc=" + rc);
133189251Ssam        return findDeps(out);
134189251Ssam    }
135189251Ssam
136189251Ssam    // Pattern used to parse lines
137189251Ssam    private static Pattern linePattern = Pattern.compile(".*\r?\n");
138189251Ssam    private static Pattern pattern = Pattern.compile("\\s+ -> (\\S+) +(.*)");
139337817Scy
140189251Ssam    // Use the linePattern to break the given String into lines, applying
141189251Ssam    // the pattern to each line to see if we have a match
142189251Ssam    private static Map<String,String> findDeps(String out) {
143189251Ssam        Map<String,String> result = new HashMap<>();
144189251Ssam        Matcher lm = linePattern.matcher(out);  // Line matcher
145189251Ssam        Matcher pm = null;                      // Pattern matcher
146189251Ssam        int lines = 0;
147189251Ssam        while (lm.find()) {
148189251Ssam            lines++;
149189251Ssam            CharSequence cs = lm.group();       // The current line
150189251Ssam            if (pm == null)
151189251Ssam                pm = pattern.matcher(cs);
152189251Ssam            else
153189251Ssam                pm.reset(cs);
154189251Ssam            if (pm.find())
155189251Ssam                result.put(pm.group(1), pm.group(2).trim());
156189251Ssam            if (lm.end() == out.length())
157189251Ssam                break;
158189251Ssam        }
159189251Ssam        return result;
160189251Ssam    }
161189251Ssam
162189251Ssam    void checkResult(String label, String[] expect, Collection<String> found) {
163189251Ssam        List<String> list = Arrays.asList(expect);
164189251Ssam        if (!isEqual(list, found))
165189251Ssam            error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'");
166189251Ssam    }
167189251Ssam
168189251Ssam    void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) {
169189251Ssam        // check the dependencies
170189251Ssam        checkResult(label, expect, result.keySet());
171189251Ssam        // check profile information
172189251Ssam        Set<String> values = new TreeSet<>();
173189251Ssam        String internal = "JDK internal API";
174189251Ssam        for (String s: result.values()) {
175189251Ssam            if (s.startsWith(internal)){
176189251Ssam                values.add(internal);
177189251Ssam            } else {
178189251Ssam                values.add(s);
179189251Ssam            }
180189251Ssam        }
181189251Ssam        checkResult(label, profiles, values);
182189251Ssam    }
183189251Ssam
184337817Scy    boolean isEqual(List<String> expected, Collection<String> found) {
185189251Ssam        if (expected.size() != found.size())
186189251Ssam            return false;
187189251Ssam
188189251Ssam        List<String> list = new ArrayList<>(found);
189189251Ssam        list.removeAll(expected);
190189251Ssam        return list.isEmpty();
191189251Ssam    }
192189251Ssam
193189251Ssam    void error(String msg) {
194189251Ssam        System.err.println("Error: " + msg);
195189251Ssam        errors++;
196189251Ssam    }
197189251Ssam
198189251Ssam    int errors;
199189251Ssam}
200189251Ssam