Bug 506367 - EditorsTestSuite is leaking state between tests

Wait for the job manager and SWT event queue to be idle
before advancing to the next test.

Change-Id: I8647ce00680e168d69785ba5d2e3988ac07abaaf
Signed-off-by: Stefan Xenos <sxenos@gmail.com>
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ChainedPreferenceStoreTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ChainedPreferenceStoreTest.java
index c2d33e3..87ae401 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ChainedPreferenceStoreTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ChainedPreferenceStoreTest.java
@@ -11,11 +11,12 @@
 
 package org.eclipse.ui.editors.tests;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.junit.After;
 import org.junit.Test;
 
 import org.eclipse.jface.preference.IPreferenceStore;
@@ -43,6 +44,11 @@
 	private static final String DEFAULT_VALUE= "4";
 	private static final String DEFAULT_DEFAULT_VALUE= "";
 
+	@After
+	public void tearDown() {
+		TestUtil.cleanUp();
+	}
+
 	/**
 	 * [implementation] ChainedPreferenceStore
 	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=69419
@@ -131,5 +137,4 @@
 		assertEquals(null, event.getOldValue());
 		assertEquals(DEFAULT_VALUE, event.getNewValue());
 	}
-
 }
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EncodingChangeTests.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EncodingChangeTests.java
index 38f3fb7..5cfaed7 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EncodingChangeTests.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EncodingChangeTests.java
@@ -11,7 +11,9 @@
 
 package org.eclipse.ui.editors.tests;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 
 import org.junit.After;
 import org.junit.Before;
@@ -89,6 +91,7 @@
 		fEditor= null;
 		fFile= null;
 		ResourceHelper.deleteProject("EncodingChangeTestProject");
+		TestUtil.cleanUp();
 	}
 
 	@Test
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java
index e5adfd6..9035d7b 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java
@@ -92,7 +92,7 @@
 		IWorkbench workbench = PlatformUI.getWorkbench();
 		page = workbench.getActiveWorkbenchWindow().getActivePage();
 		editor = IDE.openEditor(page, file);
-		processUIEvents();
+		TestUtil.runEventLoop();
 
 		IStatusLineManager statusLineManager = editor.getEditorSite().getActionBars().getStatusLineManager();
 		// This is default monitor which almost all editors are using
@@ -106,7 +106,7 @@
 		// process UI events while waiting on workspace lock
 		fileProvider.setProgressMonitor(progressMonitor);
 
-		waitForJobs(500, 5000);
+		TestUtil.waitForJobs(500, 5000);
 		Job[] jobs = Job.getJobManager().find(null);
 		String jobsList = Arrays.toString(jobs);
 		System.out.println("Still running jobs: " + jobsList);
@@ -127,7 +127,8 @@
 			page.closeEditor(editor, false);
 		}
 		ResourceHelper.deleteProject(file.getProject().getName());
-		processUIEvents();
+		TestUtil.runEventLoop();
+		TestUtil.cleanUp();
 	}
 
 	@Test
@@ -218,75 +219,6 @@
 		assertFalse(fsManager.fastIsSynchronized(file));
 	}
 
-	/**
-	 * Utility for waiting until the execution of jobs of any family has
-	 * finished or timeout is reached. If no jobs are running, the method waits
-	 * given minimum wait time. While this method is waiting for jobs, UI events
-	 * are processed.
-	 *
-	 * @param minTimeMs
-	 *            minimum wait time in milliseconds
-	 * @param maxTimeMs
-	 *            maximum wait time in milliseconds
-	 */
-	public static void waitForJobs(long minTimeMs, long maxTimeMs) {
-		if (maxTimeMs < minTimeMs) {
-			throw new IllegalArgumentException("Max time is smaller as min time!");
-		}
-		final long start = System.currentTimeMillis();
-		while (System.currentTimeMillis() - start < minTimeMs) {
-			processUIEvents();
-		}
-		while (!Job.getJobManager().isIdle() && System.currentTimeMillis() - start < maxTimeMs) {
-			processUIEvents();
-		}
-	}
-
-	/**
-	 * Process all queued UI events. If called from background thread, blocks
-	 * until all pending events are processed in UI thread.
-	 */
-	public static void processUIEvents() {
-		processUIEvents(0);
-	}
-
-	/**
-	 * Process all queued UI events. If called from background thread, blocks
-	 * until all pending events are processed in UI thread.
-	 *
-	 * @param timeInMillis
-	 *            time to wait. During this time all UI events are processed but
-	 *            the current thread is blocked
-	 */
-	public static void processUIEvents(final long timeInMillis) {
-		if (Display.getCurrent() != null) {
-			if (timeInMillis <= 0) {
-				while (Display.getCurrent().readAndDispatch()) {
-					// process queued ui events at least once
-				}
-			} else {
-				long start = System.currentTimeMillis();
-				while (System.currentTimeMillis() - start <= timeInMillis) {
-					while (Display.getCurrent().readAndDispatch()) {
-						try {
-							Thread.sleep(10);
-						} catch (InterruptedException e) {
-							break;
-						}
-					}
-				}
-			}
-		} else {
-			// synchronously refresh UI
-			PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
-				@Override
-				public void run() {
-					processUIEvents();
-				}
-			});
-		}
-	}
-
 	static void logError(String message, Exception ex) {
 		String PLUGIN_ID = "org.eclipse.jface.text"; //$NON-NLS-1$
 		ILog log = Platform.getLog(Platform.getBundle(PLUGIN_ID));
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/GotoLineTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/GotoLineTest.java
index 115b9e1..28fb433 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/GotoLineTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/GotoLineTest.java
@@ -68,6 +68,7 @@
 	public void tearDown() throws Exception {
 		ResourceHelper.deleteProject("GoToLineTestProject");
 		fFile= null;
+		TestUtil.cleanUp();
 	}
 
 	@Test
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/MarkerAnnotationOrderTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/MarkerAnnotationOrderTest.java
index 4003b65..a3a31f7 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/MarkerAnnotationOrderTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/MarkerAnnotationOrderTest.java
@@ -10,7 +10,8 @@
  *******************************************************************************/
 package org.eclipse.ui.editors.tests;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 import java.io.BufferedInputStream;
 import java.lang.reflect.Field;
@@ -77,6 +78,7 @@
 			if ("org.eclipse.ui.editors.markerUpdaters".equals(extensions[i].getExtensionPointUniqueIdentifier()))
 				registry.removeExtension(extensions[i], masterToken);
 		}
