• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package annotations.tests.classfile;
2 
3 /*>>>
4 import org.checkerframework.checker.nullness.qual.*;
5 */
6 
7 import java.io.File;
8 import java.io.FileInputStream;
9 import java.io.FileOutputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 
13 import junit.framework.TestCase;
14 import junit.framework.TestResult;
15 import junit.framework.TestSuite;
16 
17 import org.objectweb.asm.ClassReader;
18 
19 import annotations.Annotation;
20 import annotations.AnnotationFactory;
21 import annotations.el.AScene;
22 import annotations.io.IndexFileParser;
23 import annotations.io.IndexFileWriter;
24 import annotations.io.classfile.ClassFileReader;
25 import annotations.io.classfile.ClassFileWriter;
26 import annotations.tests.classfile.foo.A;
27 
28 /**
29  * This class is the testing framework for the class file/index file
30  * annotations converter.  To add a new test,
31  * <ul>
32  *  <li>add the class name to array {@link #allTests}
33  *  <li>place two files in directory {@link #CLASS_FILE_BASE}:
34  *    a .class file (for the unannotated version of the class),
35  *    an _Expected.class file (for the annotated version of the class).
36  *  <li>place two files in directory {@link #INDEX_FILE_BASE}:
37  *    a .java source file (this is not used by the tests -- it is only for
38  *      documentation, and is helpful when creating the test files),
39  *    a .jaif index file.
40  *  <li>Add a <code>testc*()</code> method to test against class file and a
41  *    <code>testi*()</code> method to test against index file; this is just so
42  *     that JUnit has an accurate count of all tests.
43  * </ul>
44  *
45  * Two types of tests are performed:
46  * <ul>
47  *   <li>"c" tests that call testAgainstClass:
48  *      Read the annotations from <code>name.jaif</code>, insert them into
49  *      <code>name.class</code>, write the results to a temporary file
50  *      (name_Generated.class), and compare this generated class file with
51  *      <code>name_Expected.class</code>, asserting that they have the same
52  *      annotations.
53  *   <li>"i" tests that call testAgainstIndexFile:
54  *      Read the annotations from the generated class file, and check them
55  *      against the annotations from the index file.
56  * </ul>
57  */
58 public class AnnotationsTest extends TestCase {
59 
60   /**
61    * The directory in which to find the index files to test.
62    */
63   private static final String INDEX_FILE_BASE =
64     "test/annotations/tests/classfile/cases/";
65 
66   /**
67    * The directory in which to find the class files (both .class and _Generated.class)
68    * to test.
69    */
70   private static final String CLASS_FILE_BASE =
71     "test/annotations-expected/tests/classfile/cases/";
72 
73   /**
74    * An array of all the classes to test.  For each name in this array, there
75    * must be a corresponding .jaif file in {@link #INDEX_FILE_BASE} and
76    * .class and _Expected.class files in {@link #CLASS_FILE_BASE}
77    */
78   public static final String[] allTests = {
79     "TestClassEmpty",
80     "TestClassNonEmpty",
81     "TestFieldSimple",
82     "TestFieldGeneric",
83     "TestLocalVariable",
84     "TestLocalVariableA",
85     "TestLocalVariableGenericArray",
86     "TestTypecast",
87     "TestTypecastGenericArray",
88     "TestTypeTest",
89     "TestObjectCreation",
90     "TestObjectCreationGenericArray",
91     "TestMethodReceiver",
92     "TestMethodReturnTypeGenericArray"
93   };
94 
95   /**
96    * Constructs a new <code>AnnotationsTest</code> with the given name.
97    *
98    * @param s the name of this test case
99    */
AnnotationsTest(String s)100   public AnnotationsTest(String s) {
101     super(s);
102   }
103 
104   /**
105    * Runs all the tests in {@link #allTests} and displays the failure and error
106    * counts.
107    */
main(String[] args)108   public static void main(String[] args) {
109     TestSuite suite = new TestSuite(AnnotationsTest.class);
110     TestResult result = new TestResult();
111     suite.run(result);
112     System.out.println(
113         "AnnotationsTests ran with " + result.failureCount() + " failures and "
114         + result.errorCount() + " errors. (" + result.runCount()
115         + " successes.)");
116   }
117 
118   /**
119    * Prepends {@link #CLASS_FILE_BASE} to s.
120    */
nameClass(String s)121   private String nameClass(String s) {
122     return CLASS_FILE_BASE + s;
123   }
124 
125   /**
126    * Prepends {@link #INDEX_FILE_BASE} to s.
127    */
nameIndex(String s)128   private String nameIndex(String s) {
129     return INDEX_FILE_BASE + s;
130   }
131 
132   /**
133    * Writes out scene to filename as an index file.
134    *
135    * @param filename the file to write to
136    * @param scene the scene to write out
137    */
writeScene(String filename, AScene scene)138   private void writeScene(String filename, AScene scene) {
139     try {
140       IndexFileWriter.write(scene, filename);
141     } catch (Exception e) {
142       System.err.println("caught exception: ");
143       e.printStackTrace();
144       fail();
145     }
146   }
147 
148   /**
149    * Reads in the annotations from filename, an index file, into scene.
150    *
151    * @param filename the index file to read from
152    * @param scene the scene to write out to
153    */
readScene(String filename, AScene scene)154   private void readScene(String filename, AScene scene) {
155     try {
156       IndexFileParser.parseFile(filename, scene);
157     } catch (Exception e) {
158       System.err.println("caught exception: ");
159       e.printStackTrace();
160       fail("caught exception: " + e.toString());
161     }
162   }
163 
164   /**
165    * Reads in the class file from the given filename, inserts the annotations
166    * from scene, and writes out the result into the same file.
167    *
168    * @param filename the class file to insert annotations into
169    * @param scene the scene that contains annotations to be inserted
170    * @param overwrite whether to overwrite existing annotations
171    */
writeClass(String filename, AScene scene, boolean overwrite)172   private void writeClass(String filename,
173       AScene scene, boolean overwrite) {
174     writeClass(filename, filename, scene, overwrite);
175   }
176 
177   /**
178    * Like {@link #writeClass(String, AScene, boolean)}, except the class will be read from and written to
179    * different files.
180    *
181    * @param oldFileName the class file to read from
182    * @param newFileName the class file to write to
183    * @param scene the scene that contains annotations to be inserted
184    * @param overwrite whether to overwrite existing annotations
185    */
writeClass( String oldFileName, String newFileName, AScene scene, boolean overwrite)186   private void writeClass(
187       String oldFileName,
188       String newFileName,
189       AScene scene,
190       boolean overwrite) {
191     try {
192       ClassFileWriter.insert(
193           scene,
194           new FileInputStream(oldFileName),
195           new FileOutputStream(newFileName),
196           overwrite);
197     } catch (Throwable e) {
198       System.err.printf("caught exception in writeClass(oldFileName=%s, newFileName=%s, ...):%n",
199                         oldFileName, newFileName);
200       e.printStackTrace();
201       fail();
202     }
203   }
204 
205   /**
206    * Reads in the annotations from the class file at filename and inserts them
207    * into scene.
208    *
209    * @param filename the class file to read from
210    * @param scene the scene to write to
211    */
readClass(String filename, AScene scene)212   private void readClass(String filename,
213       AScene scene) {
214     try {
215       ClassFileReader.read(scene, filename);
216     } catch (Exception e) {
217       System.err.printf("caught exception while reading %s:%n", new File(filename).getAbsolutePath());
218       e.printStackTrace();
219       fail();
220     }
221   }
222 
223   /**
224    * Creates scene from the annotations in the given index file.
225    *
226    * @param indexFile the index file to create a scene from
227    * @return the scene created from the given index file
228    */
createScene(String indexFile)229   private AScene createScene(String indexFile) {
230     AScene scene =
231       new AScene();
232     readScene(indexFile, scene);
233     return scene;
234   }
235 
236   /**
237    * Asserts that the annotations in two class files match.
238    * This method will cause this test to fail if there
239    * is a mismatch in annotations, or if there is a mismatch in either field
240    * or method information that means these classes cannot reasonably be
241    * compared.
242    *
243    * @param correctClass the file name of the correct version of the class
244    * @param generatedClass the file name of the version of the class being tested
245    */
assertClassAnnotations(String correctClass, String generatedClass)246   private void assertClassAnnotations(String correctClass, String generatedClass) {
247 
248     try {
249       InputStream correctIs = new FileInputStream(correctClass);
250 
251       InputStream generatedIs = new FileInputStream(generatedClass);
252 
253       ClassReader crCorrect = new ClassReader(correctIs);
254       ClassReader crGenerated = new ClassReader(generatedIs);
255 
256       AnnotationVerifier av = new AnnotationVerifier();
257 
258       crCorrect.accept(av.originalVisitor(), false);
259       crGenerated.accept(av.newVisitor(), false);
260 
261       try {
262         av.verify();
263       } catch (AnnotationVerifier.AnnotationMismatchException e) {
264         String message = String.format("assertClassAnnotations (consider running javap on the two .class files):%n  correctClass %s%n  generatedClass %s%n%s", correctClass, generatedClass, e.toString());
265         System.out.println();
266         System.out.println(message);
267         av.verifyPrettyPrint();
268         System.out.println(message);
269         System.out.println();
270         fail(message);
271       }
272 
273     } catch (IOException e) {
274       fail("IOException caught: " + e);
275     }
276   }
277 
278   /**
279    * Runs a test that:
280    *  <li> reads annotations from indexFileName,
281    *  <li> inserts them into baseClassName.class,
282    *  <li> writes the result out to baseClassName_Generated.class, and
283    *  <li> asserts that the results written out match baseClassName_Expected.class
284    */
testAgainstClass(String indexFileName, String baseClassName)285   private void testAgainstClass(String indexFileName, String baseClassName) {
286     String base = baseClassName + ".class";
287     String expected = baseClassName + "_Expected.class";
288     String generated = baseClassName + "_Generated.class";
289 
290     AScene scene = new AScene();
291 
292     // read in annotations from index file to scene
293     readScene(indexFileName, scene);
294 
295     // read in class from base, merge annotations from scene and
296     //  write out to generated
297     writeClass(base, generated, scene, true);
298 
299     // assert that generated class has same annotations as expected class
300     assertClassAnnotations(expected, generated);
301   }
302 
303   /**
304    * Runs a test that:
305    *  <li> reads annotations from indexFileName,
306    *  <li> inserts them into className
307    *  <li> writes results out to a temporary class file
308    *  <li> reads annotations from that class file, and
309    *  <li> asserts that results written out match the annotations in the index file.
310    */
testAgainstIndexFile(String indexFileName, String className)311   private void testAgainstIndexFile(String indexFileName, String className) {
312     AScene correctScene = createScene(indexFileName);
313 
314     String basename = className;
315     if (basename.endsWith(".class")) {
316       basename = basename.substring(0, basename.length() - 6);
317     }
318 
319     File tempFile = new File(basename+"_temp.class");
320 
321     writeClass(className, tempFile.toString(), correctScene, true);
322 
323     AScene generatedScene = new AScene();
324 
325     readClass(tempFile.toString(), generatedScene);
326 
327     correctScene.prune();
328     generatedScene.prune();
329 
330     if (!correctScene.equals(generatedScene)) {
331       String fname1 = className+"-from-indexfile.txt";
332       String fname2 = className+"-via-classfile-scene.txt";
333       writeScene(fname1, correctScene);
334       writeScene(fname2, generatedScene);
335       fail(String.format("For annotations read from %s :%n  After writing to class file and re-reading, result differed.%n  Scene read from index file is in %s .%n  Scene generated from class file is in %s .%n  Also consider running javap -v on %s .%n", indexFileName, fname1, fname2, tempFile));
336     }
337 
338     tempFile.delete();
339 
340   }
341 
342   /**
343    * Runs both types of tests (against class file and index file), on all
344    * classes specified by {@link #allTests}
345    */
testAll()346   public void testAll() throws Exception {
347 //    for (String s : allTests) {
348 //      testAgainstIndexFile(nameIndex(s + ".jaif"), nameClass(s+".class"));
349 //      testAgainstClass(nameIndex(s + ".jaif"), nameClass(s));
350 //    }
351   }
352 
353   /**
354    * Runs a test on class files for package-info.
355    */
testcPackage()356   public void testcPackage() {
357     testAgainstClass(nameIndex("package-info.jaif"),
358         nameClass("package-info"));
359   }
360 
361   /**
362    * Runs a test on index files for package-info.
363    */
testiPackage()364   public void testiPackage() {
365     testAgainstIndexFile(nameIndex("package-info.jaif"),
366         nameClass("package-info.class"));
367   }
368 
369   /**
370    * Runs a test on class files for TestClassEmpty.
371    */
testcClassEmpty()372   public void testcClassEmpty() {
373     testAgainstClass(nameIndex("TestClassEmpty.jaif"),
374         nameClass("TestClassEmpty"));
375   }
376 
377   /**
378    * Runs a test on index files for TestClassEmpty.
379    */
testiClassEmpty()380   public void testiClassEmpty() {
381     testAgainstIndexFile(nameIndex("TestClassEmpty.jaif"),
382         nameClass("TestClassEmpty.class"));
383   }
384 
385   /**
386    * Runs a test on class files for TestClassNonEmpty.
387    */
testcClassNonEmpty()388   public void testcClassNonEmpty() {
389     testAgainstClass(nameIndex("TestClassNonEmpty.jaif"),
390         nameClass("TestClassNonEmpty"));
391   }
392 
393   /**
394    * Runs a test on index files for TestClassNonEmpty.
395    */
testiClassNonEmpty()396   public void testiClassNonEmpty() {
397     testAgainstIndexFile(nameIndex("TestClassNonEmpty.jaif"),
398         nameClass("TestClassNonEmpty.class"));
399   }
400 
401   /**
402    * Runs a test on class files for TestFieldSimple.
403    */
testcFieldSimple()404   public void testcFieldSimple() {
405     testAgainstClass(nameIndex("TestFieldSimple.jaif"),
406         nameClass("TestFieldSimple"));
407   }
408 
409   /**
410    * Runs a test on index files for TestFieldSimple.
411    */
testiFieldSimple()412   public void testiFieldSimple() {
413     testAgainstIndexFile(nameIndex("TestFieldSimple.jaif"),
414         nameClass("TestFieldSimple.class"));
415   }
416 
417   /**
418    * Runs a test on class files for TestFieldGeneric.
419    */
testcFieldGeneric()420   public void testcFieldGeneric() {
421     testAgainstClass(nameIndex("TestFieldGeneric.jaif"),
422         nameClass("TestFieldGeneric"));
423   }
424 
425   /**
426    * Runs a test on index files for TestFieldGeneric.
427    */
testiFieldGeneric()428   public void testiFieldGeneric() {
429     testAgainstIndexFile(nameIndex("TestFieldGeneric.jaif"),
430         nameClass("TestFieldGeneric.class"));
431   }
432 
433   /**
434    * Runs a test on class files for TestLocalVariable.
435    */
testcLocalVariable()436   public void testcLocalVariable() {
437     testAgainstClass(nameIndex("TestLocalVariable.jaif"),
438         nameClass("TestLocalVariable"));
439   }
440 
441   /**
442    * Runs a test on index files for TestLocalVariable.
443    */
testiLocalVariable()444   public void testiLocalVariable() {
445     testAgainstIndexFile(nameIndex("TestLocalVariable.jaif"),
446         nameClass("TestLocalVariable.class"));
447   }
448 
449   /**
450    * Runs a test on class files for TestLocalVariableA.
451    */
testcLocalVariableA()452   public void testcLocalVariableA() {
453     testAgainstClass(nameIndex("TestLocalVariableA.jaif"),
454         nameClass("TestLocalVariableA"));
455   }
456 
457   /**
458    * Runs a test on index files for TestLocalVariableA.
459    */
testiLocalVariableA()460   public void testiLocalVariableA() {
461     testAgainstIndexFile(nameIndex("TestLocalVariableA.jaif"),
462         nameClass("TestLocalVariableA.class"));
463   }
464 
465   /**
466    * Runs a test on class files for TestLocalVariableGenericArray.
467    */
testcLocalVariableGenericArray()468   public void testcLocalVariableGenericArray() {
469     testAgainstClass(nameIndex("TestLocalVariableGenericArray.jaif"),
470         nameClass("TestLocalVariableGenericArray"));
471   }
472 
473   /**
474    * Runs a test on index files for TestLocalVariableGenericArray.
475    */
testiLocalVariableGenericArray()476   public void testiLocalVariableGenericArray() {
477     testAgainstIndexFile(nameIndex("TestLocalVariableGenericArray.jaif"),
478         nameClass("TestLocalVariableGenericArray.class"));
479   }
480 
481   /**
482    * Runs a test on class files for TestTypecast.
483    */
testcTypecast()484   public void testcTypecast() {
485     testAgainstClass(nameIndex("TestTypecast.jaif"),
486         nameClass("TestTypecast"));
487   }
488 
489   /**
490    * Runs a test on index files for TestTypecast.
491    */
testiTypecast()492   public void testiTypecast() {
493     testAgainstIndexFile(nameIndex("TestTypecast.jaif"),
494         nameClass("TestTypecast.class"));
495   }
496 
497   /**
498    * Runs a test on class files for TestTypecastGenericArray.
499    */
testcTypecastGenericArray()500   public void testcTypecastGenericArray() {
501     testAgainstClass(nameIndex("TestTypecastGenericArray.jaif"),
502         nameClass("TestTypecastGenericArray"));
503   }
504 
505   /**
506    * Runs a test on index files for TestTypecastGenericArray.
507    */
testiTypecastGenericArray()508   public void testiTypecastGenericArray() {
509     testAgainstIndexFile(nameIndex("TestTypecastGenericArray.jaif"),
510         nameClass("TestTypecastGenericArray.class"));
511   }
512 
513   /**
514    * Runs a test on class files for TestTypeTest.
515    */
testcTypeTest()516   public void testcTypeTest() {
517     testAgainstClass(nameIndex("TestTypeTest.jaif"),
518         nameClass("TestTypeTest"));
519   }
520 
521   /**
522    * Runs a test on index files for TestTypeTest.
523    */
testiTypeTest()524   public void testiTypeTest() {
525     testAgainstIndexFile(nameIndex("TestTypeTest.jaif"),
526         nameClass("TestTypeTest.class"));
527   }
528 
529   /**
530    * Runs a test on class files for TestObjectCreation.
531    */
testcObjectCreation()532   public void testcObjectCreation() {
533     testAgainstClass(nameIndex("TestObjectCreation.jaif"),
534         nameClass("TestObjectCreation"));
535   }
536 
537   /**
538    * Runs a test on index files for TestObjectCreation.
539    */
testiObjectCreation()540   public void testiObjectCreation() {
541     testAgainstIndexFile(nameIndex("TestObjectCreation.jaif"),
542         nameClass("TestObjectCreation.class"));
543   }
544 
545   /**
546    * Runs a test on class files for TestObjectCreationGenericArray.
547    */
testcObjectCreationGenericArray()548   public void testcObjectCreationGenericArray() {
549     testAgainstClass(nameIndex("TestObjectCreationGenericArray.jaif"),
550         nameClass("TestObjectCreationGenericArray"));
551   }
552 
553   /**
554    * Runs a test on index files for TestObjectCreationGenericArray.
555    */
testiObjectCreationGenericArray()556   public void testiObjectCreationGenericArray() {
557     testAgainstIndexFile(nameIndex("TestObjectCreationGenericArray.jaif"),
558         nameClass("TestObjectCreationGenericArray.class"));
559   }
560 
561   /**
562    * Runs a test on class files for TestMethodReceiver.
563    */
testcMethodReceiver()564   public void testcMethodReceiver() {
565     testAgainstClass(nameIndex("TestMethodReceiver.jaif"),
566         nameClass("TestMethodReceiver"));
567   }
568 
569   /**
570    * Runs a test on index files for TestMethodReceiver.
571    */
testiMethodReceiver()572   public void testiMethodReceiver() {
573     testAgainstIndexFile(nameIndex("TestMethodReceiver.jaif"),
574         nameClass("TestMethodReceiver.class"));
575   }
576 
577   /**
578    * Runs a test on class files for TestMethodReturnTypeGenericArray.
579    */
testcMethodReturnTypeGenericArray()580   public void testcMethodReturnTypeGenericArray() {
581     testAgainstClass(nameIndex("TestMethodReturnTypeGenericArray.jaif"),
582         nameClass("TestMethodReturnTypeGenericArray"));
583   }
584 
585   /**
586    * Runs a test on index files for TestMethodReturnTypeGenericArray.
587    */
testiMethodReturnTypeGenericArray()588   public void testiMethodReturnTypeGenericArray() {
589     testAgainstIndexFile(nameIndex("TestMethodReturnTypeGenericArray.jaif"),
590         nameClass("TestMethodReturnTypeGenericArray.class"));
591   }
592 
593 //   // Call javap programmatically.
594 //   public static void javap(InputStream is, PrintStream ps) {
595 //     JavapEnvironment env = new JavapEnvironment();
596 //     PrintWriter pw = new PrintWriter(ps);
597 //     JavapPrinter javapPrinter = new JavapPrinter(is, pw, env);
598 //     javapPrinter.print();
599 //     pw.flush();
600 //   }
601 
602 }
603