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
  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<String>();
 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                         if (!out.isEmpty())
 132                             error("unexpected output from compiler");
 133                         break;
 134                     default:
 135                         if (p != Profile.DEFAULT
 136                                 && !out.contains("profile " + p.name
 137                                     + " is not valid for target release " + t.name)) {
 138                             error("expected message not found");
 139                         }
 140                 }
 141             }
 142         }
 143     }
 144 
 145     @Test
 146     void testClassesInProfiles() throws Exception {
 147         for (Profile p: Profile.values()) {
 148             for (Map.Entry<Profile, List<JavaFileObject>> e: testClasses.entrySet()) {
 149                 for (JavaFileObject fo: e.getValue()) {
 150                     DiagnosticCollector<JavaFileObject> dl =
 151                             new DiagnosticCollector<JavaFileObject>();
 152                     List<String> opts = (p == Profile.DEFAULT)
 153                             ? Collections.<String>emptyList()
 154                             : Arrays.asList("-profile", p.name);
 155                     JavacTask task = (JavacTask) javac.getTask(null, fm, dl, opts, null,
 156                             Arrays.asList(fo));
 157                     task.analyze();
 158 
 159                     List<String> expectDiagCodes = (p.value >= e.getKey().value)
 160                             ? Collections.<String>emptyList()
 161                             : Arrays.asList("compiler.err.not.in.profile");
 162 
 163                     checkDiags(opts + " " + fo.getName(), dl.getDiagnostics(), expectDiagCodes);
 164                 }
 165             }
 166         }
 167     }
 168 
 169     Map<Profile, List<JavaFileObject>> testClasses =
 170             new EnumMap<Profile, List<JavaFileObject>>(Profile.class);
 171 
 172     void initTestClasses() {
 173         // The following table assumes the existence of specific classes
 174         // in specific profiles, as defined in the Java SE 8 spec.
 175         init(Profile.COMPACT1,
 176                 java.lang.String.class);
 177 
 178         init(Profile.COMPACT2,
 179                 javax.xml.XMLConstants.class);
 180 
 181         init(Profile.COMPACT3,
 182                 javax.sql.rowset.Predicate.class,
 183                 com.sun.security.auth.PolicyFile.class); // specifically included in 3
 184 
 185         init(Profile.DEFAULT,
 186                 java.beans.BeanInfo.class,
 187                 javax.management.remote.rmi._RMIServer_Stub.class); // specifically excluded in 3
 188     }
 189 
 190     void init(Profile p, Class<?>... classes) {
 191         List<JavaFileObject> srcs = new ArrayList<JavaFileObject>();
 192         for (Class<?> c: classes) {
 193             String name = "T" + c.getSimpleName();
 194             String src =
 195                     "class T" + name + "{" + "\n" +
 196                     "    Class<?> c = " + c.getName() + ".class;\n" +
 197                     "}";
 198             srcs.add(new StringJavaFileObject(name + ".java", src));
 199         }
 200         testClasses.put(p, srcs);
 201     }
 202 
 203     void checkDiags(String msg, List<Diagnostic<? extends JavaFileObject>> diags, List<String> expectDiagCodes) {
 204         System.err.print(msg);
 205         if (diags.isEmpty())
 206             System.err.println(" OK");
 207         else {
 208             System.err.println();
 209             System.err.println(diags);
 210         }
 211 
 212         List<String> foundDiagCodes = new ArrayList<String>();
 213         for (Diagnostic<? extends JavaFileObject> d: diags)
 214             foundDiagCodes.add(d.getCode());
 215 
 216         if (!foundDiagCodes.equals(expectDiagCodes)) {
 217             System.err.println("Found diag codes:    " + foundDiagCodes);
 218             System.err.println("Expected diag codes: " + expectDiagCodes);
 219             error("expected diagnostics not found");
 220         }
 221     }
 222 
 223     /** Marker annotation for test cases. */
 224     @Retention(RetentionPolicy.RUNTIME)
 225     @interface Test { }
 226 
 227     /** Run all test cases. */
 228     void run() throws Exception {
 229         initTestClasses();
 230 
 231         for (Method m: getClass().getDeclaredMethods()) {
 232             Annotation a = m.getAnnotation(Test.class);
 233             if (a != null) {
 234                 System.err.println(m.getName());
 235                 try {
 236                     m.invoke(this, new Object[] { });
 237                 } catch (InvocationTargetException e) {
 238                     Throwable cause = e.getCause();
 239                     throw (cause instanceof Exception) ? ((Exception) cause) : e;
 240                 }
 241                 System.err.println();
 242             }
 243         }
 244 
 245         if (errors > 0)
 246             throw new Exception(errors + " errors occurred");
 247     }
 248 
 249     void error(String msg) {
 250         System.err.println("Error: " + msg);
 251         errors++;
 252     }
 253 
 254     int errors;
 255 
 256     private static class StringJavaFileObject extends SimpleJavaFileObject {
 257         StringJavaFileObject(String name, String text) {
 258             super(URI.create(name), JavaFileObject.Kind.SOURCE);
 259             this.text = text;
 260         }
 261         @Override
 262         public CharSequence getCharContent(boolean b) {
 263             return text;
 264         }
 265         private String text;
 266     }
 267 }