Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 749253a1984b991530900c72b335bfee02f39e35 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*******************************************************************************
 * Copyright (c) 2010, 2012 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0, which accompanies this distribution
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 * 
 * Contributors:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.common.utility.tests.internal;

import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.ThreadFactory;

import org.eclipse.jpt.common.utility.internal.CompositeException;

import junit.framework.TestCase;

/**
 * This test case helps simplify the testing of multi-threaded code.
 */
@SuppressWarnings("nls")
public abstract class MultiThreadedTestCase
	extends TestCase
{
	private final ArrayList<Thread> threads = new ArrayList<Thread>();
	/* private */ final Vector<Throwable> exceptions = new Vector<Throwable>();

	/**
	 * The default "tick" is one second.
	 * Specify the appropriate system property to override.
	 */
	public static final String TICK_SYSTEM_PROPERTY_NAME = "org.eclipse.jpt.common.utility.tests.tick";
	public static final long TICK = Long.getLong(TICK_SYSTEM_PROPERTY_NAME, 1000).longValue();
	public static final long TWO_TICKS = 2 * TICK;
	public static final long THREE_TICKS = 3 * TICK;

	/**
	 * Default constructor.
	 */
	public MultiThreadedTestCase() {
		super();
	}

	/**
	 * Named constructor.
	 */
	public MultiThreadedTestCase(String name) {
		super(name);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
	}

	/**
	 * Stop all the threads constructed during the test case.
	 * If any exceptions were thrown by the threads, re-throw them here.
	 */
	@Override
	protected void tearDown() throws Exception {
		for (Thread thread : this.threads) {
			if (thread.isAlive()) {
				throw new IllegalStateException("thread is still alive: " + thread);
			}
		}
		if ( ! this.exceptions.isEmpty()) {
			throw new CompositeException(this.exceptions);
		}
		TestTools.clear(this);
		super.tearDown();
	}

	protected Thread buildThread(Runnable runnable) {
		return this.buildThread(runnable, null);
	}

	protected Thread buildThread(Runnable runnable, String name) {
		Thread thread = new Thread(new RunnableWrapper(runnable));
		if (name != null) {
			thread.setName(name);
		}
		this.threads.add(thread);
		return thread;
	}

	protected ThreadFactory buildThreadFactory() {
		return new TestThreadFactory();
	}

	/**
	 * Convenience method that handles {@link InterruptedException}.
	 */
	public void sleep(long millis) {
		TestTools.sleep(millis);
	}


	/**
	 * Wrap a runnable and log any exception it throws.
	 */
	public class TestThreadFactory implements ThreadFactory {
		public Thread newThread(Runnable r) {
			return MultiThreadedTestCase.this.buildThread(r);
		}
	}

	/**
	 * Simplify runnables that execute call that throws checked exceptions.
	 */
	public abstract class TestRunnable implements Runnable {
		public final void run() {
			try {
				this.run_();
			} catch (Throwable ex) {
				throw new RuntimeException(ex);
			}
		}
		protected abstract void run_() throws Throwable;
	}

	/**
	 * Wrap a runnable and log any exception it throws.
	 */
	private class RunnableWrapper implements Runnable {
		private final Runnable runnable;
		RunnableWrapper(Runnable runnable) {
			super();
			this.runnable = runnable;
		}
		public void run() {
			try {
				this.runnable.run();
			} catch (RuntimeException ex) {
				MultiThreadedTestCase.this.exceptions.add(ex);
			}
		}
	}
}

Back to the top