1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/processing/T6920317.java Fri Jan 29 16:54:52 2010 -0800 1.3 @@ -0,0 +1,462 @@ 1.4 +/* 1.5 + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.24 + * have any questions. 1.25 + */ 1.26 + 1.27 +/* 1.28 + * @test 1.29 + * @bug 6920317 1.30 + * @summary package-info.java file has to be specified on the javac cmdline, else it will not be avail 1.31 + */ 1.32 + 1.33 +import java.io.*; 1.34 +import java.util.*; 1.35 +import javax.annotation.processing.*; 1.36 +import javax.lang.model.*; 1.37 +import javax.lang.model.element.*; 1.38 +import javax.lang.model.util.*; 1.39 +import javax.tools.*; 1.40 + 1.41 +/** 1.42 + * The test exercises different ways of providing annotations for a package. 1.43 + * Each way provides an annotation with a unique argument. For each test 1.44 + * case, the test verifies that the annotation with the correct argument is 1.45 + * found by the compiler. 1.46 + */ 1.47 +public class T6920317 { 1.48 + public static void main(String... args) throws Exception { 1.49 + new T6920317().run(args); 1.50 + } 1.51 + 1.52 + // Used to describe properties of files to be put on command line, source path, class path 1.53 + enum Kind { 1.54 + /** File is not used. */ 1.55 + NONE, 1.56 + /** File is used. */ 1.57 + OLD, 1.58 + /** Only applies to files on classpath/sourcepath, when there is another file on the 1.59 + * other path of type OLD, in which case, this file must be newer than the other one. */ 1.60 + NEW, 1.61 + /** Only applies to files on classpath/sourcepath, when there is no file in any other 1.62 + * location, in which case, this file will be generated by the annotation processor. */ 1.63 + GEN 1.64 + } 1.65 + 1.66 + void run(String... args) throws Exception { 1.67 + // if no args given, all test cases are run 1.68 + // if args given, they indicate the test cases to be run 1.69 + for (int i = 0; i < args.length; i++) { 1.70 + tests.add(Integer.valueOf(args[i])); 1.71 + } 1.72 + 1.73 + setup(); 1.74 + 1.75 + // Run tests for all combinations of files on command line, source path and class path. 1.76 + // Invalid combinations are skipped in the test method 1.77 + for (Kind cmdLine: EnumSet.of(Kind.NONE, Kind.OLD)) { 1.78 + for (Kind srcPath: Kind.values()) { 1.79 + for (Kind clsPath: Kind.values()) { 1.80 + try { 1.81 + test(cmdLine, srcPath, clsPath); 1.82 + } catch (Exception e) { 1.83 + e.printStackTrace(); 1.84 + error("Exception " + e); 1.85 + // uncomment to stop on first failed test case 1.86 + // throw e; 1.87 + } 1.88 + } 1.89 + } 1.90 + } 1.91 + 1.92 + if (errors > 0) 1.93 + throw new Exception(errors + " errors occurred"); 1.94 + } 1.95 + 1.96 + /** One time setup for files and directories to be used in the various test cases. */ 1.97 + void setup() throws Exception { 1.98 + // Annotation used in test cases to annotate package. This file is 1.99 + // given on the command line in test cases. 1.100 + test_java = writeFile("Test.java", "package p; @interface Test { String value(); }"); 1.101 + // Compile the annotation for use later in setup 1.102 + File tmpClasses = new File("tmp.classes"); 1.103 + compile(tmpClasses, new String[] { }, test_java); 1.104 + 1.105 + // package-info file to use on the command line when requied 1.106 + cl_pkgInfo_java = writeFile("cl/p/package-info.java", "@Test(\"CL\") package p;"); 1.107 + 1.108 + // source path containing package-info 1.109 + sp_old = new File("src.old"); 1.110 + writeFile("src.old/p/package-info.java", "@Test(\"SP_OLD\") package p;"); 1.111 + 1.112 + // class path containing package-info 1.113 + cp_old = new File("classes.old"); 1.114 + compile(cp_old, new String[] { "-classpath", tmpClasses.getPath() }, 1.115 + writeFile("tmp.old/p/package-info.java", "@Test(\"CP_OLD\") package p;")); 1.116 + 1.117 + // source path containing package-info which is newer than the one in cp-old 1.118 + sp_new = new File("src.new"); 1.119 + File old_class = new File(cp_old, "p/package-info.class"); 1.120 + writeFile("src.new/p/package-info.java", "@Test(\"SP_NEW\") package p;", old_class); 1.121 + 1.122 + // class path containing package-info which is newer than the one in sp-old 1.123 + cp_new = new File("classes.new"); 1.124 + File old_java = new File(sp_old, "p/package-info.java"); 1.125 + compile(cp_new, new String[] { "-classpath", tmpClasses.getPath() }, 1.126 + writeFile("tmp.new/p/package-info.java", "@Test(\"CP_NEW\") package p;", old_java)); 1.127 + 1.128 + // directory containing package-info.java to be "generated" later by annotation processor 1.129 + sp_gen = new File("src.gen"); 1.130 + writeFile("src.gen/p/package-info.java", "@Test(\"SP_GEN\") package p;"); 1.131 + 1.132 + // directory containing package-info.class to be "generated" later by annotation processor 1.133 + cp_gen = new File("classes.gen"); 1.134 + compile(cp_gen, new String[] { "-classpath", tmpClasses.getPath() }, 1.135 + writeFile("tmp.gen/p/package-info.java", "@Test(\"CP_GEN\") package p;")); 1.136 + } 1.137 + 1.138 + void test(Kind cl, Kind sp, Kind cp) throws Exception { 1.139 + if (skip(cl, sp, cp)) 1.140 + return; 1.141 + 1.142 + ++count; 1.143 + // if test cases specified, skip this test case if not selected 1.144 + if (tests.size() > 0 && !tests.contains(count)) 1.145 + return; 1.146 + 1.147 + System.err.println("Test " + count + " cl:" + cl + " sp:" + sp + " cp:" + cp); 1.148 + 1.149 + // test specific tmp directory 1.150 + File test_tmp = new File("tmp.test" + count); 1.151 + test_tmp.mkdirs(); 1.152 + 1.153 + // build up list of options and files to be compiled 1.154 + List<String> opts = new ArrayList<String>(); 1.155 + List<File> files = new ArrayList<File>(); 1.156 + 1.157 + // expected value for annotation 1.158 + String expect = null; 1.159 + 1.160 + opts.add("-processorpath"); 1.161 + opts.add(System.getProperty("test.classes")); 1.162 + opts.add("-processor"); 1.163 + opts.add(Processor.class.getName()); 1.164 + opts.add("-proc:only"); 1.165 + opts.add("-d"); 1.166 + opts.add(test_tmp.getPath()); 1.167 + //opts.add("-verbose"); 1.168 + files.add(test_java); 1.169 + 1.170 + /* 1.171 + * Analyze each of cl, cp, sp, building up the options and files to 1.172 + * be compiled, and determining the expected outcome fo the test case. 1.173 + */ 1.174 + 1.175 + // command line file: either omitted or given 1.176 + if (cl == Kind.OLD) { 1.177 + files.add(cl_pkgInfo_java); 1.178 + // command line files always supercede files on paths 1.179 + expect = "CL"; 1.180 + } 1.181 + 1.182 + // source path: 1.183 + switch (sp) { 1.184 + case NONE: 1.185 + break; 1.186 + 1.187 + case OLD: 1.188 + opts.add("-sourcepath"); 1.189 + opts.add(sp_old.getPath()); 1.190 + if (expect == null && cp == Kind.NONE) { 1.191 + assert cl == Kind.NONE && cp == Kind.NONE; 1.192 + expect = "SP_OLD"; 1.193 + } 1.194 + break; 1.195 + 1.196 + case NEW: 1.197 + opts.add("-sourcepath"); 1.198 + opts.add(sp_new.getPath()); 1.199 + if (expect == null) { 1.200 + assert cl == Kind.NONE && cp == Kind.OLD; 1.201 + expect = "SP_NEW"; 1.202 + } 1.203 + break; 1.204 + 1.205 + case GEN: 1.206 + opts.add("-Agen=" + new File(sp_gen, "p/package-info.java")); 1.207 + assert cl == Kind.NONE && cp == Kind.NONE; 1.208 + expect = "SP_GEN"; 1.209 + break; 1.210 + } 1.211 + 1.212 + // class path: 1.213 + switch (cp) { 1.214 + case NONE: 1.215 + break; 1.216 + 1.217 + case OLD: 1.218 + opts.add("-classpath"); 1.219 + opts.add(cp_old.getPath()); 1.220 + if (expect == null && sp == Kind.NONE) { 1.221 + assert cl == Kind.NONE && sp == Kind.NONE; 1.222 + expect = "CP_OLD"; 1.223 + } 1.224 + break; 1.225 + 1.226 + case NEW: 1.227 + opts.add("-classpath"); 1.228 + opts.add(cp_new.getPath()); 1.229 + if (expect == null) { 1.230 + assert cl == Kind.NONE && sp == Kind.OLD; 1.231 + expect = "CP_NEW"; 1.232 + } 1.233 + break; 1.234 + 1.235 + case GEN: 1.236 + opts.add("-Agen=" + new File(cp_gen, "p/package-info.class")); 1.237 + assert cl == Kind.NONE && sp == Kind.NONE; 1.238 + expect = "CP_GEN"; 1.239 + break; 1.240 + } 1.241 + 1.242 + // pass expected value to annotation processor 1.243 + assert expect != null; 1.244 + opts.add("-Aexpect=" + expect); 1.245 + 1.246 + // compile the files with the options that have been built up 1.247 + compile(opts, files); 1.248 + } 1.249 + 1.250 + /** 1.251 + * Return true if this combination of parameters does not identify a useful test case. 1.252 + */ 1.253 + boolean skip(Kind cl, Kind sp, Kind cp) { 1.254 + // skip if no package files required 1.255 + if (cl == Kind.NONE && sp == Kind.NONE && cp == Kind.NONE) 1.256 + return true; 1.257 + 1.258 + // skip if both sp and sp are OLD, since results may be indeterminate 1.259 + if (sp == Kind.OLD && cp == Kind.OLD) 1.260 + return true; 1.261 + 1.262 + // skip if sp or cp is NEW but the other is not OLD 1.263 + if ((sp == Kind.NEW && cp != Kind.OLD) || (cp == Kind.NEW && sp != Kind.OLD)) 1.264 + return true; 1.265 + 1.266 + // only use GEN if no other package-info files present 1.267 + if (sp == Kind.GEN && !(cl == Kind.NONE && cp == Kind.NONE) || 1.268 + cp == Kind.GEN && !(cl == Kind.NONE && sp == Kind.NONE)) { 1.269 + return true; 1.270 + } 1.271 + 1.272 + // remaining combinations are valid 1.273 + return false; 1.274 + } 1.275 + 1.276 + /** Write a file with a given body. */ 1.277 + File writeFile(String path, String body) throws Exception { 1.278 + File f = new File(path); 1.279 + if (f.getParentFile() != null) 1.280 + f.getParentFile().mkdirs(); 1.281 + Writer out = new FileWriter(path); 1.282 + try { 1.283 + out.write(body); 1.284 + } finally { 1.285 + out.close(); 1.286 + } 1.287 + return f; 1.288 + } 1.289 + 1.290 + /** Write a file with a given body, ensuring that the file is newer than a reference file. */ 1.291 + File writeFile(String path, String body, File ref) throws Exception { 1.292 + for (int i = 0; i < 5; i++) { 1.293 + File f = writeFile(path, body); 1.294 + if (f.lastModified() > ref.lastModified()) 1.295 + return f; 1.296 + Thread.sleep(2000); 1.297 + } 1.298 + throw new Exception("cannot create file " + path + " newer than " + ref); 1.299 + } 1.300 + 1.301 + /** Compile a file to a given directory, with options provided. */ 1.302 + void compile(File dir, String[] opts, File src) throws Exception { 1.303 + dir.mkdirs(); 1.304 + List<String> opts2 = new ArrayList<String>(); 1.305 + opts2.addAll(Arrays.asList("-d", dir.getPath())); 1.306 + opts2.addAll(Arrays.asList(opts)); 1.307 + compile(opts2, Collections.singletonList(src)); 1.308 + } 1.309 + 1.310 + /** Compile files with options provided. */ 1.311 + void compile(List<String> opts, List<File> files) throws Exception { 1.312 + System.err.println("javac: " + opts + " " + files); 1.313 + List<String> args = new ArrayList<String>(); 1.314 + args.addAll(opts); 1.315 + for (File f: files) 1.316 + args.add(f.getPath()); 1.317 + StringWriter sw = new StringWriter(); 1.318 + PrintWriter pw = new PrintWriter(sw); 1.319 + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw); 1.320 + pw.flush(); 1.321 + if (sw.getBuffer().length() > 0) 1.322 + System.err.println(sw.toString()); 1.323 + if (rc != 0) 1.324 + throw new Exception("compilation failed: rc=" + rc); 1.325 + } 1.326 + 1.327 + /** Report an error. */ 1.328 + void error(String msg) { 1.329 + System.err.println("Error: " + msg); 1.330 + errors++; 1.331 + } 1.332 + 1.333 + /** Test case counter. */ 1.334 + int count; 1.335 + 1.336 + /** Number of errors found. */ 1.337 + int errors; 1.338 + 1.339 + /** Optional set of test cases to be run; empty implies all test cases. */ 1.340 + Set<Integer> tests = new HashSet<Integer>(); 1.341 + 1.342 + /* Files created by setup. */ 1.343 + File test_java; 1.344 + File sp_old; 1.345 + File sp_new; 1.346 + File sp_gen; 1.347 + File cp_old; 1.348 + File cp_new; 1.349 + File cp_gen; 1.350 + File cl_pkgInfo_java; 1.351 + 1.352 + /** Annotation processor used to verify the expected value for the 1.353 + package annotations found by javac. */ 1.354 + @SupportedOptions({ "gen", "expect" }) 1.355 + @SupportedAnnotationTypes({"*"}) 1.356 + public static class Processor extends AbstractProcessor { 1.357 + public SourceVersion getSupportedSourceVersion() { 1.358 + return SourceVersion.latest(); 1.359 + } 1.360 + 1.361 + public boolean process(Set<? extends TypeElement> annots, RoundEnvironment renv) { 1.362 + round++; 1.363 + System.err.println("Round " + round + " annots:" + annots + " rootElems:" + renv.getRootElements()); 1.364 + 1.365 + // if this is the first round and the gen option is given, use the filer to create 1.366 + // a copy of the file specified by the gen option. 1.367 + String gen = getOption("gen"); 1.368 + if (round == 1 && gen != null) { 1.369 + try { 1.370 + Filer filer = processingEnv.getFiler(); 1.371 + JavaFileObject f; 1.372 + if (gen.endsWith(".java")) 1.373 + f = filer.createSourceFile("p.package-info"); 1.374 + else 1.375 + f = filer.createClassFile("p.package-info"); 1.376 + System.err.println("copy " + gen + " to " + f.getName()); 1.377 + write(f, read(new File(gen))); 1.378 + } catch (IOException e) { 1.379 + error("Cannot create package-info file: " + e); 1.380 + } 1.381 + } 1.382 + 1.383 + // if annotation processing is complete, verify the package annotation 1.384 + // found by the compiler. 1.385 + if (renv.processingOver()) { 1.386 + System.err.println("final round"); 1.387 + Elements eu = processingEnv.getElementUtils(); 1.388 + TypeElement te = eu.getTypeElement("p.Test"); 1.389 + PackageElement pe = eu.getPackageOf(te); 1.390 + System.err.println("final: te:" + te + " pe:" + pe); 1.391 + List<? extends AnnotationMirror> annos = pe.getAnnotationMirrors(); 1.392 + System.err.println("final: annos:" + annos); 1.393 + if (annos.size() == 1) { 1.394 + String expect = "@" + te + "(\"" + getOption("expect") + "\")"; 1.395 + String actual = annos.get(0).toString(); 1.396 + checkEqual("package annotations", actual, expect); 1.397 + } else { 1.398 + error("Wrong number of annotations found: (" + annos.size() + ") " + annos); 1.399 + } 1.400 + } 1.401 + 1.402 + return true; 1.403 + } 1.404 + 1.405 + /** Get an option given to the annotation processor. */ 1.406 + String getOption(String name) { 1.407 + return processingEnv.getOptions().get(name); 1.408 + } 1.409 + 1.410 + /** Read a file. */ 1.411 + byte[] read(File file) { 1.412 + byte[] bytes = new byte[(int) file.length()]; 1.413 + DataInputStream in = null; 1.414 + try { 1.415 + in = new DataInputStream(new FileInputStream(file)); 1.416 + in.readFully(bytes); 1.417 + } catch (IOException e) { 1.418 + error("Error reading file: " + e); 1.419 + } finally { 1.420 + if (in != null) { 1.421 + try { 1.422 + in.close(); 1.423 + } catch (IOException e) { 1.424 + error("Error closing file: " + e); 1.425 + } 1.426 + } 1.427 + } 1.428 + return bytes; 1.429 + } 1.430 + 1.431 + /** Write a file. */ 1.432 + void write(JavaFileObject file, byte[] bytes) { 1.433 + OutputStream out = null; 1.434 + try { 1.435 + out = file.openOutputStream(); 1.436 + out.write(bytes, 0, bytes.length); 1.437 + } catch (IOException e) { 1.438 + error("Error writing file: " + e); 1.439 + } finally { 1.440 + if (out != null) { 1.441 + try { 1.442 + out.close(); 1.443 + } catch (IOException e) { 1.444 + error("Error closing file: " + e); 1.445 + } 1.446 + } 1.447 + } 1.448 + } 1.449 + 1.450 + /** Check two strings are equal, and report an error if they are not. */ 1.451 + private void checkEqual(String label, String actual, String expect) { 1.452 + if (!actual.equals(expect)) { 1.453 + error("Unexpected value for " + label + "; actual=" + actual + ", expected=" + expect); 1.454 + } 1.455 + } 1.456 + 1.457 + /** Report an error to the annotation processing system. */ 1.458 + void error(String msg) { 1.459 + Messager messager = processingEnv.getMessager(); 1.460 + messager.printMessage(Diagnostic.Kind.ERROR, msg); 1.461 + } 1.462 + 1.463 + int round; 1.464 + } 1.465 +}