001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model;
038:
039: import edu.rice.cs.drjava.DrJavaTestCase;
040: import edu.rice.cs.drjava.ui.DefinitionsPaneTest;
041: import edu.rice.cs.util.Log;
042:
043: import junit.framework.AssertionFailedError;
044:
045: /** TestCase which can fail if another thread causes an error or failure.
046: * @version $Id: MultiThreadedTestCase.java 4255 2007-08-28 19:17:37Z mgricken $
047: */
048: public abstract class MultiThreadedTestCase extends DrJavaTestCase {
049: public MultiThreadedTestCase() {
050: super ();
051: }
052:
053: public MultiThreadedTestCase(String name) {
054: super (name);
055: }
056:
057: /** Flag to keep track of whether or not a test failed in another thread (not the testing thread). */
058: protected volatile static boolean _testFailed = false;
059:
060: /** Initialize test state to not failed. */
061: public void setUp() throws Exception {
062: super .setUp();
063: _testFailed = false;
064: ExceptionHandler.ONLY.reset();
065: Thread
066: .setDefaultUncaughtExceptionHandler(ExceptionHandler.ONLY);
067: }
068:
069: /** If any test failed, print a message saying that some test failed in another thread (not the testing thread). */
070: public void tearDown() throws Exception {
071: ExceptionHandler.ONLY.rethrow();
072: if (_testFailed)
073: fail("test failed in another thread");
074: DefinitionsPaneTest._log
075: .log("MultithreadedTestCase.tearDown() calling super.tearDown()");
076: super .tearDown();
077: }
078:
079: /** This method prints the failure message to System.out and kills the JVM. Just calling fail() doesn't always cause
080: * the test to fail, because the listener is often called from another thread.
081: */
082: protected static void listenerFail(String s) {
083: StackTraceElement[] trace = Thread.getAllStackTraces().get(
084: Thread.currentThread());
085: System.out.println("TEST FAILED in a listener thread");
086: System.out.println("Failing thread stack trace:\n "
087: + Log.traceToString(trace));
088: // new AssertionFailedError(s).printStackTrace(System.out);
089: _testFailed = true;
090: fail(s);
091: }
092:
093: /** Join with a thread, i.e. continue only after that thread has terminated. If the join is interrupted, an
094: * UnexpectedException is thrown.
095: * @param t thread to join with
096: */
097: public static void join(Thread t) {
098: try {
099: t.join();
100: } catch (InterruptedException e) {
101: throw new edu.rice.cs.util.UnexpectedException(e,
102: "Thread.join was unexpectedly interrupted.");
103: }
104: }
105:
106: /** Wait for a notify or notifyAll. If the wait is interrupted, an UnexpectedException is thrown.
107: * @param o object to wait for
108: */
109: public static void wait(Object o) {
110: try {
111: o.wait();
112: } catch (InterruptedException e) {
113: e.printStackTrace();
114: throw new edu.rice.cs.util.UnexpectedException(e,
115: "Thread.wait was unexpectedly interrupted.");
116: }
117: }
118:
119: /** Class that stores exceptions thrown in other threads so they can be rethrown in the main thread.
120: * AssertionFailedErrors thrown in other threads do not count as AssertionFailedErrors in the
121: * main class, i.e. if an assertion fails in a thread that is not the main thread, the unit test will not fail!
122: */
123: private static class ExceptionHandler implements
124: java.lang.Thread.UncaughtExceptionHandler {
125:
126: /** Stored throwable, or null if nothing stored. */
127: private volatile Throwable _e = null;
128:
129: /** Stored thread that threw or null if none. */
130: private volatile java.lang.Thread _t = null;
131:
132: /** Thread that spawns the other threads. */
133: private volatile java.lang.Thread _mainThread = java.lang.Thread
134: .currentThread();
135:
136: /** Gets called if an uncaught exception occurs in a thread.
137: * @param t the thread
138: * @param e the uncaught exception
139: */
140: public void uncaughtException(java.lang.Thread t, Throwable e) {
141: _t = t;
142: _e = e;
143: if (_mainThread != null) {
144: System.out
145: .println("***Uncaught Exception in spawned thread within a MultiThreadedTestCase:");
146: e.printStackTrace(System.out);
147: _mainThread.interrupt();
148: }
149: }
150:
151: /** Reset the stored exception and thread. */
152: public void reset() {
153: _t = null;
154: _e = null;
155: }
156:
157: /** Rethrow the exception, if one was stored. */
158: public void rethrow() {
159: if (exceptionOccurred()) {
160: if (_e instanceof Error)
161: throw (Error) _e;
162: if (_e instanceof RuntimeException)
163: throw (RuntimeException) _e;
164: else {
165: // avoid checked exceptions
166: throw new AssertionFailedError(
167: "Exception in thread " + _t + ": " + _e);
168: }
169: }
170: }
171:
172: /** Returns true if an exception has occurred.
173: * @return true if exception has occurred
174: */
175: public boolean exceptionOccurred() {
176: return (_e != null);
177: }
178:
179: public Throwable getException() {
180: return _e;
181: }
182:
183: public java.lang.Thread getThread() {
184: return _t;
185: }
186:
187: /** Set the thread that spawns the other threads. */
188: public void setMainThread(java.lang.Thread mainThread) {
189: _mainThread = mainThread;
190: }
191:
192: /** Singleton constructor. */
193: private ExceptionHandler() {
194: }
195:
196: /** Singleton instance. */
197: public static final ExceptionHandler ONLY = new ExceptionHandler();
198: }
199: }
|