Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/RunUIThreadRule.java')
-rw-r--r--org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/RunUIThreadRule.java150
1 files changed, 150 insertions, 0 deletions
diff --git a/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/RunUIThreadRule.java b/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/RunUIThreadRule.java
new file mode 100644
index 00000000..2090e7aa
--- /dev/null
+++ b/org.eclipse.swtbot.swt.finder.test/src/org/eclipse/swtbot/swt/finder/RunUIThreadRule.java
@@ -0,0 +1,150 @@
+package org.eclipse.swtbot.swt.finder;
+
+import java.util.List;
+
+import org.eclipse.swt.widgets.Display;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+public class RunUIThreadRule implements MethodRule {
+
+ private final Thread uiThread = Thread.currentThread();
+ private Thread testThread;
+ private Display display;
+ private boolean waitForDisplay = true;
+ private Throwable testException;
+ private final Object testObject;
+
+ public RunUIThreadRule(Object testObject) {
+ this.testObject = testObject;
+ }
+
+ public Statement apply(final Statement testStatement, FrameworkMethod method, final Object target) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ // Fork Test Thread
+ testThread = new Thread(createTestRunnable(testStatement), "test");
+ testThread.start();
+ // Run UI thread
+ runUiThread();
+ }
+
+ };
+
+ }
+
+ private Runnable createTestRunnable(final Statement testStatement) {
+ return new Runnable() {
+ public void run() {
+ runTestThread(testStatement);
+ }
+ };
+ }
+
+ // Test Thread
+ private void runTestThread(final Statement testStatement) {
+ try {
+ waitForDisplay();
+ waitForEventLoop();
+ testStatement.evaluate();
+ } catch (Throwable e) {
+ testException = e;
+ } finally {
+ disposeDisplay();
+ }
+ }
+
+ private void waitForDisplay() throws InterruptedException {
+ while ((display = Display.findDisplay(uiThread)) == null && waitForDisplay) {
+ Thread.sleep(10);
+ }
+ if (display == null) {
+ throw new RuntimeException("@UIThread methods need to create a Display!");
+ }
+ }
+
+ private void waitForEventLoop() {
+ display.syncExec(new Runnable() {
+ public void run() {
+ // no-op, wait for sync
+ }
+ });
+ }
+
+ private void disposeDisplay() {
+ if (display != null && !display.isDisposed()) {
+ display.syncExec(new Runnable() {
+ public void run() {
+ display.dispose();
+ }
+ });
+ }
+ }
+
+ // UI Thread
+
+ private void runUiThread() throws Throwable {
+ try {
+ // Run all methods annotated with @UIThread
+ for (FrameworkMethod frameworkMethod : uiThreadMethods()) {
+ frameworkMethod.invokeExplosively(testObject);
+ }
+ } finally {
+ // Get the Display created by the @UIThread methods
+ display = Display.getCurrent();
+
+ // If the UI thread never created a Display, we need to tell the
+ // test thread to stop looking for it.
+ if (display == null) {
+ waitForDisplay = false;
+ }
+
+ // Running the event loop in the @UIThread method is optional. If
+ // the test doesn't run it, we do. It can also happen that the
+ // @UIThread method finishes with an undisposed display. In this
+ // case we also need to run the event loop to dispose the
+ // Display properly.
+ runEventLoop();
+ }
+
+ // Wait for the test thread to finish
+ testThread.join();
+
+ // Rethrow any test exception that could not be thrown directly
+ if (testException != null) {
+ throw testException;
+ }
+
+ }
+
+ private List<FrameworkMethod> uiThreadMethods() {
+ return new TestClass(testObject.getClass()).getAnnotatedMethods(UIThread.class);
+ }
+
+ /**
+ * Runs the event loop very carefully to make sure the Display can be disposed in every case. This can never throw
+ * an Exception, if an exception bubbles up to the Event thread, it's only stored.
+ */
+ private void runEventLoop() {
+ while (display != null && !display.isDisposed()) {
+ try {
+ // Rescue Measure: if the test thread dies and leaves the
+ // Display behind
+ if (!testThread.isAlive()) {
+ display.dispose();
+ }
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ } catch (Exception e) {
+ // An Exception that occurs from the event loop is recorded.
+ // It's not allowed to disrupt the event loop, because an event
+ // loop needs to be present to properly dispose the Display.
+ testException = e;
+ }
+ }
+ }
+}

Back to the top