1/* 2 * Copyright (c) 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 24import com.sun.tools.classfile.Attribute; 25import com.sun.tools.classfile.ConstantPoolException; 26import com.sun.tools.classfile.Descriptor; 27 28import javax.tools.JavaFileObject; 29import java.io.IOException; 30import java.lang.annotation.RetentionPolicy; 31import java.nio.file.Path; 32import java.nio.file.Paths; 33import java.util.*; 34import java.util.stream.Collectors; 35import java.util.stream.IntStream; 36import java.util.stream.Stream; 37 38public abstract class AnnotationsTestBase extends TestResult { 39 40 /** 41 * Element values which are used in generation of annotations. 42 */ 43 private static final TestAnnotationInfo.Pair[] elementValues = { 44 new TestAnnotationInfo.Pair("booleanValue", new TestAnnotationInfo.TestBooleanElementValue(true)), 45 new TestAnnotationInfo.Pair("byteValue", new TestAnnotationInfo.TestIntegerElementValue('B', 83)), 46 new TestAnnotationInfo.Pair("charValue", new TestAnnotationInfo.TestCharElementValue('H')), 47 new TestAnnotationInfo.Pair("shortValue", new TestAnnotationInfo.TestIntegerElementValue('S', 14)), 48 new TestAnnotationInfo.Pair("intValue", new TestAnnotationInfo.TestIntegerElementValue('I', 18)), 49 new TestAnnotationInfo.Pair("longValue", new TestAnnotationInfo.TestLongElementValue(14)), 50 new TestAnnotationInfo.Pair("floatValue", new TestAnnotationInfo.TestFloatElementValue(-1)), 51 new TestAnnotationInfo.Pair("doubleValue", new TestAnnotationInfo.TestDoubleElementValue(-83)), 52 new TestAnnotationInfo.Pair("stringValue", new TestAnnotationInfo.TestStringElementValue("///")), 53 new TestAnnotationInfo.Pair("arrayValue1", new TestAnnotationInfo.TestArrayElementValue( 54 new TestAnnotationInfo.TestIntegerElementValue('I', 1), 55 new TestAnnotationInfo.TestIntegerElementValue('I', 4), 56 new TestAnnotationInfo.TestIntegerElementValue('I', 8), 57 new TestAnnotationInfo.TestIntegerElementValue('I', 3))), 58 new TestAnnotationInfo.Pair("arrayValue2", new TestAnnotationInfo.TestArrayElementValue( 59 new TestAnnotationInfo.TestStringElementValue("AAA"), 60 new TestAnnotationInfo.TestStringElementValue("BBB"))), 61 new TestAnnotationInfo.Pair("enumValue", new TestAnnotationInfo.TestEnumElementValue("EnumValue", "VALUE2")), 62 new TestAnnotationInfo.Pair("classValue1", new TestAnnotationInfo.TestClassElementValue("void.class")), 63 new TestAnnotationInfo.Pair("classValue2", new TestAnnotationInfo.TestClassElementValue("Character.class")), 64 new TestAnnotationInfo.Pair("annoValue", new TestAnnotationInfo.TestAnnotationElementValue("AnnotationValue", 65 new TestAnnotationInfo("AnnotationValue", RetentionPolicy.CLASS, 66 new TestAnnotationInfo.Pair("stringValue", 67 new TestAnnotationInfo.TestStringElementValue("StringValue1"))))), 68 new TestAnnotationInfo.Pair("annoArrayValue", new TestAnnotationInfo.TestArrayElementValue( 69 new TestAnnotationInfo.TestAnnotationElementValue("AnnotationValue", 70 new TestAnnotationInfo("AnnotationValue", RetentionPolicy.CLASS, 71 new TestAnnotationInfo.Pair("stringValue", 72 new TestAnnotationInfo.TestStringElementValue("StringValue1")))), 73 new TestAnnotationInfo.TestAnnotationElementValue("AnnotationValue", 74 new TestAnnotationInfo("AnnotationValue", RetentionPolicy.CLASS, 75 new TestAnnotationInfo.Pair("stringValue", 76 new TestAnnotationInfo.TestStringElementValue("StringValue1")))))) 77 }; 78 79 /** 80 * Masks which are used in generation of annotations. 81 * E.g. mask 0 corresponds to an annotation without element values. 82 */ 83 private static final int[] elementValuesCombinations; 84 85 static { 86 List<Integer> combinations = new ArrayList<>(); 87 combinations.add(0); 88 for (int i = 0; i < elementValues.length; ++i) { 89 combinations.add(1 << i); 90 } 91 // pairs int value and another value 92 for (int i = 0; i < elementValues.length; ++i) { 93 combinations.add((1 << 5) | (1 << i)); 94 } 95 combinations.add((1 << elementValues.length) - 1); 96 elementValuesCombinations = combinations.stream().mapToInt(Integer::intValue).toArray(); 97 } 98 99 /** 100 * Method generates a list of test cases. 101 * Method is called in the method {@code call()}. 102 * 103 * @return a list of test cases 104 */ 105 public abstract List<TestCase> generateTestCases(); 106 107 public abstract void test(TestCase testCase, Map<String, ? extends JavaFileObject> classes) 108 throws IOException, ConstantPoolException, Descriptor.InvalidDescriptor; 109 110 /** 111 * The method is used to create a repeatable annotation. 112 */ 113 private TestAnnotationInfo createSomeAnnotation(String annotationName) { 114 return new TestAnnotationInfo(annotationName, getRetentionPolicy(annotationName), 115 new TestAnnotationInfo.Pair("booleanValue", 116 new TestAnnotationInfo.TestBooleanElementValue(true)), 117 new TestAnnotationInfo.Pair("intValue", 118 new TestAnnotationInfo.TestIntegerElementValue('I', 1)), 119 new TestAnnotationInfo.Pair("enumValue", 120 new TestAnnotationInfo.TestEnumElementValue("EnumValue", "VALUE1"))); 121 } 122 123 private TestAnnotationInfo getAnnotationByMask(String annotationName, int mask) { 124 List<TestAnnotationInfo.Pair> pairs = new ArrayList<>(); 125 for (int i = 0; i < elementValues.length; ++i) { 126 if ((mask & (1 << i)) != 0) { 127 pairs.add(elementValues[i]); 128 } 129 } 130 return new TestAnnotationInfo( 131 annotationName, 132 getRetentionPolicy(annotationName), 133 pairs.toArray(new TestAnnotationInfo.Pair[pairs.size()])); 134 } 135 136 /** 137 * Class represents annotations which will be applied to one method. 138 */ 139 public static class TestAnnotationInfos { 140 public final List<TestAnnotationInfo> annotations; 141 142 public TestAnnotationInfos(List<TestAnnotationInfo> a) { 143 this.annotations = a; 144 } 145 146 public void annotate(TestCase.TestMemberInfo memberInfo) { 147 annotations.forEach(memberInfo::addAnnotation); 148 } 149 } 150 151 /** 152 * Convenience method to group test cases. 153 * Increases speed of tests. 154 */ 155 public List<List<TestAnnotationInfos>> groupAnnotations(List<TestAnnotationInfos> annotations) { 156 List<List<TestAnnotationInfos>> groupedAnnotations = new ArrayList<>(); 157 int size = 32; 158 List<TestAnnotationInfos> current = null; 159 for (TestAnnotationInfos infos : annotations) { 160 if (current == null || current.size() == size) { 161 current = new ArrayList<>(); 162 groupedAnnotations.add(current); 163 } 164 current.add(infos); 165 } 166 return groupedAnnotations; 167 } 168 169 public List<TestAnnotationInfos> getAllCombinationsOfAnnotations() { 170 List<TestAnnotationInfos> combinations = new ArrayList<>(); 171 for (Annotations annotationName1 : Annotations.values()) { 172 List<TestAnnotationInfo> list = IntStream.of(elementValuesCombinations) 173 .mapToObj(e -> getAnnotationByMask(annotationName1.getAnnotationName(), e)) 174 .collect(Collectors.toList()); 175 // add cases with a single annotation 176 combinations.addAll(list.stream() 177 .map(Collections::singletonList) 178 .map(TestAnnotationInfos::new) 179 .collect(Collectors.toList())); 180 181 // add cases with a repeatable annotation 182 for (Annotations annotationName2 : Annotations.values()) { 183 if (annotationName1 == annotationName2 && !annotationName1.isRepeatable()) { 184 continue; 185 } 186 TestAnnotationInfo annotation2 = createSomeAnnotation(annotationName2.getAnnotationName()); 187 for (TestAnnotationInfo annotation1 : list) { 188 List<TestAnnotationInfo> list1 = new ArrayList<>(); 189 Collections.addAll(list1, annotation1, annotation2); 190 combinations.add(new TestAnnotationInfos(list1)); 191 } 192 } 193 } 194 return combinations; 195 } 196 197 protected RetentionPolicy getRetentionPolicy(String name) { 198 if (name.contains("Visible")) { 199 return RetentionPolicy.RUNTIME; 200 } else if (name.contains("Invisible")) { 201 return RetentionPolicy.CLASS; 202 } 203 throw new IllegalArgumentException(name); 204 } 205 206 protected long countNumberOfAttributes(Attribute[] attrs, 207 Class<? extends Attribute> clazz) { 208 return Stream.of(attrs) 209 .filter(clazz::isInstance) 210 .count(); 211 } 212 213 public void test() throws TestFailedException { 214 try { 215 List<TestCase> testCases = generateTestCases(); 216 for (int i = 0; i < testCases.size(); ++i) { 217 TestCase testCase = testCases.get(i); 218 String source = testCase.generateSource(); 219 Path sourceFile = Paths.get(getClass().getSimpleName() + i + ".java"); 220 addTestCase(sourceFile.toAbsolutePath().toString()); 221 writeToFileIfEnabled(sourceFile, source); 222 echo("Testing: " + sourceFile.toString()); 223 try { 224 test(testCase, compile(source).getClasses()); 225 } catch (Exception e) { 226 addFailure(e); 227 } 228 } 229 } catch (RuntimeException | IOException e) { 230 addFailure(e); 231 } finally { 232 checkStatus(); 233 } 234 } 235 236 public enum Annotations { 237 RUNTIME_INVISIBLE_REPEATABLE("RuntimeInvisibleRepeatable", true), 238 RUNTIME_INVISIBLE_NOT_REPEATABLE("RuntimeInvisibleNotRepeatable", false), 239 RUNTIME_VISIBLE_REPEATABLE("RuntimeVisibleRepeatable", true), 240 RUNTIME_VISIBLE_NOT_REPEATABLE("RuntimeVisibleNotRepeatable", false); 241 242 private final String annotationName; 243 private final boolean isRepeatable; 244 245 Annotations(String annotationName, boolean isRepeatable) { 246 this.annotationName = annotationName; 247 this.isRepeatable = isRepeatable; 248 } 249 250 public String getAnnotationName() { 251 return annotationName; 252 } 253 254 public boolean isRepeatable() { 255 return isRepeatable; 256 } 257 } 258} 259