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 }