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