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 
24 import java.io.File;
25 import java.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.util.*;
29 import java.util.function.Function;
30 import java.util.function.Supplier;
31 import java.util.regex.*;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
34 
35 import com.sun.tools.classfile.*;
36 
37 /**
38  * The tests work as follows. Firstly, it looks through the test cases
39  * and extracts the appropriate compiled classes. Each test case contains
40  * a set of expected classes, methods and fields. Those class members must not have
41  * the Synthetic attribute, while other found classes, methods and fields must have
42  * the Synthetic attribute if they are not in the set of expected class members.
43  *
44  * Each test executes SyntheticTestDriver specifying the name of test cases and
45  * the number of expected synthetic classes. Each test class is annotated by
46  * annotations which contains non-synthetic class members.
47  *
48  * See the appropriate class for more information about a test case.
49  */
50 public class SyntheticTestDriver extends TestResult {
51 
52     private static final String ACC_SYNTHETIC = "ACC_SYNTHETIC";
53 
54     private final String testCaseName;
55     private final Map<String, ClassFile> classes;
56     private final Map<String, ExpectedClass> expectedClasses;
57 
main(String[] args)58     public static void main(String[] args)
59             throws TestFailedException, ConstantPoolException, IOException, ClassNotFoundException {
60         if (args.length != 1 && args.length != 2) {
61             throw new IllegalArgumentException("Usage: SyntheticTestDriver <class-name> [<number-of-synthetic-classes>]");
62         }
63         int numberOfSyntheticClasses = args.length == 1 ? 0 : Integer.parseInt(args[1]);
64         new SyntheticTestDriver(args[0]).test(numberOfSyntheticClasses);
65     }
66 
SyntheticTestDriver(String testCaseName)67     public SyntheticTestDriver(String testCaseName) throws IOException, ConstantPoolException, ClassNotFoundException {
68         Class<?> clazz = Class.forName(testCaseName);
69         this.testCaseName = testCaseName;
70         this.expectedClasses = Stream.of(clazz.getAnnotationsByType(ExpectedClass.class))
71                 .collect(Collectors.toMap(ExpectedClass::className, Function.identity()));
72         this.classes = new HashMap<>();
73         Path classDir = getClassDir().toPath();
74         Pattern filePattern = Pattern.compile(Pattern.quote(testCaseName.replace('.', File.separatorChar)) + ".*\\.class");
75         List<Path> paths = Files.walk(classDir)
76                 .map(p -> classDir.relativize(p.toAbsolutePath()))
77                 .filter(p -> filePattern.matcher(p.toString()).matches())
78                 .collect(Collectors.toList());
79         for (Path path : paths) {
80             String className = path.toString().replace(".class", "").replace(File.separatorChar, '.');
81             classes.put(className, readClassFile(classDir.resolve(path).toFile()));
82         }
83         if (classes.isEmpty()) {
84             throw new RuntimeException("Classes have not been found.");
85         }
86         boolean success = classes.entrySet().stream()
87                 .allMatch(e -> e.getKey().startsWith(testCaseName));
88         if (!success) {
89             classes.forEach((className, $) -> printf("Found class: %s\n", className));
90             throw new RuntimeException("Found classes are not from the test case : " + testCaseName);
91         }
92     }
93 
getMethodName(ClassFile classFile, Method method)94     private String getMethodName(ClassFile classFile, Method method)
95             throws ConstantPoolException, Descriptor.InvalidDescriptor {
96         String methodName = method.getName(classFile.constant_pool);
97         String parameters = method.descriptor.getParameterTypes(classFile.constant_pool);
98         return methodName + parameters;
99     }
100 
test(int expectedNumberOfSyntheticClasses)101     public void test(int expectedNumberOfSyntheticClasses) throws TestFailedException {
102         try {
103             addTestCase(testCaseName);
104             Set<String> foundClasses = new HashSet<>();
105 
106             int numberOfSyntheticClasses = 0;
107             for (Map.Entry<String, ClassFile> entry : classes.entrySet()) {
108                 String className = entry.getKey();
109                 ClassFile classFile = entry.getValue();
110                 foundClasses.add(className);
111                 if (testAttribute(
112                         classFile,
113                         () -> (Synthetic_attribute) classFile.getAttribute(Attribute.Synthetic),
114                         classFile.access_flags::getClassFlags,
115                         expectedClasses.keySet(),
116                         className,
117                         "Testing class " + className)) {
118                     ++numberOfSyntheticClasses;
119                 }
120                 ExpectedClass expectedClass = expectedClasses.get(className);
121                 Set<String> expectedMethods = expectedClass != null
122                         ? toSet(expectedClass.expectedMethods())
123                         : new HashSet<>();
124                 int numberOfSyntheticMethods = 0;
125                 Set<String> foundMethods = new HashSet<>();
126                 for (Method method : classFile.methods) {
127                     String methodName = getMethodName(classFile, method);
128                     foundMethods.add(methodName);
129                     if (testAttribute(
130                             classFile,
131                             () -> (Synthetic_attribute) method.attributes.get(Attribute.Synthetic),
132                             method.access_flags::getMethodFlags,
133                             expectedMethods,
134                             methodName,
135                             "Testing method " + methodName + " in class "
136                                     + className)) {
137                         ++numberOfSyntheticMethods;
138                     }
139                 }
140                 checkContains(foundMethods, expectedMethods,
141                         "Checking that all methods of class " + className
142                                 + " without Synthetic attribute have been found");
143                 checkEquals(numberOfSyntheticMethods,
144                         expectedClass == null ? 0 : expectedClass.expectedNumberOfSyntheticMethods(),
145                         "Checking number of synthetic methods in class: " + className);
146 
147                 Set<String> expectedFields = expectedClass != null
148                         ? toSet(expectedClass.expectedFields())
149                         : new HashSet<>();
150                 int numberOfSyntheticFields = 0;
151                 Set<String> foundFields = new HashSet<>();
152                 for (Field field : classFile.fields) {
153                     String fieldName = field.getName(classFile.constant_pool);
154                     foundFields.add(fieldName);
155                     if (testAttribute(
156                             classFile,
157                             () -> (Synthetic_attribute) field.attributes.get(Attribute.Synthetic),
158                             field.access_flags::getFieldFlags,
159                             expectedFields,
160                             fieldName,
161                             "Testing field " + fieldName + " in class "
162                                     + className)) {
163                         ++numberOfSyntheticFields;
164                     }
165                 }
166                 checkContains(foundFields, expectedFields,
167                         "Checking that all fields of class " + className
168                                 + " without Synthetic attribute have been found");
169                 checkEquals(numberOfSyntheticFields,
170                         expectedClass == null ? 0 : expectedClass.expectedNumberOfSyntheticFields(),
171                         "Checking number of synthetic fields in class: " + className);
172             }
173             checkContains(foundClasses, expectedClasses.keySet(),
174                     "Checking that all classes have been found");
175             checkEquals(numberOfSyntheticClasses, expectedNumberOfSyntheticClasses,
176                     "Checking number of synthetic classes");
177         } catch (Exception e) {
178             addFailure(e);
179         } finally {
180             checkStatus();
181         }
182     }
183 
testAttribute(ClassFile classFile, Supplier<Synthetic_attribute> getSyntheticAttribute, Supplier<Set<String>> getAccessFlags, Set<String> expectedMembers, String memberName, String info)184     private boolean testAttribute(ClassFile classFile,
185                                Supplier<Synthetic_attribute> getSyntheticAttribute,
186                                Supplier<Set<String>> getAccessFlags,
187                                Set<String> expectedMembers, String memberName,
188                                String info) throws ConstantPoolException {
189         echo(info);
190         String className = classFile.getName();
191         Synthetic_attribute attr = getSyntheticAttribute.get();
192         Set<String> flags = getAccessFlags.get();
193         if (expectedMembers.contains(memberName)) {
194             checkNull(attr, "Member must not have synthetic attribute : "
195                     + memberName);
196             checkFalse(flags.contains(ACC_SYNTHETIC),
197                     "Member must not have synthetic flag : " + memberName
198                             + " in class : " + className);
199             return false;
200         } else {
201             return checkNull(attr, "Synthetic attribute should not be generated")
202                     && checkTrue(flags.contains(ACC_SYNTHETIC), "Member must have synthetic flag : "
203                                 + memberName + " in class : " + className);
204         }
205     }
206 
toSet(String[] strings)207     private Set<String> toSet(String[] strings) {
208         HashSet<String> set = new HashSet<>();
209         Collections.addAll(set, strings);
210         return set;
211     }
212 }
213