1/*
2 * Copyright (c) 2011, 2016, 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 8004182 8028545
27 * @summary Add support for profiles in javac
28 * @modules java.desktop
29 *          java.sql.rowset
30 *          jdk.compiler/com.sun.tools.javac.api
31 *          jdk.compiler/com.sun.tools.javac.jvm
32 *          jdk.security.auth
33 */
34
35import java.io.PrintWriter;
36import java.io.StringWriter;
37import java.lang.annotation.Annotation;
38import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
40import java.lang.reflect.InvocationTargetException;
41import java.lang.reflect.Method;
42import java.net.URI;
43import java.util.ArrayList;
44import java.util.Arrays;
45import java.util.Collections;
46import java.util.EnumMap;
47import java.util.List;
48import java.util.Map;
49
50import javax.tools.Diagnostic;
51import javax.tools.DiagnosticCollector;
52import javax.tools.JavaCompiler;
53import javax.tools.JavaFileObject;
54import javax.tools.SimpleJavaFileObject;
55import javax.tools.StandardJavaFileManager;
56
57import com.sun.source.util.JavacTask;
58import com.sun.tools.javac.api.JavacTool;
59import com.sun.tools.javac.jvm.Profile;
60import com.sun.tools.javac.jvm.Target;
61
62
63public class ProfileOptionTest {
64    public static void main(String... args) throws Exception {
65        new ProfileOptionTest().run();
66    }
67
68    private final JavaCompiler javac = JavacTool.create();
69    private final StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null);
70
71
72    // ---------- Test cases, invoked reflectively via run. ----------
73
74    @Test
75    void testInvalidProfile_API() throws Exception {
76        JavaFileObject fo = new StringJavaFileObject("Test.java", "class Test { }");
77        String badName = "foo";
78        List<String> opts = Arrays.asList("--release", "8", "-profile", badName);
79        StringWriter sw = new StringWriter();
80        try {
81            JavacTask task = (JavacTask) javac.getTask(sw, fm, null, opts, null,
82                Arrays.asList(fo));
83            throw new Exception("expected exception not thrown");
84        } catch (IllegalArgumentException e) {
85            // expected
86        }
87    }
88
89    @Test
90    void testInvalidProfile_CommandLine() throws Exception {
91        String badName = "foo";
92        String[] opts = { "--release", "8", "-profile", badName };
93        StringWriter sw = new StringWriter();
94        PrintWriter pw = new PrintWriter(sw);
95        int rc = com.sun.tools.javac.Main.compile(opts, pw);
96
97        // sadly, command line errors are not (yet?) reported to
98        // the diag listener
99        String out = sw.toString();
100        if (!out.isEmpty())
101            System.err.println(out.trim());
102
103        if (!out.contains("invalid profile: " + badName)) {
104            error("expected message not found");
105        }
106    }
107
108    @Test
109    void testTargetProfileCombinations() throws Exception {
110        JavaFileObject fo = new StringJavaFileObject("Test.java", "class Test { }");
111        for (Target t: Target.values()) {
112            switch (t) {
113                case JDK1_1:
114                case JDK1_2:
115                case JDK1_3:
116                case JDK1_4:
117                case JDK1_5: // not supported
118                    continue;
119            }
120
121            for (Profile p: Profile.values()) {
122                List<String> opts = new ArrayList<>();
123                opts.addAll(Arrays.asList("-source", t.name, "-target", t.name));
124                opts.add("-Xlint:-options"); // don't warn about no -bootclasspath
125                if (p != Profile.DEFAULT)
126                    opts.addAll(Arrays.asList("-profile", p.name));
127
128                IllegalStateException ise;
129                StringWriter sw = new StringWriter();
130                try {
131                    JavacTask task = (JavacTask) javac.getTask(sw, fm, null, opts, null,
132                            Arrays.asList(fo));
133                    task.analyze();
134                    ise = null;
135                } catch (IllegalStateException e) {
136                    ise = e;
137                }
138
139                // sadly, command line errors are not (yet?) reported to
140                // the diag listener
141                String out = sw.toString();
142                if (!out.isEmpty())
143                    System.err.println(out.trim());
144
145                switch (t) {
146                    case JDK1_8:
147                        if (ise != null)
148                            error("unexpected exception from compiler: " + ise);
149                        break;
150                    case JDK1_9:
151                        if (p == Profile.DEFAULT)
152                            break;
153                        if (ise == null)
154                            error("IllegalStateException not thrown as expected");
155                        else if (!ise.getMessage().contains("option -profile " +
156                                "not allowed with target " + t.name)) {
157                            error("exception not thrown as expected: " + ise);
158                        }
159                        break;
160                    default:
161                        if (p == Profile.DEFAULT)
162                            break;
163                        if (ise == null)
164                            error("IllegalStateException not thrown as expected");
165                        else if (!ise.getMessage().contains("profile " + p.name
166                                    + " is not valid for target release " + t.name)) {
167                            error("exception not thrown as expected: " + ise);
168                        }
169                        break;
170                }
171            }
172        }
173    }
174
175    @Test
176    void testClassesInProfiles() throws Exception {
177        for (Profile p: Profile.values()) {
178            for (Map.Entry<Profile, List<JavaFileObject>> e: testClasses.entrySet()) {
179                for (JavaFileObject fo: e.getValue()) {
180                    DiagnosticCollector<JavaFileObject> dl =
181                            new DiagnosticCollector<JavaFileObject>();
182                    List<String> opts = (p == Profile.DEFAULT)
183                            ? Collections.<String>emptyList()
184                            : Arrays.asList("--release", "8", "-profile", p.name);
185                    JavacTask task = (JavacTask) javac.getTask(null, fm, dl, opts, null,
186                            Arrays.asList(fo));
187                    task.analyze();
188
189                    List<String> expectDiagCodes = new ArrayList<>();
190                    if (fo.getName().equals("TPolicyFile.java")) {
191                        expectDiagCodes.add("compiler.warn.has.been.deprecated.for.removal");
192                    }
193
194                    if (p.value < e.getKey().value) {
195                        expectDiagCodes.add("compiler.err.not.in.profile");
196                    }
197
198                    checkDiags(opts + " " + fo.getName(), dl.getDiagnostics(), expectDiagCodes);
199                }
200            }
201        }
202    }
203
204    Map<Profile, List<JavaFileObject>> testClasses =
205            new EnumMap<Profile, List<JavaFileObject>>(Profile.class);
206
207    void initTestClasses() {
208        // The following table assumes the existence of specific classes
209        // in specific profiles, as defined in the Java SE 8 spec.
210        init(Profile.COMPACT1,
211                java.lang.String.class);
212
213        init(Profile.COMPACT2,
214                javax.xml.XMLConstants.class);
215
216        //init(Profile.COMPACT3,
217        //        javax.sql.rowset.Predicate.class,
218        //        com.sun.security.auth.PolicyFile.class); // specifically included in 3
219
220        init(Profile.COMPACT3,
221                javax.sql.rowset.Predicate.class);
222
223        init(Profile.DEFAULT,
224                java.beans.BeanInfo.class);
225    }
226
227    void init(Profile p, Class<?>... classes) {
228        List<JavaFileObject> srcs = new ArrayList<JavaFileObject>();
229        for (Class<?> c: classes) {
230            String name = "T" + c.getSimpleName();
231            String src =
232                    "class T" + name + "{" + "\n" +
233                    "    Class<?> c = " + c.getName() + ".class;\n" +
234                    "}";
235            srcs.add(new StringJavaFileObject(name + ".java", src));
236        }
237        testClasses.put(p, srcs);
238    }
239
240    void checkDiags(String msg, List<Diagnostic<? extends JavaFileObject>> diags, List<String> expectDiagCodes) {
241        System.err.print(msg);
242        if (diags.isEmpty())
243            System.err.println(" OK");
244        else {
245            System.err.println();
246            System.err.println(diags);
247        }
248
249        List<String> foundDiagCodes = new ArrayList<String>();
250        for (Diagnostic<? extends JavaFileObject> d: diags)
251            foundDiagCodes.add(d.getCode());
252
253        if (!foundDiagCodes.equals(expectDiagCodes)) {
254            System.err.println("Found diag codes:    " + foundDiagCodes);
255            System.err.println("Expected diag codes: " + expectDiagCodes);
256            error("expected diagnostics not found");
257        }
258    }
259
260    /** Marker annotation for test cases. */
261    @Retention(RetentionPolicy.RUNTIME)
262    @interface Test { }
263
264    /** Run all test cases. */
265    void run() throws Exception {
266        try {
267            initTestClasses();
268
269            for (Method m: getClass().getDeclaredMethods()) {
270                Annotation a = m.getAnnotation(Test.class);
271                if (a != null) {
272                    System.err.println(m.getName());
273                    try {
274                        m.invoke(this, new Object[] { });
275                    } catch (InvocationTargetException e) {
276                        Throwable cause = e.getCause();
277                        throw (cause instanceof Exception) ? ((Exception) cause) : e;
278                    }
279                    System.err.println();
280                }
281            }
282
283            if (errors > 0)
284                throw new Exception(errors + " errors occurred");
285        } finally {
286            fm.close();
287        }
288    }
289
290    void error(String msg) {
291        System.err.println("Error: " + msg);
292        errors++;
293    }
294
295    int errors;
296
297    private static class StringJavaFileObject extends SimpleJavaFileObject {
298        StringJavaFileObject(String name, String text) {
299            super(URI.create(name), JavaFileObject.Kind.SOURCE);
300            this.text = text;
301        }
302        @Override
303        public CharSequence getCharContent(boolean b) {
304            return text;
305        }
306        private String text;
307    }
308}
309