001: package com.reeltwo.jumble.fast;
002:
003: import java.io.ByteArrayOutputStream;
004: import java.io.PrintStream;
005: import java.util.ArrayList;
006: import java.util.HashSet;
007: import java.util.List;
008: import java.util.Set;
009: import junit.framework.Test;
010: import junit.framework.TestCase;
011:
012: /**
013: * A test suite which runs tests in order and inverts the result. Remembers the
014: * tests that failed last time and does those first.
015: *
016: * @author Tin
017: * @version $Revision: 498 $
018: */
019: public class JumbleTestSuite extends FlatTestSuite {
020:
021: /** Cache of previously failed tests */
022: private FailedTestMap mCache;
023:
024: /** Order of tests */
025: private TestOrder mOrder;
026:
027: /** Mutated class */
028: private String mClass;
029:
030: /** Mutated method */
031: private String mMethod;
032:
033: /** Mutation Point */
034: private int mMethodRelativeMutationPoint;
035:
036: /** Should we dump extra output from the test runs? */
037: private boolean mVerbose;
038:
039: /**
040: * Constructs test suite from the given order of tests.
041: *
042: * @param order
043: * the order to run the tests in
044: * @throws ClassNotFoundException
045: * if <CODE>order</CODE> is malformed.
046: */
047: public JumbleTestSuite(ClassLoader loader, TestOrder order,
048: FailedTestMap cache, String mutatedClass,
049: String mutatedMethod, int mutationPoint, boolean verbose)
050: throws ClassNotFoundException {
051: super ();
052: mCache = cache;
053: mOrder = order;
054: mClass = mutatedClass;
055: mMethod = mutatedMethod;
056: mVerbose = verbose;
057: mMethodRelativeMutationPoint = mutationPoint;
058:
059: // Create the test suites from the order
060: String[] classNames = mOrder.getTestClasses();
061: for (int i = 0; i < classNames.length; i++) {
062: addTestSuite(loader.loadClass(classNames[i]));
063: }
064:
065: // for (int i = 0; i < testCount(); i++) {
066: // System.err.println("Test " + i + " is " + ((TestCase)testAt(i)).getName());
067: // }
068: // System.err.println("Total tests: " + testCount());
069: }
070:
071: /**
072: * Runs the tests returning the result as a string. If any of the individual
073: * tests fail then the run is aborted and "PASS" is returned (recall with a
074: * mutation we expect the test to fail). If all tests run correctly then
075: * "FAIL" is returned.
076: */
077: protected String run() {
078: final JUnitTestResult result = new JUnitTestResult();
079: Test[] tests = getOrder();
080: ByteArrayOutputStream bos = new ByteArrayOutputStream();
081: PrintStream newOut = new PrintStream(bos);
082: PrintStream oldOut = System.out;
083:
084: for (int i = 0; i < testCount(); i++) {
085: TestCase t = (TestCase) tests[i];
086:
087: System.setOut(newOut);
088: try {
089: bos.reset();
090: t.run(result);
091: } finally {
092: System.setOut(oldOut);
093: }
094:
095: if (mVerbose) { // Debugging to allow seeing how the tests picked up the mutation
096: String rstr = result.toString();
097: if (rstr.length() > 0) {
098: System.err.println(result);
099: }
100: if (bos.size() > 0) {
101: System.err.println("CAPTURED OUTPUT: "
102: + bos.toString());
103: }
104: System.err.flush();
105: try {
106: Thread.sleep(50);
107: } catch (InterruptedException e) {
108: ; // Don't care
109: }
110: }
111: if (result.errorCount() > 0 || result.failureCount() > 0) {
112: return "PASS: " + t.getName();
113: }
114: if (result.shouldStop()) {
115: break;
116: }
117: }
118: // all tests passed, this mutation is a problem, report it as a FAIL
119: return "FAIL";
120: }
121:
122: /**
123: * Run the tests for the given class.
124: *
125: * @param order the order in which to run the tests.
126: * @param cache the cache
127: * @param mutatedClassName the name of the class which was mutated
128: * @param mutatedMethodName the name of the method which was mutated
129: * @param relativeMutationPoint the mutation point location relative to the mutated method
130: * @see TestOrder
131: */
132: public static String run(ClassLoader loader, TestOrder order,
133: FailedTestMap cache, String mutatedClassName,
134: String mutatedMethodName, int relativeMutationPoint,
135: boolean verbose) {
136: try {
137: ClassLoader oldLoader = Thread.currentThread()
138: .getContextClassLoader();
139: Thread.currentThread().setContextClassLoader(loader);
140: try {
141: JumbleTestSuite suite = new JumbleTestSuite(loader,
142: order, cache, mutatedClassName,
143: mutatedMethodName, relativeMutationPoint,
144: verbose);
145: String ret = suite.run();
146: return ret;
147: } finally {
148: Thread.currentThread().setContextClassLoader(oldLoader);
149: }
150: } catch (ClassNotFoundException e) {
151: throw new RuntimeException(e); // Should have been picked up before now.
152: }
153: }
154:
155: /**
156: * Basically separates out the tests for the current method so that they are
157: * run first. Still keeps them in the same order.
158: *
159: * @return array of tests in the order of timing but the ones that failed
160: * previously get run first.
161: */
162: private Test[] getOrder() {
163: Test first = null;
164: String firstTestName = null;
165: Set frontTestNames = new HashSet();
166:
167: if (mCache != null) {
168: firstTestName = mCache.getLastFailure(mClass, mMethod,
169: mMethodRelativeMutationPoint);
170: frontTestNames = mCache.getFailedTests(mClass, mMethod);
171: }
172:
173: List<TestCase> front = new ArrayList<TestCase>();
174: List<TestCase> back = new ArrayList<TestCase>();
175:
176: for (int i = 0; i < testCount(); i++) {
177: int indx = mOrder.getTestIndex(i);
178: TestCase curTest = (TestCase) testAt(indx);
179: if (first == null
180: && curTest.getName().equals(firstTestName)) {
181: first = curTest;
182: } else if (frontTestNames.contains(curTest.getName())) {
183: front.add(curTest);
184: } else {
185: back.add(curTest);
186: }
187: }
188:
189: Test[] ret = new Test[testCount()];
190:
191: int i;
192:
193: if (first == null) {
194: i = 0;
195: } else {
196: i = 1;
197: ret[0] = first;
198: }
199:
200: for (int j = 0; j < front.size(); j++) {
201: ret[i] = (Test) front.get(j);
202: i++;
203: }
204: for (int j = 0; j < back.size(); j++) {
205: ret[i] = (Test) back.get(j);
206: i++;
207: }
208: return ret;
209: }
210: }
|