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
33import java.util.Set;
34import java.util.List;
35import java.io.IOException;
36import java.lang.annotation.ElementType;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.EnumSet;
40import javax.tools.Diagnostic;
41import javax.tools.DiagnosticCollector;
42import javax.tools.JavaFileObject;
43
44import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
45import static java.lang.annotation.ElementType.CONSTRUCTOR;
46import static java.lang.annotation.ElementType.FIELD;
47import static java.lang.annotation.ElementType.METHOD;
48import static java.lang.annotation.ElementType.PARAMETER;
49import static java.lang.annotation.ElementType.TYPE;
50import static java.lang.annotation.ElementType.PACKAGE;
51import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
52import static java.lang.annotation.ElementType.TYPE_USE;
53import static java.lang.annotation.ElementType.TYPE_PARAMETER;
54
55public 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
96        public TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations) {
97            this(baseAnnotations, containerAnnotations, IgnoreKind.RUN);
98        }
99
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
107        public Set getBaseAnnotations() {
108            return baseAnnotations;
109        }
110
111        public Set getContainerAnnotations() {
112            return containerAnnotations;
113        }
114
115        public boolean isIgnored() {
116            return ignore == IgnoreKind.IGNORE;
117        }
118
119        // Determine if a testCase should compile or not.
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
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
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
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
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.
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.
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
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
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.
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
506    private void debugPrint(String string) {
507        if (DEBUG) {
508            System.out.println(string);
509        }
510    }
511
512    private void error(String msg) {
513        System.out.println("ERROR: " + msg);
514        errors++;
515    }
516}
517
518