Home | History | Annotate | Download | only in classfile
      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    */
    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    */
    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    */
    121   private String nameClass(String s) {
    122     return CLASS_FILE_BASE + s;
    123   }
    124 
    125   /**
    126    * Prepends {@link #INDEX_FILE_BASE} to s.
    127    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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