1 /*
2  * Copyright (c) 2013, 2015, 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      7151010 8006547 8007766 8029017
27  * @summary  Default test cases for running combinations for Target values
28  * @modules jdk.compiler
29  * @build    Helper
30  * @run main TargetAnnoCombo
31  */
32 
33 import java.util.Set;
34 import java.util.List;
35 import java.io.IOException;
36 import java.lang.annotation.ElementType;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.EnumSet;
40 import javax.tools.Diagnostic;
41 import javax.tools.DiagnosticCollector;
42 import javax.tools.JavaFileObject;
43 
44 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
45 import static java.lang.annotation.ElementType.CONSTRUCTOR;
46 import static java.lang.annotation.ElementType.FIELD;
47 import static java.lang.annotation.ElementType.METHOD;
48 import static java.lang.annotation.ElementType.PARAMETER;
49 import static java.lang.annotation.ElementType.TYPE;
50 import static java.lang.annotation.ElementType.PACKAGE;
51 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
52 import static java.lang.annotation.ElementType.TYPE_USE;
53 import static java.lang.annotation.ElementType.TYPE_PARAMETER;
54 
55 public class TargetAnnoCombo {
56 
57     static final String TESTPKG = "testpkg";
58 
59     // Set it to true to get more debug information including base and container
60     // target sets for a given test case.
61     static final boolean DEBUG = false;
62 
63     // Define constant target sets to be used for the combination of the target values.
64     final static Set<ElementType> noSet = null;
65     final static Set<ElementType> empty = EnumSet.noneOf(ElementType.class);
66 
67     // [TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,
68     // PACKAGE, TYPE_PARAMETER, TYPE_USE]
69     final static Set<ElementType> allTargets = EnumSet.allOf(ElementType.class);
70 
71     // [TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,
72     // PACKAGE]
73     final static Set<ElementType> jdk7 = EnumSet.range(TYPE, PACKAGE);
74 
75     // [TYPE_USE, TYPE_PARAMETER]
76     final static Set<ElementType> jdk8 = EnumSet.range(TYPE_PARAMETER, TYPE_USE);
77 
78     // List of test cases to run. This list is created in generate().
79     // To run a specific test cases add case number in @run main line.
80     List<TestCase> testCases = new ArrayList<TestCase>();
81 
82     int errors = 0;
83 
84     // Identify test cases that fail.
85     enum IgnoreKind {
86         RUN,
87         IGNORE
88     };
89 
90     private class TestCase {
91 
92         private Set<ElementType> baseAnnotations;
93         private Set<ElementType> containerAnnotations;
94         private IgnoreKind ignore;
95 
TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations)96         public TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations) {
97             this(baseAnnotations, containerAnnotations, IgnoreKind.RUN);
98         }
99 
TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations, IgnoreKind ignoreKind)100         public TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations,
101                         IgnoreKind ignoreKind) {
102             this.baseAnnotations = baseAnnotations;
103             this.containerAnnotations = containerAnnotations;
104             this.ignore = ignoreKind;
105         }
106 
getBaseAnnotations()107         public Set getBaseAnnotations() {
108             return baseAnnotations;
109         }
110 
getContainerAnnotations()111         public Set getContainerAnnotations() {
112             return containerAnnotations;
113         }
114 
isIgnored()115         public boolean isIgnored() {
116             return ignore == IgnoreKind.IGNORE;
117         }
118 
119         // Determine if a testCase should compile or not.
isValidSubSet()120         private boolean isValidSubSet() {
121             /*
122              *  RULE 1: conAnnoTarget should be a subset of baseAnnoTarget
123              *  RULE 2: For empty @Target ({}) - annotation cannot be applied anywhere
124              *         - Empty sets for both is valid
125              *         - Empty baseTarget set is invalid with non-empty conTarget set
126              *         - Non-empty baseTarget set is valid with empty conTarget set
127              *  RULE 3: For no @Target specified - annotation can be applied to any JDK 7 targets
128              *         - No @Target for both is valid
129              *         - No @Target for baseTarget set with @Target conTarget set is valid
130              *         - @Target for baseTarget set with no @Target for conTarget is invalid
131              */
132 
133 
134             /* If baseAnno has no @Target, Foo can be either applied to @Target specified
135              * for container annotation else will be applicable for all default targets
136              * if no @Target is present for container annotation.
137              * In both cases, the set will be a valid set with no @Target for base annotation
138              */
139             if (baseAnnotations == null) {
140                 if (containerAnnotations == null) {
141                     return true;
142                 }
143                 return !(containerAnnotations.contains(TYPE_USE) ||
144                          containerAnnotations.contains(TYPE_PARAMETER));
145             }
146 
147             Set<ElementType> tempBaseSet = EnumSet.noneOf(ElementType.class);
148             tempBaseSet.addAll(baseAnnotations);
149 
150             // If BaseAnno has TYPE, then ANNOTATION_TYPE is allowed by default.
151             if (baseAnnotations.contains(TYPE)) {
152                 tempBaseSet.add(ANNOTATION_TYPE);
153             }
154 
155             // If BaseAnno has TYPE_USE, then add the extra allowed types
156             if (baseAnnotations.contains(TYPE_USE)) {
157                 tempBaseSet.add(ANNOTATION_TYPE);
158                 tempBaseSet.add(TYPE);
159                 tempBaseSet.add(TYPE_PARAMETER);
160             }
161 
162             // If containerAnno has no @Target, only valid case if baseAnnoTarget has
163             // all targets defined else invalid set.
164             if (containerAnnotations == null) {
165                 return tempBaseSet.containsAll(jdk7);
166             }
167 
168             // At this point, neither conAnnoTarget or baseAnnoTarget are null.
169             if (containerAnnotations.isEmpty()) {
170                 return true;
171             }
172 
173             // At this point, conAnnoTarget is non-empty.
174             if (baseAnnotations.isEmpty()) {
175                 return false;
176             }
177 
178             // At this point, neither conAnnoTarget or baseAnnoTarget are empty.
179             return tempBaseSet.containsAll(containerAnnotations);
180         }
181     }
182 
main(String args[])183     public static void main(String args[]) throws Exception {
184         TargetAnnoCombo tac = new TargetAnnoCombo();
185         // Generates all test cases to be run.
186         tac.generate();
187         List<Integer> cases = new ArrayList<Integer>();
188         for (int i = 0; i < args.length; i++) {
189             cases.add(Integer.parseInt(args[i]));
190         }
191         if (cases.isEmpty()) {
192             tac.run();
193         } else {
194             for (int index : cases) {
195                 tac.executeTestCase(tac.testCases.get(index), index);
196             }
197         }
198     }
199 
generate()200     private void generate() {
201         // Adding test cases to run.
202         testCases.addAll(Arrays.asList(
203                 // No base target against no container target.
204                 new TestCase(noSet, noSet),
205                 // No base target against empty container target.
206                 new TestCase(noSet, empty),
207                 // No base target against TYPE_USE only container target.
208                 new TestCase(noSet, less(jdk8, TYPE_PARAMETER)),
209                 // No base target against TYPE_PARAMETER only container target.
210                 new TestCase(noSet, less(jdk8, TYPE_USE)),
211                 // No base target against TYPE_USE + TYPE_PARAMETER only container target.
212                 new TestCase(noSet, jdk8),
213                 // No base target against TYPE_USE + some selection of jdk7 targets.
214                 new TestCase(noSet,
215                 plus(EnumSet.range(TYPE, LOCAL_VARIABLE), TYPE_USE)),
216                 // No base target against TYPE_PARAMETER + some selection of jdk7 targets.
217                 new TestCase(noSet,
218                 plus(EnumSet.range(TYPE, LOCAL_VARIABLE), TYPE_PARAMETER)),
219                 // No base target against each jdk7 target alone as container target.
220                 new TestCase(noSet, plus(empty, TYPE)),
221                 new TestCase(noSet, plus(empty, PARAMETER)),
222                 new TestCase(noSet, plus(empty, PACKAGE)),
223                 new TestCase(noSet, plus(empty, METHOD)),
224                 new TestCase(noSet, plus(empty, LOCAL_VARIABLE)),
225                 new TestCase(noSet, plus(empty, FIELD)),
226                 new TestCase(noSet, plus(empty, CONSTRUCTOR)),
227                 new TestCase(noSet, plus(empty, ANNOTATION_TYPE)),
228                 // Empty base target against no container target.
229                 new TestCase(empty, noSet),
230                 // Empty base target against empty container target.
231                 new TestCase(empty, empty),
232                 // Empty base target against any lone container target.
233                 new TestCase(empty, plus(empty, TYPE)),
234                 new TestCase(empty, plus(empty, PARAMETER)),
235                 new TestCase(empty, plus(empty, PACKAGE)),
236                 new TestCase(empty, plus(empty, METHOD)),
237                 new TestCase(empty, plus(empty, LOCAL_VARIABLE)),
238                 new TestCase(empty, plus(empty, FIELD)),
239                 new TestCase(empty, plus(empty, CONSTRUCTOR)),
240                 new TestCase(empty, plus(empty, ANNOTATION_TYPE)),
241                 new TestCase(empty, less(jdk8, TYPE_USE)),
242                 new TestCase(empty, less(jdk8, TYPE_PARAMETER)),
243                 // No container target against all all-but one jdk7 targets.
244                 new TestCase(less(jdk7, TYPE), noSet),
245                 new TestCase(less(jdk7, PARAMETER), noSet),
246                 new TestCase(less(jdk7, PACKAGE), noSet),
247                 new TestCase(less(jdk7, METHOD), noSet),
248                 new TestCase(less(jdk7, LOCAL_VARIABLE), noSet),
249                 new TestCase(less(jdk7, FIELD), noSet),
250                 new TestCase(less(jdk7, CONSTRUCTOR), noSet),
251                 new TestCase(less(jdk7, ANNOTATION_TYPE), noSet),
252                 // No container against all but TYPE and ANNOTATION_TYPE
253                 new TestCase(less(jdk7, TYPE, ANNOTATION_TYPE), noSet),
254                 // No container against jdk7 targets.
255                 new TestCase(jdk7, noSet),
256                 // No container against jdk7 targets plus one or both of TYPE_USE, TYPE_PARAMETER
257                 new TestCase(plus(jdk7, TYPE_USE), noSet),
258                 new TestCase(plus(jdk7, TYPE_PARAMETER), noSet),
259                 new TestCase(allTargets, noSet),
260                 // Empty container target against any lone target.
261                 new TestCase(plus(empty, TYPE), empty),
262                 new TestCase(plus(empty, PARAMETER), empty),
263                 new TestCase(plus(empty, PACKAGE), empty),
264                 new TestCase(plus(empty, METHOD), empty),
265                 new TestCase(plus(empty, LOCAL_VARIABLE), empty),
266                 new TestCase(plus(empty, FIELD), empty),
267                 new TestCase(plus(empty, CONSTRUCTOR), empty),
268                 new TestCase(plus(empty, ANNOTATION_TYPE), empty),
269                 new TestCase(plus(empty, TYPE_USE), empty),
270                 new TestCase(plus(empty, TYPE_PARAMETER), empty),
271                 // All base targets against all container targets.
272                 new TestCase(allTargets, allTargets),
273                 // All base targets against all but one container targets.
274                 new TestCase(allTargets, less(allTargets, TYPE)),
275                 new TestCase(allTargets, less(allTargets, PARAMETER)),
276                 new TestCase(allTargets, less(allTargets, PACKAGE)),
277                 new TestCase(allTargets, less(allTargets, METHOD)),
278                 new TestCase(allTargets, less(allTargets, LOCAL_VARIABLE)),
279                 new TestCase(allTargets, less(allTargets, FIELD)),
280                 new TestCase(allTargets, less(allTargets, CONSTRUCTOR)),
281                 new TestCase(allTargets, less(allTargets, ANNOTATION_TYPE)),
282                 new TestCase(allTargets, less(allTargets, TYPE_USE)),
283                 new TestCase(allTargets, less(allTargets, TYPE_PARAMETER)),
284                 // All container targets against all but one base targets.
285                 new TestCase(less(allTargets, TYPE), allTargets),
286                 new TestCase(less(allTargets, PARAMETER), allTargets),
287                 new TestCase(less(allTargets, PACKAGE), allTargets),
288                 new TestCase(less(allTargets, METHOD), allTargets),
289                 new TestCase(less(allTargets, LOCAL_VARIABLE), allTargets),
290                 new TestCase(less(allTargets, FIELD), allTargets),
291                 new TestCase(less(allTargets, CONSTRUCTOR), allTargets),
292                 new TestCase(less(allTargets, ANNOTATION_TYPE), allTargets),
293                 new TestCase(less(allTargets, TYPE_USE), allTargets),
294                 new TestCase(less(allTargets, TYPE_PARAMETER), allTargets)));
295         // Generates 100 test cases for any lone base target contained in Set
296         // allTargets against any lone container target.
297         for (ElementType b : allTargets) {
298             for (ElementType c : allTargets) {
299                 testCases.add(new TestCase(plus(empty, b), plus(empty, c)));
300             }
301         }
302     }
303 
run()304     void run() throws Exception {
305         int testCtr = 0;
306         for (TestCase tc : testCases) {
307             if (!tc.isIgnored()) {
308                 executeTestCase(tc, testCases.indexOf(tc));
309                 testCtr++;
310             }
311         }
312         System.out.println("Total tests run: " + testCtr);
313         if (errors > 0) {
314             throw new Exception(errors + " errors found");
315         }
316     }
317 
executeTestCase(TestCase testCase, int index)318     private void executeTestCase(TestCase testCase, int index) {
319         debugPrint("Test case number = " + index);
320         debugPrint(" => baseAnnoTarget = " + testCase.getBaseAnnotations());
321         debugPrint(" => containerAnnoTarget = " + testCase.getContainerAnnotations());
322 
323         String className = "TC" + index;
324         boolean shouldCompile = testCase.isValidSubSet();
325         Iterable<? extends JavaFileObject> files = getFileList(className, testCase, shouldCompile);
326         // Get result of compiling test src file(s).
327         boolean result = getCompileResult(className, shouldCompile, files);
328         // List test src code if test fails.
329         if (!result) {
330             System.out.println("FAIL: Test " + index);
331             try {
332                 for (JavaFileObject f : files) {
333                     System.out.println("File: " + f.getName() + "\n" + f.getCharContent(true));
334                 }
335             } catch (IOException ioe) {
336                 System.out.println("Exception: " + ioe);
337             }
338         } else {
339             debugPrint("PASS: Test " + index);
340         }
341 
342     }
343 
344     // Create src code and corresponding JavaFileObjects.
getFileList(String className, TestCase testCase, boolean shouldCompile)345     private Iterable<? extends JavaFileObject> getFileList(String className,
346             TestCase testCase, boolean shouldCompile) {
347         Set<ElementType> baseAnnoTarget = testCase.getBaseAnnotations();
348         Set<ElementType> conAnnoTarget = testCase.getContainerAnnotations();
349         String srcContent = "";
350         String pkgInfoContent = "";
351         String template = Helper.template;
352         String baseTarget = "", conTarget = "";
353 
354         String target = Helper.ContentVars.TARGET.getVal();
355         if (baseAnnoTarget != null) {
356             String tmp = target.replace("#VAL", convertToString(baseAnnoTarget).toString());
357             baseTarget = tmp.replace("[", "{").replace("]", "}");
358         }
359         if (conAnnoTarget != null) {
360             String tmp = target.replace("#VAL", convertToString(conAnnoTarget).toString());
361             conTarget = tmp.replace("[", "{").replace("]", "}");
362         }
363 
364         String annoData = Helper.ContentVars.IMPORTSTMTS.getVal()
365                 + conTarget
366                 + Helper.ContentVars.CONTAINER.getVal()
367                 + baseTarget
368                 + Helper.ContentVars.REPEATABLE.getVal()
369                 + Helper.ContentVars.BASE.getVal();
370 
371         JavaFileObject pkgInfoFile = null;
372 
373         // If shouldCompile = true and no @Target is specified for container annotation,
374         // then all 8 ElementType enum constants are applicable as targets for
375         // container annotation.
376         if (shouldCompile && conAnnoTarget == null) {
377             Set<ElementType> copySet = EnumSet.noneOf(ElementType.class);
378             copySet.addAll(jdk7);
379             conAnnoTarget = copySet;
380         }
381 
382         if (shouldCompile) {
383             boolean isPkgCasePresent = conAnnoTarget.contains(PACKAGE);
384             String repeatableAnno = Helper.ContentVars.BASEANNO.getVal()
385                     + " " + Helper.ContentVars.BASEANNO.getVal();
386             for (ElementType s : conAnnoTarget) {
387                 String replaceStr = "/*" + s.name() + "*/";
388                 if (s.name().equalsIgnoreCase("PACKAGE")) {
389                     //Create packageInfo file.
390                     String pkgInfoName = TESTPKG + "." + "package-info";
391                     pkgInfoContent = repeatableAnno + "\npackage " + TESTPKG + ";" + annoData;
392                     pkgInfoFile = Helper.getFile(pkgInfoName, pkgInfoContent);
393                 } else {
394                     template = template.replace(replaceStr, repeatableAnno);
395                     if (!isPkgCasePresent) {
396                         srcContent = template.replace(
397                                 "/*ANNODATA*/", annoData).replace("#ClassName", className);
398                     } else {
399                         replaceStr = "/*PACKAGE*/";
400                         String tmp = template.replace(replaceStr, "package " + TESTPKG + ";");
401                         srcContent = tmp.replace("#ClassName", className);
402                     }
403                 }
404             }
405         } else {
406             // For invalid cases, compilation should fail at declaration site.
407             template = "class #ClassName {}";
408             srcContent = annoData + template.replace("#ClassName", className);
409         }
410         JavaFileObject srcFile = Helper.getFile(className, srcContent);
411         Iterable<? extends JavaFileObject> files = null;
412         if (pkgInfoFile != null) {
413             files = Arrays.asList(pkgInfoFile, srcFile);
414         } else {
415             files = Arrays.asList(srcFile);
416         }
417         return files;
418     }
419 
420     // Compile the test source file(s) and return test result.
getCompileResult(String className, boolean shouldCompile, Iterable<? extends JavaFileObject> files)421     private boolean getCompileResult(String className, boolean shouldCompile,
422             Iterable<? extends JavaFileObject> files) {
423 
424         DiagnosticCollector<JavaFileObject> diagnostics =
425                 new DiagnosticCollector<JavaFileObject>();
426         Helper.compileCode(diagnostics, files);
427         // Test case pass or fail.
428         boolean ok = false;
429         String errMesg = "";
430         int numDiags = diagnostics.getDiagnostics().size();
431         if (numDiags == 0) {
432             if (shouldCompile) {
433                 debugPrint("Test passed, compiled as expected.");
434                 ok = true;
435             } else {
436                 errMesg = "Test failed, compiled unexpectedly.";
437                 ok = false;
438             }
439         } else {
440             if (shouldCompile) {
441                 // did not compile.
442                 errMesg = "Test failed, did not compile.";
443                 ok = false;
444             } else {
445                 // Error in compilation as expected.
446                 String expectedErrKey = "compiler.err.invalid.repeatable."
447                         + "annotation.incompatible.target";
448                 for (Diagnostic<?> d : diagnostics.getDiagnostics()) {
449                     if ((d.getKind() == Diagnostic.Kind.ERROR)
450                             && d.getCode().contains(expectedErrKey)) {
451                         // Error message as expected.
452                         debugPrint("Error message as expected.");
453                         ok = true;
454                         break;
455                     } else {
456                         // error message is incorrect.
457                         ok = false;
458                     }
459                 }
460                 if (!ok) {
461                     errMesg = "Incorrect error received when compiling "
462                             + className + ", expected: " + expectedErrKey;
463                 }
464             }
465         }
466 
467         if (!ok) {
468             error(errMesg);
469             for (Diagnostic<?> d : diagnostics.getDiagnostics()) {
470                 System.out.println(" Diags: " + d);
471             }
472         }
473         return ok;
474     }
475 
less(Set<ElementType> base, ElementType... sub)476     private Set<ElementType> less(Set<ElementType> base, ElementType... sub) {
477         Set<ElementType> res = EnumSet.noneOf(ElementType.class);
478         res.addAll(base);
479         for (ElementType t : sub) {
480             res.remove(t);
481         }
482         return res;
483     }
484 
plus(Set<ElementType> base, ElementType... add)485     private Set<ElementType> plus(Set<ElementType> base, ElementType... add) {
486         Set<ElementType> res = EnumSet.noneOf(ElementType.class);
487         res.addAll(base);
488         for (ElementType t : add) {
489             res.add(t);
490         }
491         return res;
492     }
493 
494     // Iterate target set and add "ElementType." in front of every target type.
convertToString(Set<ElementType> annoTarget)495     private List<String> convertToString(Set<ElementType> annoTarget) {
496         if (annoTarget == null) {
497             return null;
498         }
499         List<String> annoTargets = new ArrayList<String>();
500         for (ElementType e : annoTarget) {
501             annoTargets.add("ElementType." + e.name());
502         }
503         return annoTargets;
504     }
505 
debugPrint(String string)506     private void debugPrint(String string) {
507         if (DEBUG) {
508             System.out.println(string);
509         }
510     }
511 
error(String msg)512     private void error(String msg) {
513         System.out.println("ERROR: " + msg);
514         errors++;
515     }
516 }
517 
518