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