001: package org.apache.ojb.junit;
002:
003: import junit.framework.AssertionFailedError;
004: import junit.framework.TestCase;
005: import junit.framework.TestResult;
006:
007: /**
008: * Extensions for junit to write test cases for multithreaded tests.
009: * All classes are from an
010: * <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1221-junit.html">
011: * javaworld article</a> about junit.
012: *
013: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
014: * @version $Id: JUnitExtensions.java,v 1.1 2004/04/05 17:11:49 arminw Exp $
015: */
016: public class JUnitExtensions {
017: /**
018: * A multi-threaded JUnit test case.
019: * To perform test cases that spin off threads to do tests: <p>
020: * <UL>
021: * <LI>Extend <code>MultiThreadedTestCase</code>
022: * <LI>Write your tests cases as normal except for when you want to spin off threads.
023: * <LI>When you want to spin off threads:
024: * <UL>
025: * <LI>Instead of implementing <code>Runnable</code> extend <code>MultiThreadedTestCase.TestCaseRunnable</code>.
026: * <LI>Define <code>runTestCase ()</code> to do your test, you may call <code>fail (), assert ()</code> etc. and throw
027: * exceptions with impunity.
028: * <LI>Handle thread interrupts by finishing.
029: * </UL>
030: * <LI>Instantiate all the runnables (one for each thread you wish to spawn) and pass an array of them
031: * to <code>runTestCaseRunnables ()</code>.
032: * </UL>
033: * That's it. An example is below:
034: * <PRE>
035: * public class MTTest extends JUnitExtensions.MultiThreadedTestCase
036: * {
037: * MTTest (String s) { super (s); }
038: * public class CounterThread extends JUnitExtensions.TestCaseRunnable
039: * {
040: * public void runTestCase () throws Throwable
041: * {
042: * for (int i = 0; i < 1000; i++)
043: * {
044: * System.out.println ("Counter Thread: " + Thread.currentThread () + " : " + i);
045: * // Do some testing...
046: * if (Thread.currentThread ().isInterrupted ()) {
047: * return;
048: * }
049: * }
050: * }
051: * }
052: *
053: * public void test1 ()
054: * {
055: * TestCaseRunnable tct [] = new TestCaseRunnable [5];
056: * for (int i = 0; i < 5; i++)
057: * {
058: * tct[i] = new CounterThread ();
059: * }
060: * runTestCaseRunnables (tct);
061: * }
062: * }
063: * </PRE>
064: * <BR><STRONG>Category: Test</STRONG>
065: * <BR><STRONG>Not guaranteed to be thread safe.</STRONG>
066: */
067: public static class MultiThreadedTestCase extends TestCase {
068: /**
069: * The threads that are executing.
070: */
071: private Thread threads[] = null;
072: /**
073: * The tests TestResult.*/
074: private TestResult testResult = null;
075:
076: /**
077: * Simple constructor.
078: */
079:
080: public MultiThreadedTestCase(String s) {
081: super (s);
082: }
083:
084: /**
085: * Interrupt the running threads.
086: */
087: public void interruptThreads() {
088: if (threads != null) {
089: for (int i = 0; i < threads.length; i++) {
090: threads[i].interrupt();
091: }
092: }
093: }
094:
095: /**
096: * Override run so we can squirrel away the test result.*/
097:
098: public void run(final TestResult result) {
099: testResult = result;
100: super .run(result);
101: testResult = null;
102: }
103:
104: /**
105: * Run the test case threads.*/
106:
107: protected void runTestCaseRunnables(
108: final TestCaseRunnable[] runnables) {
109: if (runnables == null) {
110: throw new IllegalArgumentException("runnables is null");
111: }
112: threads = new Thread[runnables.length];
113: for (int i = 0; i < threads.length; i++) {
114: threads[i] = new Thread(runnables[i]);
115: }
116: for (int i = 0; i < threads.length; i++) {
117: threads[i].start();
118: }
119: try {
120: for (int i = 0; i < threads.length; i++) {
121: threads[i].join();
122: }
123: } catch (InterruptedException ignore) {
124: System.out.println("Thread join interrupted.");
125: }
126: threads = null;
127: }
128:
129: /**
130: * Handle an exception. Since multiple threads won't have their
131: * exceptions caught the threads must manually catch them and call
132: * <code>handleException ()</code>.
133: * @param t Exception to handle.
134: */
135: private void handleException(final Throwable t) {
136: synchronized (testResult) {
137: if (t instanceof AssertionFailedError) {
138: testResult.addFailure(this ,
139: (AssertionFailedError) t);
140: } else {
141: testResult.addError(this , t);
142: }
143: }
144: }
145:
146: // ======================================================================
147: // inner class
148: // ======================================================================
149: /**
150: * A test case thread. Override runTestCase () and define
151: * behaviour of test in there.*/
152: protected abstract class TestCaseRunnable implements Runnable {
153: /**
154: * Override this to define the test*/
155:
156: public abstract void runTestCase() throws Throwable;
157:
158: /**
159: * Run the test in an environment where
160: * we can handle the exceptions generated by the test method.*/
161:
162: public void run() {
163: try {
164: runTestCase();
165: } catch (Throwable t) /* Any other exception we handle and then we interrupt the other threads.*/
166: {
167: handleException(t);
168: interruptThreads();
169: }
170: }
171: }
172: }
173: }
|