Mon, 18 Feb 2013 14:29:40 -0800
8008339: Test TargetAnnoCombo.java is broken
Reviewed-by: jjh
1 /*
2 * Copyright (c) 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 */
24 /**
25 * @test
26 * @bug 7195131
27 * @author sogoel
28 * @summary Combo test for all possible combinations for Target values
29 * @ignore 8008339 Test TargetAnnoCombo.java is broken
30 * @build Helper
31 * @compile TargetAnnoCombo.java TestCaseGenerator.java
32 * @run main TargetAnnoCombo
33 */
35 import java.io.IOException;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.HashSet;
39 import java.util.Set;
40 import javax.tools.Diagnostic;
41 import javax.tools.DiagnosticCollector;
42 import javax.tools.JavaFileObject;
44 /*
45 * TargetAnnoCombo gets a list of test case numbers using TestCaseGenerator.
46 * For each of the test case number, @Target sets for base and container annotations
47 * are determined, source files are generated, compiled, and the result is verified
48 * based on if the @Target set for base and container is a positive or negative combination.
49 *
50 * @Target sets for base and container annotations are determined using a bit mapping of
51 * 10 ElementType enum constants defined in JDK8.
52 *
53 * Bit Target value
54 * 0 "ElementType.ANNOTATION_TYPE"
55 * 1 "ElementType.CONSTRUCTOR"
56 * 2 "ElementType.FIELD"
57 * 3 "ElementType.LOCAL_VARIABLE"
58 * 4 "ElementType.METHOD"
59 * 5 "ElementType.TYPE"
60 * 6 "ElementType.PARAMETER"
61 * 7 "ElementType.PACKAGE"
62 * 8 "ElementType.TYPE_USE"
63 * 9 "ElementType.TYPE_PARAMETER"
64 *
65 * Group 1:
66 * 20 bits mapping, representing a test case number, is used for all target set
67 * combinations ( 0 to 1048575 ) including empty @Target sets => @Target({}).
68 * From this 20 bits, 10 bits are for base followed by 10 bits for container
69 * where each bit maps to an ElementType enum constant defined in JDK8.
70 *
71 * Examples:
72 * Test case number: 4, binary: 100 => container=100, base=[], container=["ElementType.FIELD"]
73 * Test case number: 1003575, binary: 11110101000000110111 => base=1111010100, container=0000110111;
74 * base=["ElementType.PARAMETER", "ElementType.TYPE_USE", "ElementType.METHOD", "ElementType.FIELD", "ElementType.PACKAGE", "ElementType.TYPE_PARAMETER"],
75 * container=["ElementType.TYPE", "ElementType.METHOD", "ElementType.ANNOTATION_TYPE", "ElementType.CONSTRUCTOR", "ElementType.FIELD"]
76 *
77 * In the following groups, no @Target set is represented by null.
78 * Group 2:
79 * @Target is not defined on base.
80 * Target sets for container are determined using the 10-bit binary number
81 * resulting in 1024 test cases, mapping them to test case numbers from
82 * 1048576 to (1048576 + 1023) => 1048576 to 1049599.
83 *
84 * Example:
85 * Test case number: 1048587 => 1048587 - 1048576 = test case 11 in Group 2, binary: 1011 =>
86 * base = null,
87 * container = ["ElementType.ANNOTATION_TYPE","ElementType.CONSTRUCTOR","ElementType.LOCAL_VARIABLE"]
88 *
89 * Group 3:
90 * @Target is not defined on container
91 * Target sets for base are determined using the 10-bit binary number
92 * resulting in 1024 test cases, mapping them to test case numbers from
93 * 1049600 to (1049600 + 1023) => 1049600 to 1050623.
94 *
95 * Example:
96 * Test case number: 1049708 => 1049708 - 1049600 = test case 108 in Group 3, binary: 1101100 =>
97 * base = ["ElementType.FIELD", "ElementType.LOCAL_VARIABLE", "ElementType.TYPE", "ElementType.PARAMETER"],
98 * container = null
99 *
100 * For the above group, test case number: 1049855 gives compiler error, JDK-8006547 filed
101 *
102 * Group 4:
103 * @Target not defined for both base and container annotations.
104 *
105 * This is the last test and corresponds to test case number 1050624. base=null, container=null
106 *
107 * Examples to run this test:
108 * 1. Run a specific test case number:
109 * ${JTREG} -DTestCaseNum=10782 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java
110 * 2. Run specific number of tests:
111 * ${JTREG} -DNumberOfTests=4 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java
112 * 3. Run specific number of tests with a seed:
113 * ${JTREG} -DNumberOfTests=4 -DTestSeed=-972894659 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java
114 * 4. Run tests in default mode (number of tests = 1000):
115 * ${JTREG} -DTestMode=DEFAULT -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java
116 * 5. Run all tests (FULL mode):
117 * ${JTREG} -DTestMode=FULL -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java
118 *
119 */
121 public class TargetAnnoCombo {
122 int errors = 0;
123 static final String TESTPKG = "testpkg";
124 /*
125 * Set it to true to get more debug information including base and
126 * container target sets for a given test case number
127 */
128 static final boolean DEBUG = false;
130 // JDK 5/6/7/8 Targets
131 static final String[] targetVals = {"ElementType.ANNOTATION_TYPE",
132 "ElementType.CONSTRUCTOR", "ElementType.FIELD",
133 "ElementType.LOCAL_VARIABLE", "ElementType.METHOD",
134 "ElementType.TYPE", "ElementType.PARAMETER",
135 "ElementType.PACKAGE", "ElementType.TYPE_USE",
136 "ElementType.TYPE_PARAMETER"};
138 // TYPE_USE and TYPE_PARAMETER (added in JDK8) are not part of default Target set
139 static final int DEFAULT_TARGET_CNT = 8;
141 public static void main(String args[]) throws Exception {
143 /* maxTestNum = (base and container combinations of targetVals elems [0 - 1048575 combos])
144 * + (combinations where base or container has no Target [1024 combos])
145 * + (no -1 even though 1st test is number 0 as last test is where both
146 * base and container have no target)
147 */
149 int maxTestNum = (int)Math.pow(2, 2*targetVals.length) + 2*(int)Math.pow(2, targetVals.length);
150 TestCaseGenerator tcg = new TestCaseGenerator(maxTestNum);
151 TargetAnnoCombo tac = new TargetAnnoCombo();
153 int testCtr = 0;
154 int testCase = -1;
155 while ( (testCase=tcg.getNextTestCase()) != -1 ) {
156 tac.executeTestCase(testCase, maxTestNum);
157 testCtr++;
158 }
160 System.out.println("Total tests run: " + testCtr);
161 if (tac.errors > 0)
162 throw new Exception(tac.errors + " errors found");
163 }
165 /*
166 * For given testCase, determine the base and container annotation Target sets,
167 * get if testCase should compile, get test source file(s), get compilation result and verify.
168 *
169 */
170 private void executeTestCase(int testCase, int maxTestNum) {
172 // Determine base and container annotation Target sets for the testCase
173 Set<String> baseAnnoTarget = null;
174 Set<String> conAnnoTarget = null;
176 //Number of base and container combinations [0 - 1048575 combos]
177 int baseContCombos = (int)Math.pow(2, 2*targetVals.length);
178 //Number of either base or container combinations when one of them has no @Target [1024 combos]
179 int targetValsCombos = (int)Math.pow(2, targetVals.length);
181 if (testCase >= baseContCombos) {
182 //Base annotation do not have @Target
183 if (testCase < baseContCombos + targetValsCombos) {
184 baseAnnoTarget = null;
185 conAnnoTarget = getSetFromBitVec(Integer.toBinaryString(testCase - baseContCombos));
186 } else if (testCase < baseContCombos + 2*targetValsCombos) {
187 //Container annotation do not have @Target
188 baseAnnoTarget = getSetFromBitVec(Integer.toBinaryString(testCase - baseContCombos - targetValsCombos));
189 conAnnoTarget = null;
190 } else {
191 //Both Base and Container annotation do not have @Target
192 baseAnnoTarget = null;
193 conAnnoTarget = null;
194 }
195 } else {
196 //TestCase number is represented as 10-bits for base followed by container bits
197 String bin = Integer.toBinaryString(testCase);
198 String base="", cont=bin;
199 if (bin.length() > targetVals.length){
200 base = bin.substring(0, bin.length() - targetVals.length);
201 cont = bin.substring(bin.length() - targetVals.length,bin.length());
202 }
203 baseAnnoTarget = getSetFromBitVec(base);
204 conAnnoTarget = getSetFromBitVec(cont);
205 }
207 debugPrint("Test case number = " + testCase + " => binary = " + Integer.toBinaryString(testCase));
208 debugPrint(" => baseAnnoTarget = " + baseAnnoTarget);
209 debugPrint(" => containerAnnoTarget = " + conAnnoTarget);
211 // Determine if a testCase should compile or not
212 String className = "TC" + testCase;
213 boolean shouldCompile = isValidSubSet(baseAnnoTarget, conAnnoTarget);
215 // Get test source file(s)
216 Iterable<? extends JavaFileObject> files = getFileList(className, baseAnnoTarget,
217 conAnnoTarget, shouldCompile);
219 // Get result of compiling test src file(s)
220 boolean result = getCompileResult(className, shouldCompile, files);
222 // List test src code if test fails
223 if(!result) {
224 System.out.println("FAIL: Test " + testCase);
225 try {
226 for (JavaFileObject f: files) {
227 System.out.println("File: " + f.getName() + "\n" + f.getCharContent(true));
228 }
229 } catch (IOException ioe) {
230 System.out.println("Exception: " + ioe);
231 }
232 } else {
233 debugPrint("PASS: Test " + testCase);
234 }
235 }
237 // Get a Set<String> based on bits that are set to 1
238 public Set<String> getSetFromBitVec(String bitVec) {
239 Set<String> ret = new HashSet<>();
240 char[] bit = bitVec.toCharArray();
241 for (int i=bit.length-1, j=0; i>=0; i--, j++){
242 if (bit[i] == '1') {
243 ret.add(targetVals[j]);
244 }
245 }
246 return ret;
247 }
249 // Compile the test source file(s) and return test result
250 private boolean getCompileResult(String className, boolean shouldCompile,
251 Iterable<? extends JavaFileObject> files) {
253 DiagnosticCollector<JavaFileObject> diagnostics =
254 new DiagnosticCollector<JavaFileObject>();
255 Helper.compileCode(diagnostics, files);
257 // Test case pass or fail
258 boolean ok = false;
260 String errMesg = "";
261 int numDiags = diagnostics.getDiagnostics().size();
263 if (numDiags == 0) {
264 if (shouldCompile) {
265 debugPrint("Test passed, compiled as expected.");
266 ok = true;
267 } else {
268 errMesg = "Test failed, compiled unexpectedly.";
269 ok = false;
270 }
271 } else {
272 if (shouldCompile) {
273 // did not compile
274 errMesg = "Test failed, did not compile.";
275 ok = false;
276 } else {
277 // Error in compilation as expected
278 String expectedErrKey = "compiler.err.invalid.repeatable." +
279 "annotation.incompatible.target";
280 for (Diagnostic<?> d : diagnostics.getDiagnostics()) {
281 if((d.getKind() == Diagnostic.Kind.ERROR) &&
282 d.getCode().contains(expectedErrKey)) {
283 // Error message as expected
284 debugPrint("Error message as expected.");
285 ok = true;
286 break;
287 } else {
288 // error message is incorrect
289 ok = false;
290 }
291 }
292 if (!ok) {
293 errMesg = "Incorrect error received when compiling " +
294 className + ", expected: " + expectedErrKey;
295 }
296 }
297 }
299 if(!ok) {
300 error(errMesg);
301 for (Diagnostic<?> d : diagnostics.getDiagnostics())
302 System.out.println(" Diags: " + d);
303 }
304 return ok;
305 }
307 private void debugPrint(String string) {
308 if(DEBUG)
309 System.out.println(string);
310 }
312 // Create src code and corresponding JavaFileObjects
313 private Iterable<? extends JavaFileObject> getFileList(String className,
314 Set<String> baseAnnoTarget, Set<String> conAnnoTarget,
315 boolean shouldCompile) {
317 String srcContent = "";
318 String pkgInfoContent = "";
319 String template = Helper.template;
320 String baseTarget = "", conTarget = "";
322 String target = Helper.ContentVars.TARGET.getVal();
323 if(baseAnnoTarget != null) {
324 baseTarget = target.replace("#VAL", baseAnnoTarget.toString())
325 .replace("[", "{").replace("]", "}");
326 }
327 if(conAnnoTarget != null) {
328 conTarget = target.replace("#VAL", conAnnoTarget.toString())
329 .replace("[", "{").replace("]", "}");
330 }
332 String annoData = Helper.ContentVars.IMPORTSTMTS.getVal() +
333 conTarget +
334 Helper.ContentVars.CONTAINER.getVal() +
335 baseTarget +
336 Helper.ContentVars.REPEATABLE.getVal() +
337 Helper.ContentVars.BASE.getVal();
339 JavaFileObject pkgInfoFile = null;
341 /*
342 * If shouldCompile = true and no @Target is specified for container annotation,
343 * then all 8 ElementType enum constants are applicable as targets for
344 * container annotation.
345 */
346 if(shouldCompile && conAnnoTarget == null) {
347 //conAnnoTarget = new HashSet<String>(Arrays.asList(targetVals));
348 conAnnoTarget = getDefaultTargetSet();
349 }
351 if(shouldCompile) {
352 boolean isPkgCasePresent = new ArrayList<String>(conAnnoTarget).contains("ElementType.PACKAGE");
353 String repeatableAnno = Helper.ContentVars.BASEANNO.getVal() + " " + Helper.ContentVars.BASEANNO.getVal();
354 for(String s: conAnnoTarget) {
355 s = s.replace("ElementType.","");
356 String replaceStr = "/*"+s+"*/";
357 if(s.equalsIgnoreCase("PACKAGE")) {
358 //Create packageInfo file
359 String pkgInfoName = TESTPKG + "." + "package-info";
360 pkgInfoContent = repeatableAnno + "\npackage " + TESTPKG + ";" + annoData;
361 pkgInfoFile = Helper.getFile(pkgInfoName, pkgInfoContent);
362 } else {
363 template = template.replace(replaceStr, repeatableAnno);
364 //srcContent = template.replace("#ClassName",className);
365 if(!isPkgCasePresent) {
366 srcContent = template.replace("/*ANNODATA*/", annoData).replace("#ClassName",className);
367 } else {
368 replaceStr = "/*PACKAGE*/";
369 srcContent = template.replace(replaceStr, "package " + TESTPKG + ";")
370 .replace("#ClassName", className);
371 }
372 }
373 }
374 } else {
375 // For invalid cases, compilation should fail at declaration site
376 template = "class #ClassName {}";
377 srcContent = annoData + template.replace("#ClassName",className);
378 }
379 JavaFileObject srcFile = Helper.getFile(className, srcContent);
380 Iterable<? extends JavaFileObject> files = null;
381 if(pkgInfoFile != null)
382 files = Arrays.asList(pkgInfoFile,srcFile);
383 else
384 files = Arrays.asList(srcFile);
385 return files;
386 }
388 private Set<String> getDefaultTargetSet() {
389 Set<String> defaultSet = new HashSet<>();
390 int ctr = 0;
391 for(String s : targetVals) {
392 if(ctr++ < DEFAULT_TARGET_CNT) {
393 defaultSet.add(s);
394 }
395 }
396 return defaultSet;
397 }
399 private boolean isValidSubSet(Set<String> baseAnnoTarget, Set<String> conAnnoTarget) {
400 /*
401 * RULE 1: conAnnoTarget should be a subset of baseAnnoTarget
402 * RULE 2: For empty @Target ({}) - annotation cannot be applied anywhere
403 * - Empty sets for both is valid
404 * - Empty baseTarget set is invalid with non-empty conTarget set
405 * - Non-empty baseTarget set is valid with empty conTarget set
406 * RULE 3: For no @Target specified - annotation can be applied to any JDK 7 targets
407 * - No @Target for both is valid
408 * - No @Target for baseTarget set with @Target conTarget set is valid
409 * - @Target for baseTarget set with no @Target for conTarget is invalid
410 */
413 /* If baseAnno has no @Target, Foo can be either applied to @Target specified for container annotation
414 * else will be applicable for all default targets if no @Target is present for container annotation.
415 * In both cases, the set will be a valid set with no @Target for base annotation
416 */
417 if(baseAnnoTarget == null) {
418 if(conAnnoTarget == null) return true;
419 return !(conAnnoTarget.contains("ElementType.TYPE_USE") || conAnnoTarget.contains("ElementType.TYPE_PARAMETER"));
420 }
422 Set<String> tempBaseSet = new HashSet<>(baseAnnoTarget);
423 // If BaseAnno has TYPE, then ANNOTATION_TYPE is allowed by default
424 if(baseAnnoTarget.contains("ElementType.TYPE")) {
425 tempBaseSet.add("ElementType.ANNOTATION_TYPE");
426 }
428 /*
429 * If containerAnno has no @Target, only valid case if baseAnnoTarget has all targets defined
430 * else invalid set
431 */
432 if(conAnnoTarget == null) {
433 return (tempBaseSet.containsAll(getDefaultTargetSet()));
434 }
436 // At this point, neither conAnnoTarget or baseAnnoTarget are null
437 if(conAnnoTarget.size() == 0) return true;
439 // At this point, conAnnoTarget is non-empty
440 if (baseAnnoTarget.size() == 0) return false;
442 // At this point, neither conAnnoTarget or baseAnnoTarget are empty
443 return tempBaseSet.containsAll(conAnnoTarget);
444 }
446 void error(String msg) {
447 System.out.println("ERROR: " + msg);
448 errors++;
449 }
451 // Lists the start and end range for the given set of target vals
452 void showGroups() {
453 //Group 1: All target set combinations ( 0 to 1048575 ) including empty @Target sets => @Target({})
454 int grpEnd1 = (int)Math.pow(2, 2*targetVals.length) - 1;
455 System.out.println("[Group 1]: 0 - " + grpEnd1);
457 //Group 2: @Target not defined for base annotation ( 1048576 - 1049599 ).
458 System.out.print("[Group 2]: " + (grpEnd1 + 1) + " - ");
459 int grpEnd2 = grpEnd1 + 1 + (int)Math.pow(2, targetVals.length) - 1;
460 System.out.println(grpEnd2);
462 //Group 3: @Target not defined for container annotation ( 1049600 - 1050623 ).
463 System.out.print("[Group 3]: " + (grpEnd2 + 1) + " - ");
464 int grpEnd3 = grpEnd2 + 1 + (int)Math.pow(2, targetVals.length) - 1;
465 System.out.println(grpEnd3);
467 //Group 4: @Target not defined for both base and container annotations ( 1050624 ).
468 System.out.println("[Group 4]: " + (grpEnd3 + 1));
469 }
470 }