| /* |
| * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 7151010 8006547 8007766 8029017 |
| * @summary Default test cases for running combinations for Target values |
| * @modules jdk.compiler |
| * @build Helper |
| * @run main TargetAnnoCombo |
| */ |
| |
| import java.util.Set; |
| import java.util.List; |
| import java.io.IOException; |
| import java.lang.annotation.ElementType; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| import javax.tools.Diagnostic; |
| import javax.tools.DiagnosticCollector; |
| import javax.tools.JavaFileObject; |
| |
| import static java.lang.annotation.ElementType.ANNOTATION_TYPE; |
| import static java.lang.annotation.ElementType.CONSTRUCTOR; |
| import static java.lang.annotation.ElementType.FIELD; |
| import static java.lang.annotation.ElementType.METHOD; |
| import static java.lang.annotation.ElementType.PARAMETER; |
| import static java.lang.annotation.ElementType.TYPE; |
| import static java.lang.annotation.ElementType.PACKAGE; |
| import static java.lang.annotation.ElementType.LOCAL_VARIABLE; |
| import static java.lang.annotation.ElementType.TYPE_USE; |
| import static java.lang.annotation.ElementType.TYPE_PARAMETER; |
| |
| public class TargetAnnoCombo { |
| |
| static final String TESTPKG = "testpkg"; |
| |
| // Set it to true to get more debug information including base and container |
| // target sets for a given test case. |
| static final boolean DEBUG = false; |
| |
| // Define constant target sets to be used for the combination of the target values. |
| final static Set<ElementType> noSet = null; |
| final static Set<ElementType> empty = EnumSet.noneOf(ElementType.class); |
| |
| // [TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, |
| // PACKAGE, TYPE_PARAMETER, TYPE_USE] |
| final static Set<ElementType> allTargets = EnumSet.allOf(ElementType.class); |
| |
| // [TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, |
| // PACKAGE] |
| final static Set<ElementType> jdk7 = EnumSet.range(TYPE, PACKAGE); |
| |
| // [TYPE_USE, TYPE_PARAMETER] |
| final static Set<ElementType> jdk8 = EnumSet.range(TYPE_PARAMETER, TYPE_USE); |
| |
| // List of test cases to run. This list is created in generate(). |
| // To run a specific test cases add case number in @run main line. |
| List<TestCase> testCases = new ArrayList<TestCase>(); |
| |
| int errors = 0; |
| |
| // Identify test cases that fail. |
| enum IgnoreKind { |
| RUN, |
| IGNORE |
| }; |
| |
| private class TestCase { |
| |
| private Set<ElementType> baseAnnotations; |
| private Set<ElementType> containerAnnotations; |
| private IgnoreKind ignore; |
| |
| public TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations) { |
| this(baseAnnotations, containerAnnotations, IgnoreKind.RUN); |
| } |
| |
| public TestCase(Set<ElementType> baseAnnotations, Set<ElementType> containerAnnotations, |
| IgnoreKind ignoreKind) { |
| this.baseAnnotations = baseAnnotations; |
| this.containerAnnotations = containerAnnotations; |
| this.ignore = ignoreKind; |
| } |
| |
| public Set getBaseAnnotations() { |
| return baseAnnotations; |
| } |
| |
| public Set getContainerAnnotations() { |
| return containerAnnotations; |
| } |
| |
| public boolean isIgnored() { |
| return ignore == IgnoreKind.IGNORE; |
| } |
| |
| // Determine if a testCase should compile or not. |
| private boolean isValidSubSet() { |
| /* |
| * RULE 1: conAnnoTarget should be a subset of baseAnnoTarget |
| * RULE 2: For empty @Target ({}) - annotation cannot be applied anywhere |
| * - Empty sets for both is valid |
| * - Empty baseTarget set is invalid with non-empty conTarget set |
| * - Non-empty baseTarget set is valid with empty conTarget set |
| * RULE 3: For no @Target specified - annotation can be applied to any JDK 7 targets |
| * - No @Target for both is valid |
| * - No @Target for baseTarget set with @Target conTarget set is valid |
| * - @Target for baseTarget set with no @Target for conTarget is invalid |
| */ |
| |
| |
| /* If baseAnno has no @Target, Foo can be either applied to @Target specified |
| * for container annotation else will be applicable for all default targets |
| * if no @Target is present for container annotation. |
| * In both cases, the set will be a valid set with no @Target for base annotation |
| */ |
| if (baseAnnotations == null) { |
| if (containerAnnotations == null) { |
| return true; |
| } |
| return !(containerAnnotations.contains(TYPE_USE) || |
| containerAnnotations.contains(TYPE_PARAMETER)); |
| } |
| |
| Set<ElementType> tempBaseSet = EnumSet.noneOf(ElementType.class); |
| tempBaseSet.addAll(baseAnnotations); |
| |
| // If BaseAnno has TYPE, then ANNOTATION_TYPE is allowed by default. |
| if (baseAnnotations.contains(TYPE)) { |
| tempBaseSet.add(ANNOTATION_TYPE); |
| } |
| |
| // If BaseAnno has TYPE_USE, then add the extra allowed types |
| if (baseAnnotations.contains(TYPE_USE)) { |
| tempBaseSet.add(ANNOTATION_TYPE); |
| tempBaseSet.add(TYPE); |
| tempBaseSet.add(TYPE_PARAMETER); |
| } |
| |
| // If containerAnno has no @Target, only valid case if baseAnnoTarget has |
| // all targets defined else invalid set. |
| if (containerAnnotations == null) { |
| return tempBaseSet.containsAll(jdk7); |
| } |
| |
| // At this point, neither conAnnoTarget or baseAnnoTarget are null. |
| if (containerAnnotations.isEmpty()) { |
| return true; |
| } |
| |
| // At this point, conAnnoTarget is non-empty. |
| if (baseAnnotations.isEmpty()) { |
| return false; |
| } |
| |
| // At this point, neither conAnnoTarget or baseAnnoTarget are empty. |
| return tempBaseSet.containsAll(containerAnnotations); |
| } |
| } |
| |
| public static void main(String args[]) throws Exception { |
| TargetAnnoCombo tac = new TargetAnnoCombo(); |
| // Generates all test cases to be run. |
| tac.generate(); |
| List<Integer> cases = new ArrayList<Integer>(); |
| for (int i = 0; i < args.length; i++) { |
| cases.add(Integer.parseInt(args[i])); |
| } |
| if (cases.isEmpty()) { |
| tac.run(); |
| } else { |
| for (int index : cases) { |
| tac.executeTestCase(tac.testCases.get(index), index); |
| } |
| } |
| } |
| |
| private void generate() { |
| // Adding test cases to run. |
| testCases.addAll(Arrays.asList( |
| // No base target against no container target. |
| new TestCase(noSet, noSet), |
| // No base target against empty container target. |
| new TestCase(noSet, empty), |
| // No base target against TYPE_USE only container target. |
| new TestCase(noSet, less(jdk8, TYPE_PARAMETER)), |
| // No base target against TYPE_PARAMETER only container target. |
| new TestCase(noSet, less(jdk8, TYPE_USE)), |
| // No base target against TYPE_USE + TYPE_PARAMETER only container target. |
| new TestCase(noSet, jdk8), |
| // No base target against TYPE_USE + some selection of jdk7 targets. |
| new TestCase(noSet, |
| plus(EnumSet.range(TYPE, LOCAL_VARIABLE), TYPE_USE)), |
| // No base target against TYPE_PARAMETER + some selection of jdk7 targets. |
| new TestCase(noSet, |
| plus(EnumSet.range(TYPE, LOCAL_VARIABLE), TYPE_PARAMETER)), |
| // No base target against each jdk7 target alone as container target. |
| new TestCase(noSet, plus(empty, TYPE)), |
| new TestCase(noSet, plus(empty, PARAMETER)), |
| new TestCase(noSet, plus(empty, PACKAGE)), |
| new TestCase(noSet, plus(empty, METHOD)), |
| new TestCase(noSet, plus(empty, LOCAL_VARIABLE)), |
| new TestCase(noSet, plus(empty, FIELD)), |
| new TestCase(noSet, plus(empty, CONSTRUCTOR)), |
| new TestCase(noSet, plus(empty, ANNOTATION_TYPE)), |
| // Empty base target against no container target. |
| new TestCase(empty, noSet), |
| // Empty base target against empty container target. |
| new TestCase(empty, empty), |
| // Empty base target against any lone container target. |
| new TestCase(empty, plus(empty, TYPE)), |
| new TestCase(empty, plus(empty, PARAMETER)), |
| new TestCase(empty, plus(empty, PACKAGE)), |
| new TestCase(empty, plus(empty, METHOD)), |
| new TestCase(empty, plus(empty, LOCAL_VARIABLE)), |
| new TestCase(empty, plus(empty, FIELD)), |
| new TestCase(empty, plus(empty, CONSTRUCTOR)), |
| new TestCase(empty, plus(empty, ANNOTATION_TYPE)), |
| new TestCase(empty, less(jdk8, TYPE_USE)), |
| new TestCase(empty, less(jdk8, TYPE_PARAMETER)), |
| // No container target against all all-but one jdk7 targets. |
| new TestCase(less(jdk7, TYPE), noSet), |
| new TestCase(less(jdk7, PARAMETER), noSet), |
| new TestCase(less(jdk7, PACKAGE), noSet), |
| new TestCase(less(jdk7, METHOD), noSet), |
| new TestCase(less(jdk7, LOCAL_VARIABLE), noSet), |
| new TestCase(less(jdk7, FIELD), noSet), |
| new TestCase(less(jdk7, CONSTRUCTOR), noSet), |
| new TestCase(less(jdk7, ANNOTATION_TYPE), noSet), |
| // No container against all but TYPE and ANNOTATION_TYPE |
| new TestCase(less(jdk7, TYPE, ANNOTATION_TYPE), noSet), |
| // No container against jdk7 targets. |
| new TestCase(jdk7, noSet), |
| // No container against jdk7 targets plus one or both of TYPE_USE, TYPE_PARAMETER |
| new TestCase(plus(jdk7, TYPE_USE), noSet), |
| new TestCase(plus(jdk7, TYPE_PARAMETER), noSet), |
| new TestCase(allTargets, noSet), |
| // Empty container target against any lone target. |
| new TestCase(plus(empty, TYPE), empty), |
| new TestCase(plus(empty, PARAMETER), empty), |
| new TestCase(plus(empty, PACKAGE), empty), |
| new TestCase(plus(empty, METHOD), empty), |
| new TestCase(plus(empty, LOCAL_VARIABLE), empty), |
| new TestCase(plus(empty, FIELD), empty), |
| new TestCase(plus(empty, CONSTRUCTOR), empty), |
| new TestCase(plus(empty, ANNOTATION_TYPE), empty), |
| new TestCase(plus(empty, TYPE_USE), empty), |
| new TestCase(plus(empty, TYPE_PARAMETER), empty), |
| // All base targets against all container targets. |
| new TestCase(allTargets, allTargets), |
| // All base targets against all but one container targets. |
| new TestCase(allTargets, less(allTargets, TYPE)), |
| new TestCase(allTargets, less(allTargets, PARAMETER)), |
| new TestCase(allTargets, less(allTargets, PACKAGE)), |
| new TestCase(allTargets, less(allTargets, METHOD)), |
| new TestCase(allTargets, less(allTargets, LOCAL_VARIABLE)), |
| new TestCase(allTargets, less(allTargets, FIELD)), |
| new TestCase(allTargets, less(allTargets, CONSTRUCTOR)), |
| new TestCase(allTargets, less(allTargets, ANNOTATION_TYPE)), |
| new TestCase(allTargets, less(allTargets, TYPE_USE)), |
| new TestCase(allTargets, less(allTargets, TYPE_PARAMETER)), |
| // All container targets against all but one base targets. |
| new TestCase(less(allTargets, TYPE), allTargets), |
| new TestCase(less(allTargets, PARAMETER), allTargets), |
| new TestCase(less(allTargets, PACKAGE), allTargets), |
| new TestCase(less(allTargets, METHOD), allTargets), |
| new TestCase(less(allTargets, LOCAL_VARIABLE), allTargets), |
| new TestCase(less(allTargets, FIELD), allTargets), |
| new TestCase(less(allTargets, CONSTRUCTOR), allTargets), |
| new TestCase(less(allTargets, ANNOTATION_TYPE), allTargets), |
| new TestCase(less(allTargets, TYPE_USE), allTargets), |
| new TestCase(less(allTargets, TYPE_PARAMETER), allTargets))); |
| // Generates 100 test cases for any lone base target contained in Set |
| // allTargets against any lone container target. |
| for (ElementType b : allTargets) { |
| for (ElementType c : allTargets) { |
| testCases.add(new TestCase(plus(empty, b), plus(empty, c))); |
| } |
| } |
| } |
| |
| void run() throws Exception { |
| int testCtr = 0; |
| for (TestCase tc : testCases) { |
| if (!tc.isIgnored()) { |
| executeTestCase(tc, testCases.indexOf(tc)); |
| testCtr++; |
| } |
| } |
| System.out.println("Total tests run: " + testCtr); |
| if (errors > 0) { |
| throw new Exception(errors + " errors found"); |
| } |
| } |
| |
| private void executeTestCase(TestCase testCase, int index) { |
| debugPrint("Test case number = " + index); |
| debugPrint(" => baseAnnoTarget = " + testCase.getBaseAnnotations()); |
| debugPrint(" => containerAnnoTarget = " + testCase.getContainerAnnotations()); |
| |
| String className = "TC" + index; |
| boolean shouldCompile = testCase.isValidSubSet(); |
| Iterable<? extends JavaFileObject> files = getFileList(className, testCase, shouldCompile); |
| // Get result of compiling test src file(s). |
| boolean result = getCompileResult(className, shouldCompile, files); |
| // List test src code if test fails. |
| if (!result) { |
| System.out.println("FAIL: Test " + index); |
| try { |
| for (JavaFileObject f : files) { |
| System.out.println("File: " + f.getName() + "\n" + f.getCharContent(true)); |
| } |
| } catch (IOException ioe) { |
| System.out.println("Exception: " + ioe); |
| } |
| } else { |
| debugPrint("PASS: Test " + index); |
| } |
| |
| } |
| |
| // Create src code and corresponding JavaFileObjects. |
| private Iterable<? extends JavaFileObject> getFileList(String className, |
| TestCase testCase, boolean shouldCompile) { |
| Set<ElementType> baseAnnoTarget = testCase.getBaseAnnotations(); |
| Set<ElementType> conAnnoTarget = testCase.getContainerAnnotations(); |
| String srcContent = ""; |
| String pkgInfoContent = ""; |
| String template = Helper.template; |
| String baseTarget = "", conTarget = ""; |
| |
| String target = Helper.ContentVars.TARGET.getVal(); |
| if (baseAnnoTarget != null) { |
| String tmp = target.replace("#VAL", convertToString(baseAnnoTarget).toString()); |
| baseTarget = tmp.replace("[", "{").replace("]", "}"); |
| } |
| if (conAnnoTarget != null) { |
| String tmp = target.replace("#VAL", convertToString(conAnnoTarget).toString()); |
| conTarget = tmp.replace("[", "{").replace("]", "}"); |
| } |
| |
| String annoData = Helper.ContentVars.IMPORTSTMTS.getVal() |
| + conTarget |
| + Helper.ContentVars.CONTAINER.getVal() |
| + baseTarget |
| + Helper.ContentVars.REPEATABLE.getVal() |
| + Helper.ContentVars.BASE.getVal(); |
| |
| JavaFileObject pkgInfoFile = null; |
| |
| // If shouldCompile = true and no @Target is specified for container annotation, |
| // then all 8 ElementType enum constants are applicable as targets for |
| // container annotation. |
| if (shouldCompile && conAnnoTarget == null) { |
| Set<ElementType> copySet = EnumSet.noneOf(ElementType.class); |
| copySet.addAll(jdk7); |
| conAnnoTarget = copySet; |
| } |
| |
| if (shouldCompile) { |
| boolean isPkgCasePresent = conAnnoTarget.contains(PACKAGE); |
| String repeatableAnno = Helper.ContentVars.BASEANNO.getVal() |
| + " " + Helper.ContentVars.BASEANNO.getVal(); |
| for (ElementType s : conAnnoTarget) { |
| String replaceStr = "/*" + s.name() + "*/"; |
| if (s.name().equalsIgnoreCase("PACKAGE")) { |
| //Create packageInfo file. |
| String pkgInfoName = TESTPKG + "." + "package-info"; |
| pkgInfoContent = repeatableAnno + "\npackage " + TESTPKG + ";" + annoData; |
| pkgInfoFile = Helper.getFile(pkgInfoName, pkgInfoContent); |
| } else { |
| template = template.replace(replaceStr, repeatableAnno); |
| if (!isPkgCasePresent) { |
| srcContent = template.replace( |
| "/*ANNODATA*/", annoData).replace("#ClassName", className); |
| } else { |
| replaceStr = "/*PACKAGE*/"; |
| String tmp = template.replace(replaceStr, "package " + TESTPKG + ";"); |
| srcContent = tmp.replace("#ClassName", className); |
| } |
| } |
| } |
| } else { |
| // For invalid cases, compilation should fail at declaration site. |
| template = "class #ClassName {}"; |
| srcContent = annoData + template.replace("#ClassName", className); |
| } |
| JavaFileObject srcFile = Helper.getFile(className, srcContent); |
| Iterable<? extends JavaFileObject> files = null; |
| if (pkgInfoFile != null) { |
| files = Arrays.asList(pkgInfoFile, srcFile); |
| } else { |
| files = Arrays.asList(srcFile); |
| } |
| return files; |
| } |
| |
| // Compile the test source file(s) and return test result. |
| private boolean getCompileResult(String className, boolean shouldCompile, |
| Iterable<? extends JavaFileObject> files) { |
| |
| DiagnosticCollector<JavaFileObject> diagnostics = |
| new DiagnosticCollector<JavaFileObject>(); |
| Helper.compileCode(diagnostics, files); |
| // Test case pass or fail. |
| boolean ok = false; |
| String errMesg = ""; |
| int numDiags = diagnostics.getDiagnostics().size(); |
| if (numDiags == 0) { |
| if (shouldCompile) { |
| debugPrint("Test passed, compiled as expected."); |
| ok = true; |
| } else { |
| errMesg = "Test failed, compiled unexpectedly."; |
| ok = false; |
| } |
| } else { |
| if (shouldCompile) { |
| // did not compile. |
| errMesg = "Test failed, did not compile."; |
| ok = false; |
| } else { |
| // Error in compilation as expected. |
| String expectedErrKey = "compiler.err.invalid.repeatable." |
| + "annotation.incompatible.target"; |
| for (Diagnostic<?> d : diagnostics.getDiagnostics()) { |
| if ((d.getKind() == Diagnostic.Kind.ERROR) |
| && d.getCode().contains(expectedErrKey)) { |
| // Error message as expected. |
| debugPrint("Error message as expected."); |
| ok = true; |
| break; |
| } else { |
| // error message is incorrect. |
| ok = false; |
| } |
| } |
| if (!ok) { |
| errMesg = "Incorrect error received when compiling " |
| + className + ", expected: " + expectedErrKey; |
| } |
| } |
| } |
| |
| if (!ok) { |
| error(errMesg); |
| for (Diagnostic<?> d : diagnostics.getDiagnostics()) { |
| System.out.println(" Diags: " + d); |
| } |
| } |
| return ok; |
| } |
| |
| private Set<ElementType> less(Set<ElementType> base, ElementType... sub) { |
| Set<ElementType> res = EnumSet.noneOf(ElementType.class); |
| res.addAll(base); |
| for (ElementType t : sub) { |
| res.remove(t); |
| } |
| return res; |
| } |
| |
| private Set<ElementType> plus(Set<ElementType> base, ElementType... add) { |
| Set<ElementType> res = EnumSet.noneOf(ElementType.class); |
| res.addAll(base); |
| for (ElementType t : add) { |
| res.add(t); |
| } |
| return res; |
| } |
| |
| // Iterate target set and add "ElementType." in front of every target type. |
| private List<String> convertToString(Set<ElementType> annoTarget) { |
| if (annoTarget == null) { |
| return null; |
| } |
| List<String> annoTargets = new ArrayList<String>(); |
| for (ElementType e : annoTarget) { |
| annoTargets.add("ElementType." + e.name()); |
| } |
| return annoTargets; |
| } |
| |
| private void debugPrint(String string) { |
| if (DEBUG) { |
| System.out.println(string); |
| } |
| } |
| |
| private void error(String msg) { |
| System.out.println("ERROR: " + msg); |
| errors++; |
| } |
| } |
| |