View Javadoc

    1   /*
    2    * Copyright (c) 2001-2003 The XDoclet team
    3    * All rights reserved.
    4    */
    5   package xjavadoc.codeunit;
    6
    7   import junit.framework.TestCase;
    8   import xjavadoc.*;
    9   import xjavadoc.filesystem.ReaderFile;
    10
    11  import java.util.Iterator;
    12  import java.util.StringTokenizer;
    13  import java.io.StringWriter;
    14  import java.io.File;
    15  import java.io.Reader;
    16
    17  /***
    18   * CodeTestCase is a JUnit extension that will let you compare two sources
    19   * (typically one we keep as test data and a generated one) on the API level or
    20   * on the abstract syntax tree (AST) level. This is a lot more powerful than
    21   * comparing on a character by character basis, because it's only "what matters"
    22   * that is compared.
    23   *
    24   * @author    Aslak Hellesøy
    25   * @created   24. februar 2003
    26   */
    27  public abstract class CodeTestCase extends TestCase
    28  {
    29      public CodeTestCase() {
    30          super();
    31      }
    32
    33      public static void assertAstEqualsDir( File expectedDir, File actualDir ) {
    34          if( !expectedDir.isDirectory() ) {
    35              fail(expectedDir.getAbsolutePath() + " - should have been a directory");
    36          }
    37
    38          if( !actualDir.isDirectory() ) {
    39              fail(actualDir.getAbsolutePath() + " - should have been a directory");
    40          }
    41
    42          File[] expectedChildren =  expectedDir.listFiles();
    43
    44          for (int i = 0; i < expectedChildren.length; i++) {
    45              File expectedChild = expectedChildren[i];
    46              File actualChild = getActualChild(actualDir, expectedChild );
    47
    48              if( !actualChild.exists() ) {
    49                  fail("File should have existed: " + actualChild.getAbsolutePath());
    50              }
    51              if( (expectedChild.isDirectory() && !actualChild.isDirectory()) || (!expectedChild.isDirectory() && actualChild.isDirectory()) ) {
    52                  fail("Incompatible file types: " + expectedChild.getAbsolutePath() + "," + actualChild.getAbsolutePath());
    53              }
    54
    55              if( expectedChild.isDirectory() ) {
    56                  assertAstEqualsDir(expectedChild, actualChild );
    57              } else if( expectedChild.getName().endsWith(".java") ) {
    58                  System.out.println("Comparing " + expectedChild.getAbsolutePath());
    59                  assertAstEquals(expectedChild, actualChild);
    60              } else {
    61                  System.out.println("Ignoring non java file: " + expectedChild.getAbsolutePath());
    62              }
    63          }
    64      }
    65
    66      private static File getActualChild(File actualDir, File expectedChild) {
    67          return new File(actualDir, expectedChild.getName());
    68      }
    69
    70
    71      /***
    72  	 * Compares both API and AST. Equivalent to calling {@link #assertAstEquals}
    73  	 * and {@link #assertApiEquals}.
    74  	 *
    75  	 * @param expected  the expected source
    76  	 * @param actual    the actual source
    77  	 */
    78  	public static void assertEquals( File expected, File actual )
    79  	{
    80  		assertAstEquals( expected, actual );
    81  		assertApiEquals( expected, actual );
    82  	}
    83
    84  	public static void assertEquals( Reader expected, Reader actual )
    85  	{
    86  		assertAstEquals( expected, actual );
    87  		assertApiEquals( expected, actual );
    88  	}
    89
    90  	/***
    91  	 * Asserts (tests) that the ASTs of two sources are equal. Does not compare the
    92  	 * contents (tokens) of the nodes, and is forgiving with respect to those.
    93  	 *
    94  	 * @param expected  the expected source
    95  	 * @param actual    the actual source
    96  	 */
    97  	public static void assertAstEquals( File expected, File actual )
    98  	{
    99          checkNotDir(expected, actual);
    100
    101         SourceClass expectedClass = new SourceClass( new XJavaDoc(), expected, true, new XTagFactory() );
    102 		SourceClass actualClass = new SourceClass( new XJavaDoc(), actual, true, new XTagFactory() );
    103
    104 		assertAstEquals( expectedClass.getCompilationUnit(), actualClass.getCompilationUnit() );
    105 	}
    106
    107 	public static void assertAstEquals( Reader expected, Reader actual )
    108 	{
    109         SourceClass expectedClass = new SourceClass( new XJavaDoc(), new ReaderFile(expected), true, new XTagFactory(), null );
    110 		SourceClass actualClass = new SourceClass( new XJavaDoc(), new ReaderFile(actual), true, new XTagFactory(), null );
    111
    112 		assertAstEquals( expectedClass.getCompilationUnit(), actualClass.getCompilationUnit() );
    113 	}
    114     /***
    115 	 * Asserts (tests) that the APIs of two sources are equal. Does not go into the
    116 	 * method bodies to see if the implementation is equal, and is therefore more
    117 	 * relaxed than assertAstEquals.
    118 	 *
    119 	 * @param expected  the expected source
    120 	 * @param actual    the actual source
    121 	 */
    122 	public static void assertApiEquals( File expected, File actual )
    123 	{
    124         checkNotDir(expected, actual);
    125 		SourceClass expectedClass = new SourceClass( new XJavaDoc(), expected, false, new XTagFactory() );
    126 		SourceClass actualClass = new SourceClass( new XJavaDoc(), actual, false, new XTagFactory() );
    127
    128 		assertApiEquals( expectedClass, actualClass );
    129 	}
    130
    131 	public static void assertApiEquals( Reader expected, Reader actual )
    132 	{
    133 		SourceClass expectedClass = new SourceClass( new XJavaDoc(), new ReaderFile(expected), false, new XTagFactory(), null );
    134 		SourceClass actualClass = new SourceClass( new XJavaDoc(), new ReaderFile(actual), false, new XTagFactory(), null );
    135
    136 		assertApiEquals( expectedClass, actualClass );
    137 	}
    138
    139     private static void checkNotDir(File expected, File actual) {
    140         if( expected.isDirectory() ) {
    141             fail(expected.getAbsolutePath() + " - should not have been a directory");
    142         }
    143
    144         if( actual.isDirectory() ) {
    145             fail(actual.getAbsolutePath() + " - should not have been a directory");
    146         }
    147     }
    148
    149 	private static void assertAstEquals( SimpleNode expected, SimpleNode actual )
    150 	{
    151 		// Verify that we have the same AST type
    152 		boolean sameNodeType = expected.getType().equals( actual.getType() );
    153 		// Verify that we have the same number of children
    154 		boolean sameNumberOfChildren = expected.jjtGetNumChildren() == actual.jjtGetNumChildren();
    155
    156 		if( !sameNodeType || !sameNumberOfChildren )
    157 		{
    158 			// Something is not equal...
    159 			StringWriter expectedWriter = new StringWriter();
    160
    161 			NodePrinter.print( expected, expectedWriter );
    162
    163 			StringWriter actualWriter = new StringWriter();
    164
    165 			NodePrinter.print( actual, actualWriter );
    166
    167 			// This will always fail. -But we get a nice filtered diff.
    168 			assertEquals( expectedWriter.toString(), expectedWriter.toString(), actualWriter.toString() );
    169 		}
    170
    171 		for( int i = 0; i < expected.jjtGetNumChildren(); i++ )
    172 		{
    173 			xjavadoc.SimpleNode expectedChild = ( xjavadoc.SimpleNode ) expected.jjtGetChild( i );
    174 			xjavadoc.SimpleNode actualChild = ( xjavadoc.SimpleNode ) actual.jjtGetChild( i );
    175
    176 			assertAstEquals( expectedChild, actualChild );
    177 		}
    178 	}
    179
    180 	private static void assertApiEquals( SourceClass expected, SourceClass actual )
    181 	{
    182 		assertEquals( "Package names should be equal", expected.getContainingPackage().getName(), actual.getContainingPackage().getName() );
    183 		assertModifiersEqual( "Class modifiers should be equal", expected, actual );
    184 		assertNameEquals( "Class names should be equal", expected, actual );
    185 		assertSuperclassEquals( expected, actual );
    186 		assertInterfacesEqual( expected, actual );
    187 		assertFieldsEqual( expected, actual );
    188 		assertConstructorsEqual( expected, actual );
    189 		assertMethodsEqual( expected, actual );
    190 	}
    191
    192 	private static void assertFieldsEqual( XClass expected, XClass actual )
    193 	{
    194 		assertEquals( "Number of fields should be equal", expected.getFields().size(), actual.getFields().size() );
    195
    196 		Iterator expectedFields = expected.getFields().iterator();
    197 		Iterator actualFields = actual.getFields().iterator();
    198
    199 		while( expectedFields.hasNext() )
    200 		{
    201 			XField expectedField = ( XField ) expectedFields.next();
    202 			XField actualField = ( XField ) actualFields.next();
    203
    204 			assertFieldEquals( expectedField, actualField );
    205 		}
    206 	}
    207
    208 	private static void assertConstructorsEqual( XClass expected, XClass actual )
    209 	{
    210 		assertEquals( "Number of constructors should be equal", expected.getConstructors().size(), actual.getConstructors().size() );
    211
    212 		Iterator expectedConstructors = expected.getConstructors().iterator();
    213 		Iterator actualConstructors = actual.getConstructors().iterator();
    214
    215 		while( expectedConstructors.hasNext() )
    216 		{
    217 			XConstructor expectedConstructor = ( XConstructor ) expectedConstructors.next();
    218 			XConstructor actualConstructor = ( XConstructor ) actualConstructors.next();
    219
    220 			assertConstructorEquals( expectedConstructor, actualConstructor );
    221 		}
    222 	}
    223
    224 	private static void assertMethodsEqual( XClass expected, XClass actual )
    225 	{
    226 		assertEquals( "Number of methods should be equal", expected.getMethods().size(), actual.getMethods().size() );
    227
    228 		Iterator expectedMethods = expected.getMethods().iterator();
    229 		Iterator actualMethods = actual.getMethods().iterator();
    230
    231 		while( expectedMethods.hasNext() )
    232 		{
    233 			XMethod expectedMethod = ( XMethod ) expectedMethods.next();
    234 			XMethod actualMethod = ( XMethod ) actualMethods.next();
    235
    236 			assertMethodEquals( expectedMethod, actualMethod );
    237 		}
    238 	}
    239
    240 	private static void assertFieldEquals( XField expected, XField actual )
    241 	{
    242 		assertTypeEquals( "Field types should be equal", expected, actual );
    243 		assertNameEquals( "Field names should be equal", expected, actual );
    244 		assertModifiersEqual( "Field modifiers should be equal", expected, actual );
    245 	}
    246
    247 	private static void assertConstructorEquals( XConstructor expected, XConstructor actual )
    248 	{
    249 		assertNameEquals( "Constructor names should be equal", expected, actual );
    250 		assertModifiersEqual( "Constructor modifiers should be equal", expected, actual );
    251 		assertNameWithSignatureEquals( "Constructor signatures should be equal", expected, actual );
    252 		assertParametersEqual( "Constructor parameters should be equal", expected, actual );
    253 		assertThrownExceptionsEqual( "Constructor exceptions should be equal", expected, actual );
    254 	}
    255
    256 	private static void assertMethodEquals( XMethod expected, XMethod actual )
    257 	{
    258 		assertTypeEquals( "Method types should be equal", expected.getReturnType(), actual.getReturnType() );
    259 		assertNameEquals( "Method names should be equal", expected, actual );
    260 		assertModifiersEqual( "Method modifiers should be equal", expected, actual );
    261 		assertNameWithSignatureEquals( "Method signatures should be equal", expected, actual );
    262 		assertParametersEqual( "Method parameters should be equal", expected, actual );
    263 		assertThrownExceptionsEqual( "Method exceptions should be equal", expected, actual );
    264 	}
    265
    266 	private static void assertParameterEquals( XParameter expected, XParameter actual )
    267 	{
    268 		assertTypeEquals( "Parameter types should be equal", expected, actual );
    269 		assertNameEquals( "Parameter names should be equal", expected, actual );
    270 	}
    271
    272 	private static void assertTypeEquals( String msg, Type expected, Type actual )
    273 	{
    274 		assertEquals( msg, expected.getType().getQualifiedName(), actual.getType().getQualifiedName() );
    275 		assertEquals( msg, expected.getDimensionAsString(), actual.getDimensionAsString() );
    276 	}
    277
    278 	private static void assertNameEquals( String msg, Named expected, Named actual )
    279 	{
    280 		assertEquals( msg, expected.getName(), actual.getName() );
    281 	}
    282
    283 	private static void assertSuperclassEquals( SourceClass expected, SourceClass actual )
    284 	{
    285 		String expectedSuperclass = expected.getSuperclass() != null ? expected.getSuperclass().getQualifiedName() : null;
    286 		String actualSuperclass = actual.getSuperclass() != null ? actual.getSuperclass().getQualifiedName() : null;
    287
    288 		assertEquals( "Superclass is equal", expectedSuperclass, actualSuperclass );
    289 	}
    290
    291 	private static void assertInterfacesEqual( SourceClass expected, SourceClass actual )
    292 	{
    293 		assertEquals( "Implemented interfaces should be equal", expected.getDeclaredInterfaces().size(), actual.getDeclaredInterfaces().size() );
    294
    295 		Iterator declaredInterfaces = expected.getDeclaredInterfaces().iterator();
    296
    297 		while( declaredInterfaces.hasNext() )
    298 		{
    299 			XClass declaredInterface = ( XClass ) declaredInterfaces.next();
    300
    301 			assertTrue( "Implements " + declaredInterface.getQualifiedName(), actual.isA( declaredInterface.getQualifiedName() ) );
    302 		}
    303 	}
    304
    305 	private static void assertModifiersEqual( String msg, XProgramElement expected, XProgramElement actual )
    306 	{
    307 		assertEquals( msg, expected.getModifiers(), actual.getModifiers() );
    308 	}
    309
    310 	private static void assertNameWithSignatureEquals( String msg, XExecutableMember expected, XExecutableMember actual )
    311 	{
    312 		assertEquals( msg, expected.getNameWithSignature( false ), actual.getNameWithSignature( false ) );
    313 	}
    314
    315 	private static void assertParametersEqual( String msg, XExecutableMember expected, XExecutableMember actual )
    316 	{
    317 		assertEquals( msg, expected.getParameters().size(), actual.getParameters().size() );
    318
    319 		Iterator expectedParameters = expected.getParameters().iterator();
    320 		Iterator actualParameters = actual.getParameters().iterator();
    321
    322 		while( expectedParameters.hasNext() )
    323 		{
    324 			XParameter expectedParameter = ( XParameter ) expectedParameters.next();
    325 			XParameter actualParameter = ( XParameter ) actualParameters.next();
    326
    327 			assertParameterEquals( expectedParameter, actualParameter );
    328 		}
    329 	}
    330
    331 	private static void assertThrownExceptionsEqual( String msg, XExecutableMember expected, XExecutableMember actual )
    332 	{
    333 		assertEquals( msg, expected.getThrownExceptions().size(), actual.getThrownExceptions().size() );
    334
    335 		Iterator expectedThrownExceptions = expected.getThrownExceptions().iterator();
    336
    337 		while( expectedThrownExceptions.hasNext() )
    338 		{
    339 			XClass expectedThrownException = ( XClass ) expectedThrownExceptions.next();
    340
    341 			assertTrue( "Throws " + expectedThrownException.getQualifiedName(), actual.throwsException( expectedThrownException.getQualifiedName() ) );
    342 		}
    343 	}
    344
    345 	/***
    346 	 * Returns the directory where this class is located, provided that it's not in
    347 	 * a jar. This is very useful for accessing the files you want to compare.
    348 	 *
    349 	 * @return   the directory where this class is located.
    350 	 */
    351 	protected File getDir()
    352 	{
    353 		return new File( getClass().getResource( "/" + getClass().getName().replace( '.', '/' ) + ".class" ).getFile() ).getParentFile();
    354 	}
    355
    356 	/***
    357 	 * Returns the root directory of the package hierarchy where this class is
    358 	 * located, provided that it's not in a jar. This is very useful for accessing
    359 	 * the files you want to compare.
    360 	 *
    361 	 * @return   the root directory.
    362 	 */
    363 	protected File getRootDir()
    364 	{
    365 		File dir = getDir();
    366 		StringTokenizer st = new StringTokenizer( getClass().getName(), "." );
    367
    368 		// foo.bar.Baz = 3 tokens, but only 2 "directories"
    369 		for( int i = 0; i < st.countTokens() - 1; i++ )
    370 		{
    371 			dir = dir.getParentFile();
    372 		}
    373 		return dir;
    374 	}
    375
    376     protected XJavaDoc getXJavaDoc() {
    377         return new XJavaDoc();
    378     }
    379 }