+		TestUtil.cleanUp();
 	}
 
 	@Test
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/SegmentedModeTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/SegmentedModeTest.java
index bdb2421..6017033 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/SegmentedModeTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/SegmentedModeTest.java
@@ -11,7 +11,8 @@
  *******************************************************************************/
 package org.eclipse.ui.editors.tests;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.After;
 import org.junit.Before;
@@ -57,6 +58,7 @@
 	@After
 	public void tearDown() throws Exception {
 		ResourceHelper.deleteProject("project");
+		TestUtil.cleanUp();
 	}
 
 	/*
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TestUtil.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TestUtil.java
new file mode 100644
index 0000000..9ca7dbe
--- /dev/null
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TestUtil.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Google, Inc and others.
+ * 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:
+ *   Stefan Xenos (Google) - Initial implementation
+ *******************************************************************************/
+package org.eclipse.ui.editors.tests;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.runtime.jobs.Job;
+
+public class TestUtil {
+	/**
+	 * Call this in the tearDown method of every test to clean up state that can
+	 * otherwise leak through SWT between tests.
+	 */
+	public static void cleanUp() {
+		// Ensure that the Thread.interrupted() flag didn't leak.
+		Assert.assertFalse("The main thread should not be interrupted at the end of a test", Thread.interrupted());
+		// Wait for any outstanding jobs to finish. Protect against deadlock by
+		// terminating the wait after a timeout.
+		boolean timedOut = waitForJobs(0, TimeUnit.MINUTES.toMillis(3));
+		Assert.assertFalse("Some Job did not terminate at the end of the test", timedOut);
+		// Wait for any pending *syncExec calls to finish
+		runEventLoop();
+		// Ensure that the Thread.interrupted() flag didn't leak.
+		Assert.assertFalse("The main thread should not be interrupted at the end of a test", Thread.interrupted());
+	}
+
+	/**
+	 * Process all queued UI events. If called from background thread, does
+	 * nothing.
+	 */
+	public static void runEventLoop() {
+		Display display = Display.getCurrent();
+		if (display != null && !display.isDisposed()) {
+			while (display.readAndDispatch()) {
+				// Keep pumping events until the queue is empty
+			}
+		}
+	}
+
+	/**
+	 * Utility for waiting until the execution of jobs of any family has
+	 * finished or timeout is reached. If no jobs are running, the method waits
+	 * given minimum wait time. While this method is waiting for jobs, UI events
+	 * are processed.
+	 *
+	 * @param minTimeMs
+	 *            minimum wait time in milliseconds
+	 * @param maxTimeMs
+	 *            maximum wait time in milliseconds
+	 * @return true if the method timed out, false if all the jobs terminated
+	 *         before the timeout
+	 */
+	public static boolean waitForJobs(long minTimeMs, long maxTimeMs) {
+		if (maxTimeMs < minTimeMs) {
+			throw new IllegalArgumentException("Max time is smaller as min time!");
+		}
+		final long start = System.currentTimeMillis();
+		while (System.currentTimeMillis() - start < minTimeMs) {
+			runEventLoop();
+			try {
+				Thread.sleep(100);
+			} catch (InterruptedException e) {
+				// Uninterruptable
+			}
+		}
+		while (!Job.getJobManager().isIdle()) {
+			if (System.currentTimeMillis() - start >= maxTimeMs) {
+				return true;
+			}
+			runEventLoop();
+			try {
+				Thread.sleep(100);
+			} catch (InterruptedException e) {
+				// Uninterruptable
+			}
+		}
+		return false;
+	}
+}
diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ZoomTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ZoomTest.java
index af637e7..bd6af3d 100644
--- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ZoomTest.java
+++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/ZoomTest.java
@@ -65,6 +65,7 @@
 	public static void tearDownAfterClass() throws Exception {
 		file.delete(true, new NullProgressMonitor());
 		project.delete(true, new NullProgressMonitor());
+		TestUtil.cleanUp();
 	}
 
 	@Before