001: /* TestStmtCoverage.java */
002: package org.quilt.cover.stmt;
003:
004: import java.io.*;
005: import java.lang.Class;
006: import java.lang.reflect.*;
007: import java.net.*;
008:
009: // DEBUG
010: import java.lang.reflect.Field;
011: import java.lang.reflect.Method; // END
012:
013: import junit.framework.*;
014: import org.apache.bcel.generic.*;
015: import org.quilt.cl.*;
016: import org.quilt.cover.stmt.*;
017:
018: /**
019: * This is derived from org.quilt.cl.TestTransformer; it instruments
020: * the test-data classes and the classes synthesized by ClassFactory.
021: * All of the instrumented classes are run and checked to see that
022: * they produce normal output, despite the instrumentation ;-)
023: *
024: * @author <a href="jddixon@users.sourceforge.net">Jim Dixon</a>
025: */
026: public class TestStmtCoverage extends TestCase {
027:
028: private ControlFlowGraph cfg;
029:
030: /** Classpath. */
031: private URL[] cp = null; // this is built up below
032:
033: private String[] delegating = {
034: // NOTHING beyond standard defaults
035: };
036: // we want everything to be instrumented
037: private String[] include = { "test.data.", "AnonymousClass",
038: "AnonymousClass2Catches", "BasicLoad",
039: "ComplicatedConstructor", "ExceptionLoad",
040: "ExceptionThrow", "Finally", "Finally2Classes",
041: "InnerClass", "Looper", "NestedTryBlocks", "OddSwitches",
042: "PrivateClass", "StaticInit", "SuperClass", "SwitchLoad",
043: "Wimple" };
044: private String[] exclude = {
045: // NOTHING
046: };
047:
048: private GraphXformer spy;
049: private GraphXformer talker, talker2;
050:
051: private StmtRegistry stmtReg;
052:
053: private QuiltClassLoader qLoader = null;
054:
055: public TestStmtCoverage(String name) {
056: super (name);
057: }
058:
059: public void setUp() {
060: File sam1 = new File("target/test-data-classes/");
061: String fullPath1 = sam1.getAbsolutePath() + "/";
062: File sam2 = new File("target/classes");
063: String fullPath2 = sam2.getAbsolutePath() + "/";
064: File sam3 = new File("target/test-classes");
065: String fullPath3 = sam3.getAbsolutePath() + "/";
066: try {
067: // Terminating slash is required. Relative paths don't
068: // work.
069: URL[] samples = { new URL("file://" + fullPath1),
070: new URL("file://" + fullPath2),
071: new URL("file://" + fullPath3) };
072: cp = samples;
073: } catch (MalformedURLException e) {
074: e.printStackTrace();
075: fail("problem creating class path");
076: }
077: ClassLoader parent = ClassLoader.getSystemClassLoader();
078: qLoader = new QuiltClassLoader(cp, parent, // parent
079: delegating, // delegated classes
080: include, // being instrumented
081: exclude); // do NOT instrument
082:
083: // Graph Xformers /////////////////////////////////
084: spy = new GraphSpy();
085: qLoader.addGraphXformer(spy);
086:
087: // dumps graph BEFORE transformation
088: talker = new GraphTalker();
089: qLoader.addGraphXformer(talker);
090:
091: // adds xformers for statement coverage to qLoader
092: // EXPERIMENT
093: stmtReg = (StmtRegistry) qLoader
094: .addQuiltRegistry("org.quilt.cover.stmt.StmtRegistry");
095: // old way
096: //stmtReg = new StmtRegistry(qLoader);
097: //qLoader.addQuiltRegistry(stmtReg);
098:
099: // dumps graph AFTER transformation
100: talker2 = new GraphTalker();
101: qLoader.addGraphXformer(talker2);
102: }
103:
104: // SUPPORT METHODS //////////////////////////////////////////////
105: private RunTest loadAsRunTest(String name) {
106: Class clazz = null;
107: try {
108: clazz = qLoader.loadClass(name);
109: // // DEBUG -- trying to get fields causes an error
110: // Field[] fields = clazz.getFields();
111: // StringBuffer fieldData = new StringBuffer();
112: // for (int k = 0; k < fields.length; k++)
113: // fieldData.append(" ").append(fields[k].toString())
114: // .append("\n");
115:
116: // Method[] methods = clazz.getMethods();
117: // StringBuffer methodData = new StringBuffer();
118: // for (int k = 0; k < methods.length; k++)
119: // methodData.append(" ").append(methods[k].toString())
120: // .append("\n");
121: //
122: // System.out.println("TestStmtRegistry: loading class " + name
123: // + "\nFIELDS (" + fields.length + ") :\n"
124: // + fieldData.toString()
125: // + "\nMETHODS (" + methods.length + ") :\n"
126: // + methodData.toString() );
127: // // END
128: } catch (ClassNotFoundException e) {
129: e.printStackTrace();
130: fail("exception loading " + name + " using loadClass");
131: }
132: RunTest rt = null;
133: try {
134: rt = (RunTest) clazz.newInstance();
135: } catch (InstantiationException e) {
136: fail("InstantiationException instantiating loaded class "
137: + name);
138: } catch (IllegalAccessException e) {
139: fail("IllegalAccessException instantiating loaded class "
140: + name);
141: } catch (ClassCastException e) {
142: fail("ClassCastException instantiating loaded class "
143: + name);
144: }
145: return rt;
146: }
147:
148: /**
149: * Check that the class has been properly instrumented by
150: * org.quilt.cover.stmt. At the moment this means only that the
151: * hitcount array, public static int[] q$$q is one of the class's
152: * fields. We also check that it has NOT been initialized,
153: * although we will eventually make sure that it has been.
154: */
155: private void checkInstrumentation(Object rt) {
156: String name = rt.getClass().getName();
157: System.out.println("checkInstrumentation for class " + name);
158: try {
159: Field qField = rt.getClass().getField("q$$q");
160: if (qField == null) {
161: System.out.println(name + " has no hit count array");
162: fail(name + " has NO hit count array");
163: } else
164: try {
165: int[] hitCounts = (int[]) qField.get(null);
166: assertNotNull("q$$q has not been initialized",
167: hitCounts);
168: } catch (IllegalAccessException e) {
169: e.printStackTrace();
170: }
171: // check version
172: qField = rt.getClass().getField("q$$qVer");
173: if (qField == null) {
174: System.out.println(name + " has no version field");
175: fail(name + " has NO version field");
176: } else
177: try {
178: int version = qField.getInt(qField);
179: assertEquals("q$$q has wrong version number", 0,
180: version);
181: } catch (IllegalAccessException e) {
182: e.printStackTrace();
183: }
184: // check StmtRegistry
185:
186: } catch (NoSuchFieldException e) {
187: fail(name + " has no q$$q field");
188: }
189: }
190:
191: // ACTUAL TESTS /////////////////////////////////////////////////
192: public void testGetReg() {
193: StmtRegistry regInLoader = (StmtRegistry) qLoader
194: .getRegistry("org.quilt.cover.stmt.StmtRegistry");
195: assertNotNull("qLoader StmtRegistry is null", regInLoader);
196: assertSame("qLoader has different StmtRegistry", stmtReg,
197: regInLoader);
198: }
199:
200: public void testLoader() {
201: Class a1 = null;
202: try {
203: a1 = qLoader.loadClass("AnonymousClass");
204: } catch (ClassNotFoundException e) {
205: e.printStackTrace();
206: fail("Error loading AnonymousClass using loadClass");
207: }
208: assertNotNull("qLoader returned null", a1);
209: } // END
210:
211: // /**
212: // * Test classes from src/test-data. All of these (should) have a
213: // * RunTest interface. They are loaded and then run to see that
214: // * they produce expected values.
215: // *
216: // * By and large the test inputs are primes greater than 2; in earlier
217: // * tests some failed, returned 0, and were judged successful because
218: // * 0 was the expected result.
219: // *
220: // * At the moment these are split into three groups, so that if they
221: // * fail we will see more results.
222: // */
223: public void testInvokeTestData() {
224: RunTest rt = loadAsRunTest("AnonymousClass");
225: checkInstrumentation(rt);
226: // AnonymousClass.runTest(x) returns x
227: assertEquals("AnonymousClass isn't working", 47, rt.runTest(47));
228:
229: rt = loadAsRunTest("BasicLoad");
230: checkInstrumentation(rt);
231: // BasicLoad.runTest(x) returns x*x
232: assertEquals("BasicLoad isn't working", 49, rt.runTest(7));
233:
234: rt = loadAsRunTest("ComplicatedConstructor");
235: checkInstrumentation(rt);
236: assertEquals("ComplicatedConstructor isn't working", 61, rt
237: .runTest(3));
238: rt = loadAsRunTest("ExceptionLoad");
239: checkInstrumentation(rt);
240: // ExceptionLoad.runTest(x) also returns x*x
241: assertEquals("ExceptionLoad isn't working", 121, rt.runTest(11));
242:
243: rt = loadAsRunTest("InnerClass");
244: checkInstrumentation(rt);
245: // InnerClass.runTest(x) also returns x*x
246: assertEquals("InnerClass isn't working", 9, rt.runTest(3));
247:
248: rt = loadAsRunTest("Looper");
249: assertEquals("Looper isn't working", 127008000, rt.runTest(5));
250:
251: // rt = loadAsRunTest("OddSwitches");
252: // // we like to play
253: // assertEquals( 91, rt.runTest(1001));
254: // assertEquals( 31, rt.runTest(3));
255: // assertEquals( 9, rt.runTest(9));
256: // assertEquals(101, rt.runTest(1005));
257: // assertEquals(-41, rt.runTest(-1));
258: // assertEquals( -3, rt.runTest(-51));
259: // assertEquals( 7, rt.runTest(-2));
260:
261: rt = loadAsRunTest("PrivateClass");
262: checkInstrumentation(rt);
263: // returns 4
264: assertEquals("PrivateClass isn't working", 4, rt.runTest(7));
265:
266: rt = loadAsRunTest("SuperClass");
267: checkInstrumentation(rt);
268: // returns 3*x
269: assertEquals("SuperClass isn't working", 21, rt.runTest(7));
270:
271: // This would normally not be here, it would be at a higher
272: // level in the testing process. But we are testing the
273: // registry itself. XXX Collect the string and extract
274: // run results from it.
275: String runResults = stmtReg.getReport();
276: System.out.println("\nQuilt coverage report:\n" + runResults);
277: } // END TESTDATA
278:
279: // SYNTHESIZED CLASSES //////////////////////////////////////////
280: // public void testSynth () {
281: // assertEquals ("synthesizing isn't disabled in loader",
282: // false, qLoader.getSynthEnabled() );
283: // qLoader.setSynthEnabled(true);
284: // assertEquals ("enabling synthesizing failed",
285: // true, qLoader.getSynthEnabled() );
286:
287: // RunTest
288: // rt = loadAsRunTest("test.data.TestDefault");
289: // checkInstrumentation(rt);
290: // // testDefault.runTest(x) returns 2 whatever the input is
291: // assertEquals ("testDefault isn't working", 2, rt.runTest(47));
292: // assertEquals ("testDefault isn't working", 2, rt.runTest(-7));
293: // String
294: // runResults = stmtReg.getReport(); /// <-----------
295: // System.out.println("\nQuilt coverage report:\n" + runResults);
296:
297: // rt = loadAsRunTest("test.data.TestIfThen");
298: // checkInstrumentation(rt);
299: // // testIfThen.runTest(x) returns 3 if x > 0, 5 otherwise
300: // assertEquals ("testIfThen isn't working", 3, rt.runTest(47));
301: // assertEquals ("testIfThen isn't working", 5, rt.runTest(-7));
302:
303: // runResults = stmtReg.getReport(); /// <-----------
304: // System.out.println("\nQuilt coverage report:\n" + runResults);
305:
306: // rt = loadAsRunTest("test.data.TestNPEWithCatch");
307: // checkInstrumentation(rt);
308: // // testNPEWithCatch.runTest(x) always returns 3
309: // assertEquals ("testNPEWithCatch isn't working", 3, rt.runTest(47));
310: // assertEquals ("testNPEWithCatch isn't working", 3, rt.runTest(-7));
311:
312: // rt = loadAsRunTest("test.data.TestNPENoCatch");
313: // checkInstrumentation(rt);
314: // // testNPENoCatch.runTest(x) always throws a NullPointerException
315: // int x;
316: // try {
317: // x = rt.runTest(47);
318: // fail ("testNPENoCatch didn't throw exception");
319: // } catch (NullPointerException e) {
320: // ; // ignore it
321: // }
322: // try {
323: // x = rt.runTest(-7);
324: // fail ("testNPENoCatch didn't throw exception");
325: // } catch (NullPointerException e) {
326: // ; // ignore it
327: // }
328:
329: // rt = loadAsRunTest("test.data.TestSelect");
330: // checkInstrumentation(rt);
331: // // testSelect.runTest(x) returns
332: // // 1 if x == 1; 3 if x == 2; 5 if x == 3; 2 otherwise
333: // assertEquals ("testSelect isn't working", 2, rt.runTest(47));
334: // assertEquals ("testSelect isn't working", 2, rt.runTest(-7));
335: // assertEquals ("testSelect isn't working", 1, rt.runTest(1));
336: // assertEquals ("testSelect isn't working", 3, rt.runTest(2));
337: // assertEquals ("testSelect isn't working", 5, rt.runTest(3));
338:
339: // rt = loadAsRunTest("test.data.TestWhile");
340: // checkInstrumentation(rt);
341: // // testWhile.runTest(x) returns
342: // // 0 if x >= 0, x otherwise
343: // assertEquals ("testWhile isn't working", 0, rt.runTest(47));
344: // assertEquals ("testWhile isn't working",-7, rt.runTest(-7));
345: // } // END999
346:
347: // /////////////////////////////////////////////////////////////////
348: // // BUGS BUGS BUGS BUGS //////////////////////////////////////////
349: // /////////////////////////////////////////////////////////////////
350: //
351: // /////////////////////////////////////////////////////////////////
352: // // LESSER BUGS //////////////////////////////////////////////////
353: // // "edge not in this graph" -- FIXED (a bit crudely)
354: // public void testNestedTryBlocks() {
355: // RunTest
356: // rt = loadAsRunTest("NestedTryBlocks");
357: // checkInstrumentation(rt);
358: // assertEquals ("NestedTryBlocks isn't working", 22, rt.runTest(7));
359: // } // END NESTED
360:
361: /////////////////////////////////////////////////////////////////
362: // SERIOUS BUGS /////////////////////////////////////////////////
363:
364: // // STATICINIT ///////////////////////////////////////////////////
365: // // XXX BUG java.lang.NullPointerException
366: // // at org.apache.bcel.generic.LineNumberGen
367: // // .getLineNumber(LineNumberGen.java:109)
368: // // at org.apache.bcel.generic.MethodGen
369: // // .getLineNumberTable(MethodGen.java:420)
370: // // at org.apache.bcel.generic.MethodGen.getMethod(MethodGen.java:599)
371: // public void testStaticInit() {
372: // RunTest
373: // rt = loadAsRunTest("StaticInit");
374: // checkInstrumentation(rt);
375: // assertEquals("StaticInit isn't working", 10, rt.runTest(7));
376: // } // END STATIC
377:
378: // // TESTFINALLY //////////////////////////////////////////////////
379: // // XXX BUG Invalid start_pc/length in local var table BUG XXX
380: // public void testFinally() {
381: // RunTest
382: // rt = loadAsRunTest("Finally");
383: // checkInstrumentation(rt);
384:
385: // // Finally.runTest(x) returns -1
386: // assertEquals ("Finally isn't working", -1, rt.runTest(11));
387: // assertEquals ("Finally isn't working", -1, rt.runTest(1));
388: // } // END FINALLY
389:
390: // // TESTFINALLY2CATCHES //////////////////////////////////////////
391: // // XXX BUG Mismatched stack types BUG XXX
392: // public void testFinally2Catches() {
393: // RunTest
394: // rt = loadAsRunTest("Finally2Catches");
395: // checkInstrumentation(rt);
396:
397: // // what Finally.runTest(x) returns is a bit complicated ...
398: // assertEquals ("Finally2Catches isn't working", 3600, rt.runTest(11));
399: // } // END
400:
401: // // SWITCHLOAD
402: // // XXX BUG Falling off the end of the code BUG XXX
403: // public void testFinally2Catches() {
404: // RunTest
405: // rt = loadAsRunTest("SwitchLoad");
406: // checkInstrumentation(rt);
407: // // returns 42
408: // assertEquals ("SwitchLoad isn't working", 42, rt.runTest(7));
409: // } // END
410:
411: // // WIMPLE ///////////////////////////////////////////////////////
412: // // XXX BUG java.lang.NullPointerException
413: // // at org.apache.bcel.generic.LineNumberGen
414: // // .getLineNumber(LineNumberGen.java:109)
415: // // at org.apache.bcel.generic.MethodGen
416: // // .getLineNumberTable(MethodGen.java:420)
417: // // at org.apache.bcel.generic.MethodGen.getMethod(MethodGen.java:599)
418: // public void testWimple() {
419: // RunTest
420: // rt = loadAsRunTest("Wimple");
421: // checkInstrumentation(rt);
422: // // returns ??
423: // assertEquals ("Wimple isn't working", 92, rt.runTest(7));
424: // } // END WIMPLE
425:
426: }
|