diff -r b386b8c45387 -r 5125b9854d07 test/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/annotations/repeatingAnnotations/combo/TargetAnnoCombo.java Thu Feb 07 20:47:06 2013 -0800 @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2013, 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 7195131 + * @author sogoel + * @summary Combo test for all possible combinations for Target values + * @build Helper + * @compile TargetAnnoCombo.java TestCaseGenerator.java + * @run main TargetAnnoCombo + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; + +/* + * TargetAnnoCombo gets a list of test case numbers using TestCaseGenerator. + * For each of the test case number, @Target sets for base and container annotations + * are determined, source files are generated, compiled, and the result is verified + * based on if the @Target set for base and container is a positive or negative combination. + * + * @Target sets for base and container annotations are determined using a bit mapping of + * 10 ElementType enum constants defined in JDK8. + * + * Bit Target value + * 0 "ElementType.ANNOTATION_TYPE" + * 1 "ElementType.CONSTRUCTOR" + * 2 "ElementType.FIELD" + * 3 "ElementType.LOCAL_VARIABLE" + * 4 "ElementType.METHOD" + * 5 "ElementType.TYPE" + * 6 "ElementType.PARAMETER" + * 7 "ElementType.PACKAGE" + * 8 "ElementType.TYPE_USE" + * 9 "ElementType.TYPE_PARAMETER" + * + * Group 1: + * 20 bits mapping, representing a test case number, is used for all target set + * combinations ( 0 to 1048575 ) including empty @Target sets => @Target({}). + * From this 20 bits, 10 bits are for base followed by 10 bits for container + * where each bit maps to an ElementType enum constant defined in JDK8. + * + * Examples: + * Test case number: 4, binary: 100 => container=100, base=[], container=["ElementType.FIELD"] + * Test case number: 1003575, binary: 11110101000000110111 => base=1111010100, container=0000110111; + * base=["ElementType.PARAMETER", "ElementType.TYPE_USE", "ElementType.METHOD", "ElementType.FIELD", "ElementType.PACKAGE", "ElementType.TYPE_PARAMETER"], + * container=["ElementType.TYPE", "ElementType.METHOD", "ElementType.ANNOTATION_TYPE", "ElementType.CONSTRUCTOR", "ElementType.FIELD"] + * + * In the following groups, no @Target set is represented by null. + * Group 2: + * @Target is not defined on base. + * Target sets for container are determined using the 10-bit binary number + * resulting in 1024 test cases, mapping them to test case numbers from + * 1048576 to (1048576 + 1023) => 1048576 to 1049599. + * + * Example: + * Test case number: 1048587 => 1048587 - 1048576 = test case 11 in Group 2, binary: 1011 => + * base = null, + * container = ["ElementType.ANNOTATION_TYPE","ElementType.CONSTRUCTOR","ElementType.LOCAL_VARIABLE"] + * + * Group 3: + * @Target is not defined on container + * Target sets for base are determined using the 10-bit binary number + * resulting in 1024 test cases, mapping them to test case numbers from + * 1049600 to (1049600 + 1023) => 1049600 to 1050623. + * + * Example: + * Test case number: 1049708 => 1049708 - 1049600 = test case 108 in Group 3, binary: 1101100 => + * base = ["ElementType.FIELD", "ElementType.LOCAL_VARIABLE", "ElementType.TYPE", "ElementType.PARAMETER"], + * container = null + * + * For the above group, test case number: 1049855 gives compiler error, JDK-8006547 filed + * + * Group 4: + * @Target not defined for both base and container annotations. + * + * This is the last test and corresponds to test case number 1050624. base=null, container=null + * + * Examples to run this test: + * 1. Run a specific test case number: + * ${JTREG} -DTestCaseNum=10782 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java + * 2. Run specific number of tests: + * ${JTREG} -DNumberOfTests=4 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java + * 3. Run specific number of tests with a seed: + * ${JTREG} -DNumberOfTests=4 -DTestSeed=-972894659 -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java + * 4. Run tests in default mode (number of tests = 1000): + * ${JTREG} -DTestMode=DEFAULT -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java + * 5. Run all tests (FULL mode): + * ${JTREG} -DTestMode=FULL -samevm -jdk:${JAVA_TEST} -reportDir ${REPORT} -workDir ${WORK} TargetAnnoCombo.java + * + */ + +public class TargetAnnoCombo { + int errors = 0; + 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 number + */ + static final boolean DEBUG = false; + + // JDK 5/6/7/8 Targets + static final String[] targetVals = {"ElementType.ANNOTATION_TYPE", + "ElementType.CONSTRUCTOR", "ElementType.FIELD", + "ElementType.LOCAL_VARIABLE", "ElementType.METHOD", + "ElementType.TYPE", "ElementType.PARAMETER", + "ElementType.PACKAGE", "ElementType.TYPE_USE", + "ElementType.TYPE_PARAMETER"}; + + // TYPE_USE and TYPE_PARAMETER (added in JDK8) are not part of default Target set + static final int DEFAULT_TARGET_CNT = 8; + + public static void main(String args[]) throws Exception { + + /* maxTestNum = (base and container combinations of targetVals elems [0 - 1048575 combos]) + * + (combinations where base or container has no Target [1024 combos]) + * + (no -1 even though 1st test is number 0 as last test is where both + * base and container have no target) + */ + + int maxTestNum = (int)Math.pow(2, 2*targetVals.length) + 2*(int)Math.pow(2, targetVals.length); + TestCaseGenerator tcg = new TestCaseGenerator(maxTestNum); + TargetAnnoCombo tac = new TargetAnnoCombo(); + + int testCtr = 0; + int testCase = -1; + while ( (testCase=tcg.getNextTestCase()) != -1 ) { + tac.executeTestCase(testCase, maxTestNum); + testCtr++; + } + + System.out.println("Total tests run: " + testCtr); + if (tac.errors > 0) + throw new Exception(tac.errors + " errors found"); + } + + /* + * For given testCase, determine the base and container annotation Target sets, + * get if testCase should compile, get test source file(s), get compilation result and verify. + * + */ + private void executeTestCase(int testCase, int maxTestNum) { + + // Determine base and container annotation Target sets for the testCase + Set baseAnnoTarget = null; + Set conAnnoTarget = null; + + //Number of base and container combinations [0 - 1048575 combos] + int baseContCombos = (int)Math.pow(2, 2*targetVals.length); + //Number of either base or container combinations when one of them has no @Target [1024 combos] + int targetValsCombos = (int)Math.pow(2, targetVals.length); + + if (testCase >= baseContCombos) { + //Base annotation do not have @Target + if (testCase < baseContCombos + targetValsCombos) { + baseAnnoTarget = null; + conAnnoTarget = getSetFromBitVec(Integer.toBinaryString(testCase - baseContCombos)); + } else if (testCase < baseContCombos + 2*targetValsCombos) { + //Container annotation do not have @Target + baseAnnoTarget = getSetFromBitVec(Integer.toBinaryString(testCase - baseContCombos - targetValsCombos)); + conAnnoTarget = null; + } else { + //Both Base and Container annotation do not have @Target + baseAnnoTarget = null; + conAnnoTarget = null; + } + } else { + //TestCase number is represented as 10-bits for base followed by container bits + String bin = Integer.toBinaryString(testCase); + String base="", cont=bin; + if (bin.length() > targetVals.length){ + base = bin.substring(0, bin.length() - targetVals.length); + cont = bin.substring(bin.length() - targetVals.length,bin.length()); + } + baseAnnoTarget = getSetFromBitVec(base); + conAnnoTarget = getSetFromBitVec(cont); + } + + debugPrint("Test case number = " + testCase + " => binary = " + Integer.toBinaryString(testCase)); + debugPrint(" => baseAnnoTarget = " + baseAnnoTarget); + debugPrint(" => containerAnnoTarget = " + conAnnoTarget); + + // Determine if a testCase should compile or not + String className = "TC" + testCase; + boolean shouldCompile = isValidSubSet(baseAnnoTarget, conAnnoTarget); + + // Get test source file(s) + Iterable files = getFileList(className, baseAnnoTarget, + conAnnoTarget, 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 " + testCase); + 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 " + testCase); + } + } + + // Get a Set based on bits that are set to 1 + public Set getSetFromBitVec(String bitVec) { + Set ret = new HashSet<>(); + char[] bit = bitVec.toCharArray(); + for (int i=bit.length-1, j=0; i>=0; i--, j++){ + if (bit[i] == '1') { + ret.add(targetVals[j]); + } + } + return ret; + } + + // Compile the test source file(s) and return test result + private boolean getCompileResult(String className, boolean shouldCompile, + Iterable files) { + + DiagnosticCollector diagnostics = + new DiagnosticCollector(); + 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 void debugPrint(String string) { + if(DEBUG) + System.out.println(string); + } + + // Create src code and corresponding JavaFileObjects + private Iterable getFileList(String className, + Set baseAnnoTarget, Set conAnnoTarget, + boolean shouldCompile) { + + String srcContent = ""; + String pkgInfoContent = ""; + String template = Helper.template; + String baseTarget = "", conTarget = ""; + + String target = Helper.ContentVars.TARGET.getVal(); + if(baseAnnoTarget != null) { + baseTarget = target.replace("#VAL", baseAnnoTarget.toString()) + .replace("[", "{").replace("]", "}"); + } + if(conAnnoTarget != null) { + conTarget = target.replace("#VAL", conAnnoTarget.toString()) + .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) { + //conAnnoTarget = new HashSet(Arrays.asList(targetVals)); + conAnnoTarget = getDefaultTargetSet(); + } + + if(shouldCompile) { + boolean isPkgCasePresent = new ArrayList(conAnnoTarget).contains("ElementType.PACKAGE"); + String repeatableAnno = Helper.ContentVars.BASEANNO.getVal() + " " + Helper.ContentVars.BASEANNO.getVal(); + for(String s: conAnnoTarget) { + s = s.replace("ElementType.",""); + String replaceStr = "/*"+s+"*/"; + if(s.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); + //srcContent = template.replace("#ClassName",className); + if(!isPkgCasePresent) { + srcContent = template.replace("/*ANNODATA*/", annoData).replace("#ClassName",className); + } else { + replaceStr = "/*PACKAGE*/"; + srcContent = template.replace(replaceStr, "package " + TESTPKG + ";") + .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 files = null; + if(pkgInfoFile != null) + files = Arrays.asList(pkgInfoFile,srcFile); + else + files = Arrays.asList(srcFile); + return files; + } + + private Set getDefaultTargetSet() { + Set defaultSet = new HashSet<>(); + int ctr = 0; + for(String s : targetVals) { + if(ctr++ < DEFAULT_TARGET_CNT) { + defaultSet.add(s); + } + } + return defaultSet; + } + + private boolean isValidSubSet(Set baseAnnoTarget, Set conAnnoTarget) { + /* + * 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(baseAnnoTarget == null) { + if(conAnnoTarget == null) return true; + return !(conAnnoTarget.contains("ElementType.TYPE_USE") || conAnnoTarget.contains("ElementType.TYPE_PARAMETER")); + } + + Set tempBaseSet = new HashSet<>(baseAnnoTarget); + // If BaseAnno has TYPE, then ANNOTATION_TYPE is allowed by default + if(baseAnnoTarget.contains("ElementType.TYPE")) { + tempBaseSet.add("ElementType.ANNOTATION_TYPE"); + } + + /* + * If containerAnno has no @Target, only valid case if baseAnnoTarget has all targets defined + * else invalid set + */ + if(conAnnoTarget == null) { + return (tempBaseSet.containsAll(getDefaultTargetSet())); + } + + // At this point, neither conAnnoTarget or baseAnnoTarget are null + if(conAnnoTarget.size() == 0) return true; + + // At this point, conAnnoTarget is non-empty + if (baseAnnoTarget.size() == 0) return false; + + // At this point, neither conAnnoTarget or baseAnnoTarget are empty + return tempBaseSet.containsAll(conAnnoTarget); + } + + void error(String msg) { + System.out.println("ERROR: " + msg); + errors++; + } + + // Lists the start and end range for the given set of target vals + void showGroups() { + //Group 1: All target set combinations ( 0 to 1048575 ) including empty @Target sets => @Target({}) + int grpEnd1 = (int)Math.pow(2, 2*targetVals.length) - 1; + System.out.println("[Group 1]: 0 - " + grpEnd1); + + //Group 2: @Target not defined for base annotation ( 1048576 - 1049599 ). + System.out.print("[Group 2]: " + (grpEnd1 + 1) + " - "); + int grpEnd2 = grpEnd1 + 1 + (int)Math.pow(2, targetVals.length) - 1; + System.out.println(grpEnd2); + + //Group 3: @Target not defined for container annotation ( 1049600 - 1050623 ). + System.out.print("[Group 3]: " + (grpEnd2 + 1) + " - "); + int grpEnd3 = grpEnd2 + 1 + (int)Math.pow(2, targetVals.length) - 1; + System.out.println(grpEnd3); + + //Group 4: @Target not defined for both base and container annotations ( 1050624 ). + System.out.println("[Group 4]: " + (grpEnd3 + 1)); + } +}