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