Bug 439017 - use timeout while waiting on condition

Introduced TestUtil.waitWhile() and AbstractDebugTest.waitWhile()
methods to allow tests to fail if some condition does not change after a
given timeout. This should give some hints about test failures without
crashing (DNF) entire test suite.

Also introduced AbstractViewerModelTest to remove lot of copy/paste code
from related tests.

Change-Id: I435872c74482f86e87a42f286834200ef3b45bfe
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AbstractDebugTest.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AbstractDebugTest.java
index 39e3134..6c4a547 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AbstractDebugTest.java
@@ -10,6 +10,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests;
 
+import java.util.function.Function;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
@@ -28,6 +29,11 @@
 
 	private static boolean welcomeClosed;
 
+	/**
+	 * Default timeout in milliseconds to wait on some events
+	 */
+	protected long testTimeout = 30000;
+
 	public AbstractDebugTest() {
 		super();
 	}
@@ -82,6 +88,42 @@
 		}
 	}
 
+	/**
+	 * Waits while given condition is {@code true} for a given amount of
+	 * milliseconds. If the actual wait time exceeds given timeout and condition
+	 * will be still {@code true}, throws {@link AssertionFailedError} with
+	 * given message.
+	 * <p>
+	 * Will process UI events while waiting in UI thread, if called from
+	 * background thread, just waits.
+	 *
+	 * @param condition function which will be evaluated while waiting
+	 * @param timeout max wait time in milliseconds to wait on given condition
+	 * @param errorMessage message which will be used to construct the failure
+	 *            exception in case the condition will still return {@code true}
+	 *            after given timeout
+	 */
+	public void waitWhile(Function<AbstractDebugTest, Boolean> condition, long timeout, Function<AbstractDebugTest, String> errorMessage) throws Exception {
+		TestUtil.waitWhile(condition, this, timeout, errorMessage);
+	}
+
+	/**
+	 * Waits while given condition is {@code true} for some time. If the actual
+	 * wait time exceeds {@link #testTimeout} and condition will be still
+	 * {@code true}, throws {@link AssertionFailedError} with given message.
+	 * <p>
+	 * Will process UI events while waiting in UI thread, if called from
+	 * background thread, just waits.
+	 *
+	 * @param condition function which will be evaluated while waiting
+	 * @param errorMessage message which will be used to construct the failure
+	 *            exception in case the condition will still return {@code true}
+	 *            after given timeout
+	 */
+	public void waitWhile(Function<AbstractDebugTest, Boolean> condition, Function<AbstractDebugTest, String> errorMessage) throws Exception {
+		TestUtil.waitWhile(condition, this, testTimeout, errorMessage);
+	}
+
 	private static void closeIntro(final IWorkbench wb) {
 		IWorkbenchWindow window = wb.getActiveWorkbenchWindow();
 		if (window != null) {
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/TestUtil.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/TestUtil.java
index fba4831..5c8f42d 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/TestUtil.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/TestUtil.java
@@ -18,13 +18,16 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
-
+import java.util.function.Function;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.swt.widgets.Display;
 import org.junit.Assert;
 
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
 public class TestUtil {
 
 	/**
@@ -63,7 +66,7 @@
 	 * Process all queued UI events. If called from background thread, does
 	 * nothing.
 	 */
-	public static void runEventLoop() {
+	public static void processUIEvents() {
 		Display display = Display.getCurrent();
 		if (display != null && !display.isDisposed()) {
 			while (display.readAndDispatch()) {
@@ -75,6 +78,8 @@
 	/**
 	 * Process all queued UI events. If called from background thread, just
 	 * waits
+	 *
+	 * @param millis max wait time to process events
 	 */
 	public static void processUIEvents(final long millis) throws Exception {
 		long start = System.currentTimeMillis();
@@ -91,6 +96,41 @@
 	}
 
 	/**
+	 * Waits while given condition is {@code true} for a given amount of
+	 * milliseconds. If the actual wait time exceeds given timeout and condition
+	 * will be still {@code true}, throws {@link AssertionFailedError} with
+	 * given message.
+	 * <p>
+	 * Will process UI events while waiting in UI thread, if called from
+	 * background thread, just waits.
+	 *
+	 * @param <T> type of the context
+	 * @param context test context
+	 * @param condition function which will be evaluated while waiting
+	 * @param timeout max wait time in milliseconds to wait on given condition
+	 * @param errorMessage message which will be used to construct the failure
+	 *            exception in case the condition will still return {@code true}
+	 *            after given timeout
+	 */
+	public static <T> void waitWhile(Function<T, Boolean> condition, T context, long timeout, Function<T, String> errorMessage) throws Exception {
+		long start = System.currentTimeMillis();
+		Display display = Display.getCurrent();
+		while (System.currentTimeMillis() - start < timeout && condition.apply(context)) {
+			if (display != null && !display.isDisposed()) {
+				if (!display.readAndDispatch()) {
+					Thread.sleep(0);
+				}
+			} else {
+				Thread.sleep(5);
+			}
+		}
+		Boolean stillTrue = condition.apply(context);
+		if (stillTrue) {
+			TestCase.fail(errorMessage.apply(context));
+		}
+	}
+
+	/**
 	 * 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
@@ -128,7 +168,7 @@
 		}
 		final long start = System.currentTimeMillis();
 		while (System.currentTimeMillis() - start < minTimeMs) {
-			runEventLoop();
+			processUIEvents();
 			try {
 				Thread.sleep(Math.min(10, minTimeMs));
 			} catch (InterruptedException e) {
@@ -152,7 +192,7 @@
 				dumpRunningOrWaitingJobs(owner, jobs);
 				return true;
 			}
-			runEventLoop();
+			processUIEvents();
 			try {
 				Thread.sleep(10);
 			} catch (InterruptedException e) {
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/AbstractViewerModelTest.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/AbstractViewerModelTest.java
new file mode 100644
index 0000000..63ba0a0
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/AbstractViewerModelTest.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ *  Copyright (c) 2017 Andrey Loskutov 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:
+ *     Andrey Loskutov <loskutov@gmx.de> - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.viewer.model;
+
+import java.util.function.Function;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
+import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+public abstract class AbstractViewerModelTest extends AbstractDebugTest {
+
+	Display fDisplay;
+	Shell fShell;
+	IInternalTreeModelViewer fViewer;
+	TestModelUpdatesListener fListener;
+
+	public AbstractViewerModelTest(String name) {
+		super(name);
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		fDisplay = PlatformUI.getWorkbench().getDisplay();
+		fShell = new Shell(fDisplay);
+		fShell.setMaximized(true);
+		fShell.setLayout(new FillLayout());
+		fViewer = createViewer(fDisplay, fShell);
+		fListener = createListener(fViewer);
+		fShell.open();
+		TestUtil.processUIEvents();
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		fListener.dispose();
+		fViewer.getPresentationContext().dispose();
+
+		// Close the shell and exit.
+		fShell.close();
+		TestUtil.processUIEvents();
+		super.tearDown();
+	}
+
+	@Override
+	protected void runTest() throws Throwable {
+		try {
+			super.runTest();
+		} catch (Throwable t) {
+			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
+
+	abstract protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer);
+
+	protected Function<AbstractDebugTest, String> createListenerErrorMessage() {
+		return t -> "Listener not finished: " + fListener;
+	}
+
+}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/CheckTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/CheckTests.java
index c4e6687..45fa93c 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/CheckTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/CheckTests.java
@@ -11,17 +11,10 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests which verify the check box support.  This test is very similar to the
@@ -31,64 +24,18 @@
  *
  * @since 3.6
  */
-abstract public class CheckTests extends AbstractDebugTest {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class CheckTests extends AbstractViewerModelTest {
 
     public CheckTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
-
-    public void testSimpleSingleLevel() throws InterruptedException {
+	public void testSimpleSingleLevel() throws Exception {
         // Create the model with test data
         TestModel model = TestModel.simpleSingleLevel();
 
@@ -105,16 +52,12 @@
         fViewer.setInput(model.getRootElement());
 
         // Wait for the updates to complete.
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testSimpleMultiLevel() throws InterruptedException {
+	public void testSimpleMultiLevel() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -124,11 +67,7 @@
 
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
     }
@@ -163,7 +102,7 @@
 //        Assert.assertTrue(element.getChecked() != initialCheckState);
 //    }
 
-    public void testUpdateCheck() throws InterruptedException {
+	public void testUpdateCheck() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -174,11 +113,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -189,12 +124,7 @@
 
         fListener.reset(elementPath, element, -1, true, false);
         model.postDelta(delta);
-        while (!fListener.isFinished(ITestModelUpdatesListenerConstants.LABEL_COMPLETE | ITestModelUpdatesListenerConstants.MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ITestModelUpdatesListenerConstants.LABEL_COMPLETE | ITestModelUpdatesListenerConstants.MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
-
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ColumnPresentationTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ColumnPresentationTests.java
index 672fa36..a8b7daf 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ColumnPresentationTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ColumnPresentationTests.java
@@ -12,6 +12,7 @@
 package org.eclipse.debug.tests.viewer.model;
 
 import java.util.Arrays;
+import java.util.function.Function;
 
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation2;
@@ -20,6 +21,7 @@
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
 import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.TreePath;
@@ -78,21 +80,18 @@
         });
 		fListener = new TestModelUpdatesListener(fViewer, false, false);
 		fShell.open();
+		TestUtil.processUIEvents();
 	}
 
-	void destroyViewer() throws InterruptedException {
+	void destroyViewer() throws Exception {
 		fListener.dispose();
 		fViewer.getPresentationContext().dispose();
 		// Close the shell.
 		fShell.close();
-		while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		}
+		TestUtil.processUIEvents();
 	}
 
-	void recreateViewer() throws InterruptedException {
+	void recreateViewer() throws Exception {
 		destroyViewer();
 		createViewer();
 	}
@@ -235,7 +234,7 @@
 
 	}
 
-	private TestModel makeModel(MyColumnPresentation cp, String rootSufffix) throws InterruptedException {
+	private TestModel makeModel(MyColumnPresentation cp, String rootSufffix) throws Exception {
 		MyModel model = new MyModel(cp);
 		model.setRoot(new TestElement(model, "root" + rootSufffix, new TestElement[] { //$NON-NLS-1$
 		new TestElement(model, "1", true, true, new TestElement[0]), //$NON-NLS-1$
@@ -246,11 +245,7 @@
 		new TestElement(model, "6", new TestElement[0]) })); //$NON-NLS-1$
 		fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false);
 		fViewer.setInput(model.getRootElement());
-		while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 		model.validateData(fViewer, TreePath.EMPTY);
 		return model;
 	}
@@ -263,11 +258,7 @@
 	 * average of tree width / number of visible columns, which is the logic
 	 * in InternalTreeModelViewer.
 	 */
-	public void testInitialColumnAverageWidth() throws InterruptedException {
-        // Try to wait for the shell painting to settle
-        if (!fDisplay.readAndDispatch ()) {
-			Thread.sleep(0);
-		}
+	public void testInitialColumnAverageWidth() throws Exception {
         fResized = false;
 
 		MyColumnPresentation colPre = new MyColumnPresentation();
@@ -296,11 +287,7 @@
 	 * Also, we verify that the initial columns width is the width computed by
 	 * the IColumnPresentation2 implementation.
 	 */
-	public void testInitialColumnWidth() throws InterruptedException {
-        // Try to wait for the shell painting to settle
-        if (!fDisplay.readAndDispatch ()) {
-			Thread.sleep(0);
-		}
+	public void testInitialColumnWidth() throws Exception {
         fResized = false;
 
         MyColumnPresentation2 colPre = new MyColumnPresentation2();
@@ -327,7 +314,7 @@
 	 * is not used when there are user settings inside the viewer which are
 	 * created from user resizing columns.
 	 */
-	public void testRespectUserSettings() throws InterruptedException {
+	public void testRespectUserSettings() throws Exception {
 		MyColumnPresentation2 colPre = new MyColumnPresentation2();
 		makeModel(colPre, "m2"); //$NON-NLS-1$
 		TreeColumn[] columns = fViewer.getTree().getColumns();
@@ -353,17 +340,11 @@
 
 		// get InternalTreeModelViewer to rebuild columns due to hide and show columns
 		fViewer.setShowColumns(false);
-		do {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		} while (fViewer.getTree().getColumns().length > 0);
+		TestUtil.processUIEvents();
+		waitWhile(t -> fViewer.getTree().getColumns().length > 0, createColumnsErrorMessage());
 		fViewer.setShowColumns(true);
-		do {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		} while (fViewer.getTree().getColumns().length != newWidths.length);
+		TestUtil.processUIEvents();
+		waitWhile(t -> fViewer.getTree().getColumns().length != newWidths.length, createColumnsErrorMessage());
 		// verify user resized widths are used instead of the initial widths from IColumnPresentation2
 		columns = fViewer.getTree().getColumns();
 		for (int i = 0; i < columns.length; i++) {
@@ -377,7 +358,7 @@
 	 * is not used when there are user settings inside the viewer which are
 	 * restored from memento, e.g., restoring workspace, etc.
 	 */
-	public void testRespectMemento() throws InterruptedException {
+	public void testRespectMemento() throws Exception {
 		MyColumnPresentation2 colPre = new MyColumnPresentation2();
 		makeModel(colPre, "m2"); //$NON-NLS-1$
 		TreeColumn[] columns = fViewer.getTree().getColumns();
@@ -411,18 +392,15 @@
      * In this test: verify that tree viewer can handle the column presentation changing
      * its available column IDs between runs (bug 360015).
      */
-    public void testChangedColumnIds() throws InterruptedException {
+	public void testChangedColumnIds() throws Exception {
         MyColumnPresentation colPre = new MyColumnPresentation();
 
 		makeModel(colPre, "m1"); //$NON-NLS-1$
         TreeColumn[] columns = fViewer.getTree().getColumns();
         // Select visible columns
         fViewer.setVisibleColumns(new String[] { colPre.columnIds[0] });
-        do {
-            if (!fDisplay.readAndDispatch()) {
-                Thread.sleep(0);
-            }
-        } while (fViewer.getTree().getColumns().length != 1);
+		TestUtil.processUIEvents();
+		waitWhile(t -> fViewer.getTree().getColumns().length != 1, createColumnsErrorMessage());
 
         // get InternalTreeModelViewer to rebuild columns due to change of
         // model and presentation - first set to another model and column
@@ -442,4 +420,11 @@
         }
     }
 
+	private Function<AbstractDebugTest, String> createColumnsErrorMessage() {
+		return t -> "Unexpected columns number: " + fViewer.getTree().getColumns().length;
+	}
+
+	private Function<AbstractDebugTest, String> createListenerErrorMessage() {
+		return t -> "Listener not finished: " + fListener;
+	}
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ContentTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ContentTests.java
index 016df79..34aa8ff 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ContentTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/ContentTests.java
@@ -12,25 +12,21 @@
 package org.eclipse.debug.tests.viewer.model;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Function;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
 import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests that verify that the viewer property retrieves all the content
@@ -38,65 +34,18 @@
  *
  * @since 3.6
  */
-abstract public class ContentTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
+abstract public class ContentTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+	public ContentTests(String name) {
+		super(name);
+	}
 
-    public ContentTests(String name) {
-        super(name);
-    }
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, true, true);
+	}
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, true, true);
-
-        fShell.open ();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
-
-    public void testSimpleSingleLevel() throws InterruptedException {
+	public void testSimpleSingleLevel() throws Exception {
         // Create the model with test data
         TestModel model = TestModel.simpleSingleLevel();
 
@@ -113,18 +62,14 @@
         fViewer.setInput(model.getRootElement());
 
         // Wait for the updates to complete.
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
 
         assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) );
     }
 
-    public void testSimpleMultiLevel() throws InterruptedException {
+	public void testSimpleMultiLevel() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -134,11 +79,7 @@
 
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
 
@@ -198,7 +139,7 @@
      * use data from stale updates to populate the viewer.<br>
      * See bug 210027
      */
-    public void testLabelUpdatesCompletedOutOfSequence1() throws InterruptedException {
+	public void testLabelUpdatesCompletedOutOfSequence1() throws Exception {
         TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
         model.fCaptureLabelUpdates = true;
 
@@ -210,11 +151,8 @@
         // Set input into the view to update it, but block children updates.
         // Wait for view to start retrieving content.
         fViewer.setInput(model.getRootElement());
-        while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> model.fCapturedUpdates.size() < model.getRootElement().fChildren.length, createModelErrorMessage(model));
+
 		List<IViewerUpdate> firstUpdates = model.fCapturedUpdates;
 		model.fCapturedUpdates = new ArrayList<IViewerUpdate>(2);
 
@@ -223,11 +161,7 @@
 		model.getElement(model.findElement("2")).setLabelAppendix(" - changed"); //$NON-NLS-1$ //$NON-NLS-2$
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> model.fCapturedUpdates.size() < model.getRootElement().fChildren.length, createModelErrorMessage(model));
 
         // Complete the second set of children updates
         for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
@@ -241,16 +175,16 @@
             capturedUpdate.done();
         }
 
-        while (!fListener.isFinished(CHILDREN_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CHILDREN_UPDATES), createListenerErrorMessage());
 
         // Check viewer data
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
+	private Function<AbstractDebugTest, String> createModelErrorMessage(TestModelWithCapturedUpdates model) {
+		return t -> "Unxexpected model state: captured updates: " + model.fCapturedUpdates + ", root children: " + Arrays.toString(model.getRootElement().fChildren);
+	}
+
     /**
      * Test to make sure that label provider cancels stale updates and doesn't
      * use data from stale updates to populate the viewer.<br>
@@ -259,7 +193,7 @@
      * updates.<br>
      * See bug 210027
      */
-    public void testLabelUpdatesCompletedOutOfSequence2() throws InterruptedException {
+	public void testLabelUpdatesCompletedOutOfSequence2() throws Exception {
         TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
         model.fCaptureLabelUpdates = true;
 
@@ -271,11 +205,7 @@
         // Set input into the view to update it, but block children updates.
         // Wait for view to start retrieving content.
         fViewer.setInput(model.getRootElement());
-        while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> model.fCapturedUpdates.size() < model.getRootElement().fChildren.length, createModelErrorMessage(model));
 		List<IViewerUpdate> firstUpdates = model.fCapturedUpdates;
 		model.fCapturedUpdates = new ArrayList<IViewerUpdate>(2);
 
@@ -286,11 +216,7 @@
         });
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (model.fCapturedUpdates.size() < model.getRootElement().fChildren.length) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> model.fCapturedUpdates.size() < model.getRootElement().fChildren.length, createModelErrorMessage(model));
 
         // Complete the second set of children updates
         for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
@@ -304,11 +230,7 @@
             capturedUpdate.done();
         }
 
-        while (!fListener.isFinished(CHILDREN_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CHILDREN_UPDATES), createListenerErrorMessage());
 
         // Check viewer data
         model.validateData(fViewer, TreePath.EMPTY);
@@ -323,7 +245,7 @@
      * point, then this test should be re-enabled.<br>
      * See bug 210027
      */
-    public void _x_testChildrenUpdatesCompletedOutOfSequence() throws InterruptedException {
+	public void _x_testChildrenUpdatesCompletedOutOfSequence() throws Exception {
         TestModelWithCapturedUpdates model = new TestModelWithCapturedUpdates();
         model.fCaptureChildrenUpdates = true;
 
@@ -335,11 +257,7 @@
         // Set input into the view to update it, but block children updates.
         // Wait for view to start retrieving content.
         fViewer.setInput(model.getRootElement());
-        while (!areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length)) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> !areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length), createModelErrorMessage(model));
         IChildrenUpdate[] firstUpdates = model.fCapturedUpdates.toArray(new IChildrenUpdate[0]);
         model.fCapturedUpdates.clear();
 
@@ -350,11 +268,7 @@
         });
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length)) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> !areCapturedChildrenUpdatesComplete(model.fCapturedUpdates, model.getRootElement().fChildren.length), createModelErrorMessage(model));
 
         // Complete the second set of children updates
         for (int i = 0; i < model.fCapturedUpdates.size(); i++) {
@@ -366,11 +280,7 @@
             firstUpdates[i].done();
         }
 
-        while (!fListener.isFinished(CHILDREN_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CHILDREN_UPDATES), createListenerErrorMessage());
 
         // Check viewer data
         model.validateData(fViewer, TreePath.EMPTY);
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/DeltaTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/DeltaTests.java
index 43b150b..de8cb09 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/DeltaTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/DeltaTests.java
@@ -13,85 +13,31 @@
 
 import java.util.Arrays;
 import java.util.List;
-
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests to verify that the viewer property retrieves and processes the
  * model deltas generated by the test model.
  */
-abstract public class DeltaTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class DeltaTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public DeltaTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
-
-    public void testUpdateLabel() throws InterruptedException {
+	public void testUpdateLabel() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -102,11 +48,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -116,15 +58,11 @@
 
         fListener.reset(elementPath, element, -1, true, false);
         model.postDelta(delta);
-        while (!fListener.isFinished(LABEL_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(LABEL_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testRefreshStruct() throws InterruptedException {
+	public void testRefreshStruct() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -135,11 +73,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -154,15 +88,11 @@
 
         fListener.reset(elementPath, element, -1, true, false);
         model.postDelta(delta);
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testRefreshStruct2() throws InterruptedException {
+	public void testRefreshStruct2() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -173,11 +103,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
 		String prefix = "new - "; //$NON-NLS-1$
@@ -211,15 +137,11 @@
         fListener.reset(TreePath.EMPTY, element, -1, false, false);
 
         model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testRefreshCoalesceStruct() throws InterruptedException {
+	public void testRefreshCoalesceStruct() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         // Create a single level model and add a single child to each element.
@@ -238,27 +160,19 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
         assertTrue( fListener.checkCoalesced(TreePath.EMPTY, 0, 6) );
     }
 
 
-    public void testInsert() throws InterruptedException {
+	public void testInsert() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -270,11 +184,7 @@
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -292,11 +202,7 @@
         // TODO: redundant label updates on insert!
         fListener.setFailOnRedundantUpdates(false);
         model.postDelta(delta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
@@ -304,7 +210,7 @@
      * This test checks that insert and select delta flags are processed in correct order:
      * insert then select.
      */
-    public void testInsertAndSelect() throws InterruptedException {
+	public void testInsertAndSelect() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -316,11 +222,7 @@
 	    // Set the input into the view and update the view.
 	    fViewer.setInput(model.getRootElement());
 
-	    while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
 	    model.validateData(fViewer, TreePath.EMPTY);
 
@@ -347,11 +249,7 @@
         fListener.setFailOnRedundantUpdates(false);
 
         model.postDelta(rootDelta);
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
     }
@@ -360,7 +258,7 @@
      * This test checks that insert and remove deltas are processed in correct order:
      * remove deltas are processed first then insert deltas.
      */
-    public void testInsertAndRemove() throws InterruptedException {
+	public void testInsertAndRemove() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -368,11 +266,7 @@
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false);
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
 
@@ -399,17 +293,13 @@
         fListener.setFailOnRedundantUpdates(false);
 
         model.postDelta(combinedDelta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
 
-    public void testAddElement() throws InterruptedException {
+	public void testAddElement() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -420,11 +310,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -438,16 +324,12 @@
         // TODO: redundant updates on add!
         fListener.setFailOnRedundantUpdates(false);
         model.postDelta(delta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
     // This test currently fails.  When (if) bug 311442 gets address we should re-enable it.
-    public void _x_testAddUnexpandedElement() throws InterruptedException {
+	public void _x_testAddUnexpandedElement() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -460,11 +342,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Update the model
 		TreePath parentPath = model.findElement("1"); //$NON-NLS-1$
@@ -477,15 +355,11 @@
         fListener.reset();
         fListener.setFailOnRedundantUpdates(false);
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Update the elements that were added.
         fListener.reset();
-        fListener.addUpdates((IInternalTreeModelViewer)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
+        fListener.addUpdates(fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
         rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.CONTENT);
 		model.getElementDelta(rootDelta, model.findElement("1.1"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
 		model.getElementDelta(rootDelta, model.findElement("1.2"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
@@ -493,25 +367,17 @@
 		model.getElementDelta(rootDelta, model.findElement("1.4"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
 
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         fListener.reset(parentPath, model.getElement(parentPath), 1, false, true);
-        ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1);
+        fViewer.expandToLevel(parentPath, 1);
 
-        while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, parentPath);
     }
 
-    public void _x_testRefreshUnexpandedElementsChildren() throws InterruptedException {
+	public void _x_testRefreshUnexpandedElementsChildren() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -524,56 +390,40 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Expand elment "2"
 		TreePath parentPath = model.findElement("2"); //$NON-NLS-1$
         fListener.reset(parentPath, model.getElement(parentPath), 1, false, true);
-        ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1);
+        fViewer.expandToLevel(parentPath, 1);
 
-        while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Collapse back element "2"
-        ((IInternalTreeModelViewer)fViewer).setExpandedState(parentPath, false);
+        fViewer.setExpandedState(parentPath, false);
 
         // Update the children of element "2".
         fListener.reset();
-        fListener.addUpdates((IInternalTreeModelViewer)fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
+        fListener.addUpdates(fViewer, TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
         ModelDelta rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.CONTENT);
 		model.getElementDelta(rootDelta, model.findElement("2.1"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
 		model.getElementDelta(rootDelta, model.findElement("2.2"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
 		model.getElementDelta(rootDelta, model.findElement("2.3"), true).setFlags(IModelDelta.CONTENT); //$NON-NLS-1$
 
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Expand back element "2"
         fListener.reset(parentPath, model.getElement(parentPath), 1, false, true);
-        ((IInternalTreeModelViewer)fViewer).expandToLevel(parentPath, 1);
+        fViewer.expandToLevel(parentPath, 1);
 
-        while (fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, parentPath, true);
     }
 
 
-    public void testRemove() throws InterruptedException {
+	public void testRemove() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -584,11 +434,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -598,15 +444,11 @@
         // be processed.
         fListener.reset();
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testExpandAndSelect() throws InterruptedException {
+	public void testExpandAndSelect() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 
         // Create the listener
@@ -614,11 +456,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Create the delta
@@ -665,17 +503,13 @@
 
         // Validate the expansion state BEFORE posting the delta.
 
-        IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer;
+        IInternalTreeModelViewer contentProviderViewer = fViewer;
         assertFalse(contentProviderViewer.getExpandedState(path_root_3));
         assertFalse(contentProviderViewer.getExpandedState(path_root_3_2));
         assertFalse(contentProviderViewer.getExpandedState(path_root_3_2_2));
 
         model.postDelta(deltaRoot);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Validate the expansion state AFTER posting the delta.
@@ -696,7 +530,7 @@
     /**
      * This test verifies that expand and select updates are being ignored.
      */
-    public void testExpandAndSelect_simple() throws InterruptedException {
+	public void testExpandAndSelect_simple() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 
         // Create the listener
@@ -704,11 +538,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Create the delta
@@ -727,20 +557,12 @@
 
         // Validate the expansion state BEFORE posting the delta.
 
-        IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer;
+        IInternalTreeModelViewer contentProviderViewer = fViewer;
         assertFalse(contentProviderViewer.getExpandedState(path_root_3));
 
         model.postDelta(deltaRoot);
-        while (true) {
-            if (fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-                if (fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE) ) {
-                    break;
-                }
-            }
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		TestUtil.processUIEvents();
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Validate the expansion state AFTER posting the delta.
@@ -756,7 +578,7 @@
         }
     }
 
-    public void testCompositeModelRefreshStruct() throws InterruptedException {
+	public void testCompositeModelRefreshStruct() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.compositeMultiLevel();
@@ -768,11 +590,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Update the model
@@ -789,15 +607,11 @@
 
         fListener.reset(m4_2_1Path, m4_2_1, -1, true, false);
         model.postDelta(delta);
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testCompositeModelAddElement() throws InterruptedException {
+	public void testCompositeModelAddElement() throws Exception {
         TestModel model = TestModel.compositeMultiLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -807,11 +621,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
 		TreePath m3_1Path = model.findElement("m3.1"); //$NON-NLS-1$
@@ -826,27 +636,19 @@
         fListener.setFailOnRedundantUpdates(false);
 
         m3.postDelta(delta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
-    public void testBug292322() throws InterruptedException {
+	public void testBug292322() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, false);
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Update the model: remove one child of an un-expanded element, then
@@ -859,11 +661,7 @@
         fListener.reset(parentPath, parentElement, 0, false, false);
         //fListener.addChildreCountUpdate(parentPath);
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE), createListenerErrorMessage());
 
         // Validate the viewer data.
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -875,11 +673,7 @@
         // Update the viewer
         fListener.reset(parentPath, parentElement, 0, false, false);
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE), createListenerErrorMessage());
 
         // Validate the viewer data.
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -891,14 +685,9 @@
         // Update the viewer
         fListener.reset(parentPath, parentElement, 0, false, false);
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_COMPLETE), createListenerErrorMessage());
 
         // Validate the viewer data.
         model.validateData(fViewer, TreePath.EMPTY, true);
     }
-
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/FilterTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/FilterTests.java
index bd4a39e..6a8eacd 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/FilterTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/FilterTests.java
@@ -13,23 +13,17 @@
 
 import java.util.regex.Pattern;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewerFilter;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
 import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.jface.viewers.ViewerFilter;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests that verify that the viewer property retrieves all the content
@@ -37,69 +31,21 @@
  *
  * @since 3.8
  */
-abstract public class FilterTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class FilterTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public FilterTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, true, true);
-
-        fShell.open ();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, true, true);
+	}
 
     protected IInternalTreeModelViewer getInternalViewer() {
-        return (IInternalTreeModelViewer)fViewer;
+        return fViewer;
     }
 
-
     class TestViewerFilter extends ViewerFilter {
 
     	Pattern fPattern;
@@ -147,37 +93,37 @@
         }
     }
 
-    public void testSimpleSingleLevel() throws InterruptedException {
+	public void testSimpleSingleLevel() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter("2") }); //$NON-NLS-1$
     }
 
-    public void testSimpleSingleLevelWithTMVFilter() throws InterruptedException {
+	public void testSimpleSingleLevelWithTMVFilter() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()) }); //$NON-NLS-1$
     }
 
-    public void testSimpleSingleLevelWithMixedFilters() throws InterruptedException {
+	public void testSimpleSingleLevelWithMixedFilters() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter("2", model.getRootElement()), new TestViewerFilter("1") }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    public void testSimpleMultiLevel() throws InterruptedException {
+	public void testSimpleMultiLevel() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    public void testSimpleMultiLevelWithTMVFilter() throws InterruptedException {
+	public void testSimpleMultiLevelWithTMVFilter() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    public void testSimpleMultiLevelWithMixedFilters() throws InterruptedException {
+	public void testSimpleMultiLevelWithMixedFilters() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 		doTestSimpleLevel(model, new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    private void doTestSimpleLevel(TestModel model, ViewerFilter[] filters) throws InterruptedException {
+	private void doTestSimpleLevel(TestModel model, ViewerFilter[] filters) throws Exception {
 
         // Make sure that all elements are expanded
         fViewer.setAutoExpandLevel(-1);
@@ -192,24 +138,20 @@
         fViewer.setInput(model.getRootElement());
 
         // Wait for the updates to complete.
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         model.validateData(fViewer, TreePath.EMPTY, false, filters);
     }
 
-    public void testLargeSingleLevel() throws InterruptedException {
+	public void testLargeSingleLevel() throws Exception {
 		doTestLargeSingleLevel(new ViewerFilter[] { new TestViewerFilter("2") }); //$NON-NLS-1$
     }
 
-    public void testLargeSingleLevelWithTMVFilter() throws InterruptedException {
+	public void testLargeSingleLevelWithTMVFilter() throws Exception {
 		doTestLargeSingleLevel(new ViewerFilter[] { new TestTMVFilter("2", null) }); //$NON-NLS-1$
     }
 
-    private void doTestLargeSingleLevel(ViewerFilter[] filters) throws InterruptedException {
+	private void doTestLargeSingleLevel(ViewerFilter[] filters) throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
 		model.setElementChildren(TreePath.EMPTY, TestModel.makeSingleLevelModelElements(model, 3000, "model.")); //$NON-NLS-1$
@@ -223,11 +165,7 @@
 
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
     }
 
 
@@ -235,7 +173,7 @@
      * Replace an element that is not visible but filtered out.  With an element that is NOT filtered out.
      * Fire REPLACE delta.
      */
-    public void testReplacedUnrealizedFilteredElement() throws InterruptedException {
+	public void testReplacedUnrealizedFilteredElement() throws Exception {
 		doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") }); //$NON-NLS-1$
     }
 
@@ -244,11 +182,11 @@
      * Replace an element that is not visible but filtered out.  With an element that is NOT filtered out.
      * Fire REPLACE delta.
      */
-    public void testReplacedUnrealizedFilteredElementWithTMVFilter() throws InterruptedException {
+	public void testReplacedUnrealizedFilteredElementWithTMVFilter() throws Exception {
 		doTestReplacedUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) }); //$NON-NLS-1$
     }
 
-    private void doTestReplacedUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException {
+	private void doTestReplacedUnrealizedFilteredElement(ViewerFilter[] filters) throws Exception {
 
         // Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
         TestModel model = new TestModel();
@@ -263,11 +201,7 @@
         // Populate the view (all elements containing a "2" will be filtered out.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Switch out element "201" which is filtered out, with a "replaced element" which should NOT be
         // filtered out.
@@ -275,20 +209,12 @@
         IModelDelta replaceDelta = model.replaceElementChild(TreePath.EMPTY, 200, replacedElement);
         fListener.reset();
         model.postDelta(replaceDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Reposition the viewer to make element 100 the top element, making the replaced element visible.
         fListener.reset();
-        ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+        fViewer.reveal(TreePath.EMPTY, 150);
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Verify that the replaced element is in viewer now (i.e. it's not filtered out.
         TreePath[] replacedElementPaths = fViewer.getElementPaths(replacedElement);
@@ -296,11 +222,11 @@
     }
 
 
-    public void testRefreshUnrealizedFilteredElement() throws InterruptedException {
+	public void testRefreshUnrealizedFilteredElement() throws Exception {
 		doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestViewerFilter("2") }); //$NON-NLS-1$
     }
 
-    public void testRefreshUnrealizedFilteredElementWithTMVFilter() throws InterruptedException {
+	public void testRefreshUnrealizedFilteredElementWithTMVFilter() throws Exception {
 		doTestRefreshUnrealizedFilteredElement(new ViewerFilter[] { new TestTMVFilter("2", null) }); //$NON-NLS-1$
     }
 
@@ -308,7 +234,7 @@
      * Replace an element that is not visible but filtered out.  With an element that is NOT filtered out.
      * Fire CONTENT delta on parent.
      */
-    private void doTestRefreshUnrealizedFilteredElement(ViewerFilter[] filters) throws InterruptedException {
+	private void doTestRefreshUnrealizedFilteredElement(ViewerFilter[] filters) throws Exception {
         // Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
@@ -322,11 +248,7 @@
         // Populate the view (all elements containing a "2" will be filtered out.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Switch out element "201" which is filtered out, with a "replaced element" which should NOT be
         // filtered out.
@@ -334,35 +256,27 @@
         model.replaceElementChild(TreePath.EMPTY, 200, replacedElement);
         fListener.reset();
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Reposition the viewer to make element 100 the top element, making the replaced element visible.
         fListener.reset();
-        ((IInternalTreeModelViewer) fViewer).reveal(TreePath.EMPTY, 150);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+        fViewer.reveal(TreePath.EMPTY, 150);
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Verify that the replaced element is in viewer now (i.e. it's not filtered out.
         TreePath[] replacedElementPaths = fViewer.getElementPaths(replacedElement);
         assertTrue(replacedElementPaths.length != 0);
     }
 
-    public void testRefreshToUnfilterElements() throws InterruptedException {
+	public void testRefreshToUnfilterElements() throws Exception {
 		doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestViewerFilter(".2") }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    public void testRefreshToUnfilterElementsWithTMVFilter() throws InterruptedException {
+	public void testRefreshToUnfilterElementsWithTMVFilter() throws Exception {
 		doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestTMVFilter(".1", null), new TestTMVFilter(".2", null) }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    public void testRefreshToUnfilterElementsWithMixedFilters() throws InterruptedException {
+	public void testRefreshToUnfilterElementsWithMixedFilters() throws Exception {
 		doTestRefreshToUnfilterElements(new ViewerFilter[] { new TestViewerFilter(".1"), new TestTMVFilter(".2", null) }); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
@@ -370,7 +284,7 @@
      * Replace an element that is not visible but filtered out.  With an element that is NOT filtered out.
      * Fire CONTENT delta on parent.
      */
-    private void doTestRefreshToUnfilterElements(ViewerFilter[] filters) throws InterruptedException {
+	private void doTestRefreshToUnfilterElements(ViewerFilter[] filters) throws Exception {
 		ViewerFilter[] filters1 = filters;
         // Populate a view with a large model (only first 100 elements will be visible in virtual viewer).
         TestModel model = TestModel.simpleMultiLevel();
@@ -386,11 +300,7 @@
         // Populate the view (all elements containing a "2" will be filtered out.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Turn off filters and refresh.
 		filters1 = new ViewerFilter[0];
@@ -398,16 +308,12 @@
 
         fListener.reset();
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
 		model.validateData(fViewer, TreePath.EMPTY, false, filters1);
     }
 
-    public void testPreserveExpandedOnMultLevelContent() throws InterruptedException {
+	public void testPreserveExpandedOnMultLevelContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = StateTests.alternatingSubsreesModel(6);
 
@@ -418,11 +324,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         StateTests.expandAlternateElements(fListener, model, true);
@@ -444,11 +346,7 @@
 
         // Post the refresh delta
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true, filters);
@@ -473,11 +371,7 @@
         fListener.reset();
         fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), filters, -1, ALL_UPDATES_COMPLETE);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true, filters);
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerPopupTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerPopupTests.java
index 3d3fb98..9ab3c3c 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerPopupTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerPopupTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
 import org.eclipse.swt.SWT;
@@ -28,7 +28,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell, int style) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell, int style) {
 		return new TreeModelViewer(fShell, SWT.VIRTUAL | style, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerSelectionTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerSelectionTests.java
index e255f90..582389b 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerSelectionTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerSelectionTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
 import org.eclipse.swt.SWT;
@@ -28,7 +28,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerStateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerStateTests.java
index 282f826..b48f0b0 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerStateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerStateTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
 import org.eclipse.swt.SWT;
@@ -28,7 +28,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.MULTI, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerTopIndexTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerTopIndexTests.java
index 1f14448..09e5f94 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerTopIndexTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerTopIndexTests.java
@@ -13,7 +13,6 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
@@ -21,76 +20,29 @@
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
-import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * @since 3.6
  */
-public class JFaceViewerTopIndexTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-
-    Display fDisplay;
-    Shell fShell;
-    TreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+public class JFaceViewerTopIndexTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public JFaceViewerTopIndexTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setSize(300, 80);
-        fShell.setLayout(new FillLayout());
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
-
-    protected IInternalTreeModelViewer getCTargetViewer() {
-        return fViewer;
+	protected final TreeModelViewer getCTargetViewer() {
+		return (TreeModelViewer) fViewer;
     }
 
     /**
@@ -98,7 +50,8 @@
      * @param shell the shell
      * @return the new viewer
      */
-    protected TreeModelViewer createViewer(Display display, Shell shell) {
+    @Override
+	protected TreeModelViewer createViewer(Display display, Shell shell) {
 		return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.MULTI, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 
@@ -106,8 +59,8 @@
      * Restore REVEAL on simple model with elements without children.
      *
      */
-    public void testRestoreTopIndex() throws InterruptedException {
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+	public void testRestoreTopIndex() throws Exception {
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
 
     	TestModel model = new TestModel();
 
@@ -127,11 +80,7 @@
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Stop forcing view updates.
@@ -140,7 +89,7 @@
         // scroll to the 5th element
         int indexRevealElem = 4;
         getCTargetViewer().reveal(TreePath.EMPTY, indexRevealElem);
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
         final TreePath originalTopPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", originalTopPath); //$NON-NLS-1$
         // Bug 116105: On a Mac the reveal call is not reliable.  Use the viewer returned path instead.
@@ -155,22 +104,14 @@
         fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
 
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Set the viewer input back to the model to trigger RESTORE operation.
         fListener.reset(false, false);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
-        while (fDisplay.readAndDispatch ()) {}
+		TestUtil.processUIEvents();
         // check if REVEAL was restored OK
         final TreePath topPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", topPath); //$NON-NLS-1$
@@ -183,8 +124,8 @@
      *
      * See bug 324100
      */
-    public void testRestoreTopAndExpand() throws InterruptedException {
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+	public void testRestoreTopAndExpand() throws Exception {
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
 
         TestModel model = new TestModel();
 
@@ -213,11 +154,7 @@
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Expand first element
@@ -235,11 +172,7 @@
 
         model.postDelta(rootDelta);
 
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Validate that the first node is expanded
         assertTrue(getCTargetViewer().getExpandedState(firstElemPath) == true);
@@ -249,7 +182,7 @@
 
         // scroll to the 2nd element
         getCTargetViewer().reveal(TreePath.EMPTY, 1);
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
         final TreePath originalTopPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", originalTopPath); //$NON-NLS-1$
         // Bug 116105: On a Mac the reveal call is not reliable.  Use the viewer returned path instead.
@@ -263,22 +196,14 @@
         fListener.reset(true, false);
         fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE), createListenerErrorMessage());
 
         // Set the viewer input back to the model
         fListener.reset(false, false);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
-        while (fDisplay.readAndDispatch ()) {}
+		TestUtil.processUIEvents();
         // check if REVEAL was restored OK
         final TreePath topPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", topPath); //$NON-NLS-1$
@@ -291,8 +216,8 @@
      *
      * See bug 324100
      */
-    public void testRestoreTopTriggersExpand() throws InterruptedException {
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+	public void testRestoreTopTriggersExpand() throws Exception {
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
 
         TestModel model = new TestModel();
 
@@ -323,11 +248,7 @@
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         int indexLastElem = elements.length-1;
@@ -343,7 +264,7 @@
 
         // scroll to the element before last element
         getCTargetViewer().reveal(TreePath.EMPTY, indexLastElem-1);
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
         final TreePath originalTopPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", originalTopPath); //$NON-NLS-1$
 
@@ -356,22 +277,14 @@
         fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
 
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Set the viewer input back to the model.
         fListener.reset(false, false);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
-        while (fDisplay.readAndDispatch ()) {}
+		TestUtil.processUIEvents();
         // check if REVEAL was restored OK
         final TreePath topPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", topPath); //$NON-NLS-1$
@@ -383,8 +296,8 @@
      * This test verifies that canceling a reveal pending state delta is
      * properly handled when a new reveal delta is received from the model.
      */
-    public void testRestoreRevealAfterRevealCancel() throws InterruptedException {
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+	public void testRestoreRevealAfterRevealCancel() throws Exception {
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
         TestModel model = TestModel.simpleMultiLevel();
 
         // Expand all
@@ -395,11 +308,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Stop autopopulating the view.
@@ -407,7 +316,7 @@
 
         // Set top index of view to element "3" and wait for view to repaint.
         getCTargetViewer().reveal(TreePath.EMPTY, 2);
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
 
         // Trigger save of state.
         fListener.reset();
@@ -422,11 +331,7 @@
 		TreePath elementPath = model.findElement("3"); //$NON-NLS-1$
         fListener.addUpdates(fViewer, elementPath, model.getElement(elementPath), 1, STATE_UPDATES);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Update the viewer with new selection delta to something new in the view
 		ModelDelta revealDelta = model.makeElementDelta(model.findElement("2.1"), IModelDelta.REVEAL); //$NON-NLS-1$
@@ -434,11 +339,7 @@
         // Wait for the second model delta to process
         fListener.reset();
         model.postDelta(revealDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Clear view then reset it again.
         fListener.reset();
@@ -447,13 +348,9 @@
 			Thread.sleep(0);
 		}
 
-        autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+		autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_RESTORE_COMPLETE), createListenerErrorMessage());
         autopopulateAgent.dispose();
     }
 
@@ -462,12 +359,12 @@
      * This test verifies that canceling a reveal pending state delta is
      * properly handled when a new reveal delta is received from the model.
      */
-    public void testRestoreRevealAfterRevealCancel2() throws InterruptedException {
+	public void testRestoreRevealAfterRevealCancel2() throws Exception {
     	if (Platform.getOS().equals(Platform.OS_MACOSX)) {
     		// skip this test on Mac - see bug 327557
     		return;
     	}
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
         TestModel model = TestModel.simpleMultiLevel();
 
         // Expand all
@@ -478,11 +375,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Stop auto-populating and auto-expanding the view.
@@ -491,7 +384,7 @@
 
         // Set top index of view to element "3" and wait for view to repaint.
         getCTargetViewer().reveal(TreePath.EMPTY, 2);
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
 
         // Trigger save of state.
         fListener.reset();
@@ -508,11 +401,7 @@
 		elementPath = model.findElement("3"); //$NON-NLS-1$
         fListener.addUpdates(fViewer, elementPath, model.getElement(elementPath), 0, STATE_UPDATES);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_UPDATES), createListenerErrorMessage());
 
         // Update the viewer with new selection delta to something new in the view
 		TreePath pathToBeRevealed = model.findElement("2.1"); //$NON-NLS-1$
@@ -528,11 +417,7 @@
 
         // Wait for the second model delta to process
         model.postDelta(revealDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES | LABEL_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES | LABEL_UPDATES), createListenerErrorMessage());
 
         // check if REVEAL was triggered by the delta and not by the
         // state restore operation
@@ -549,8 +434,8 @@
      *
      * See bug 324100
      */
-    public void testRestoreDeepTreeAndReveal() throws InterruptedException {
-        TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+	public void testRestoreDeepTreeAndReveal() throws Exception {
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
 
         TestModel model = TestModel.simpleDeepMultiLevel();
         fViewer.setAutoExpandLevel(-1);
@@ -562,11 +447,7 @@
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
 
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Stop forcing view updates.
@@ -574,7 +455,7 @@
 
         // Scroll down to the last part of the tree.
 		getCTargetViewer().reveal(model.findElement("3.6.3.16.16.16.16.16"), 1); //$NON-NLS-1$
-        while(fDisplay.readAndDispatch()) {}
+		TestUtil.processUIEvents();
         final TreePath originalTopPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", originalTopPath); //$NON-NLS-1$
 
@@ -586,23 +467,15 @@
         fListener.reset(true, false);
         fListener.addStateUpdates(getCTargetViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE), createListenerErrorMessage());
 
         // Set the viewer input back to the model
         fListener.reset(false, false);
         fListener.addUpdates(getCTargetViewer(), originalTopPath, (TestElement)originalTopPath.getLastSegment(), 0, STATE_UPDATES);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(STATE_UPDATES | CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_UPDATES | CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
-        while (fDisplay.readAndDispatch ()) {}
+		TestUtil.processUIEvents();
         // check if REVEAL was restored OK
         final TreePath topPath = getCTargetViewer().getTopElementPath();
 		assertNotNull("Top item should not be null!", topPath); //$NON-NLS-1$
@@ -614,9 +487,9 @@
 	 * This test verifies that a revealed node does not get scrolled away due to
 	 * structural updates.
 	 */
-	public void testRevealWithContentChanges() throws InterruptedException {
+	public void testRevealWithContentChanges() throws Exception {
 		@SuppressWarnings("unused")
-		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
+		TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(getCTargetViewer());
 		TestModel model = TestModel.simpleDeepMultiLevel();
 
 		// Expand first level
@@ -627,17 +500,12 @@
 
 		// Set the input into the view and update the view.
 		fViewer.setInput(model.getRootElement());
-		while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 		model.validateData(fViewer, TreePath.EMPTY, true);
 
 		// Set top index of view to element "2" and wait for view to repaint.
 		getCTargetViewer().reveal(TreePath.EMPTY, 1);
-		while (fDisplay.readAndDispatch()) {
-		}
+		TestUtil.processUIEvents();
 		TreePath element2Path = model.findElement("2"); //$NON-NLS-1$
 		TreePath pathToBeRevealed = element2Path;
 		TreePath topPath = getCTargetViewer().getTopElementPath();
@@ -663,19 +531,11 @@
 
 		// Wait for the model delta to process
 		model.postDelta(revealDelta);
-		while (!fListener.isFinished(CHILD_COUNT_UPDATES_STARTED)) {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CHILD_COUNT_UPDATES_STARTED), createListenerErrorMessage());
 
 		model.setQeueueingUpdate(false);
 
-		while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
 		// check if REVEAL actually revealed the desired element
 		topPath = getCTargetViewer().getTopElementPath();
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerUpdateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerUpdateTests.java
index 60865ec..4c0c4d9 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerUpdateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/JFaceViewerUpdateTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
 import org.eclipse.swt.SWT;
@@ -28,7 +28,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/LazyTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/LazyTests.java
index 97c7c7e..7cbdb83 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/LazyTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/LazyTests.java
@@ -11,19 +11,13 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests that verify that the viewer property retrieves all the content
@@ -31,62 +25,16 @@
  *
  * @since 3.6
  */
-abstract public class LazyTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-
-    Display fDisplay;
-    Shell fShell;
-    IInternalTreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class LazyTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public LazyTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, true, true);
-
-        fShell.open ();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-        /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, true, true);
+	}
 
     /**
      * Creates a model in the pattern of:
@@ -116,7 +64,7 @@
      * not automatically materialized.
      * (bug 305739 and bug 304277)
      */
-    public void testExpandLargeSubTree() throws InterruptedException {
+	public void testExpandLargeSubTree() throws Exception {
         // Create test model with lots of children.
         TestModel model = largeSubtreeModel(1000);
 
@@ -125,11 +73,7 @@
         // Populate initial view content
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, true, true);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Create delta to expand the "1" element.
         TestElement rootElement = model.getRootElement();
@@ -152,11 +96,7 @@
         }
         model.postDelta(rootDelta);
 
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE | LABEL_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
     }
 
     /**
@@ -164,7 +104,7 @@
      * then selected and replaced, that no extra elements are retrieved.
      * (bug 304277 comment #24, and bug 305739 comment #9).
      */
-    public void testReplaceAndSelectInSubTreeTree() throws InterruptedException {
+	public void testReplaceAndSelectInSubTreeTree() throws Exception {
         // Create test model with lots of children.
         TestModel model = largeSubtreeModel(1000);
 
@@ -180,11 +120,7 @@
         fListener.setFailOnRedundantUpdates(false);
         fViewer.setInput(model.getRootElement());
 		fListener.addLabelUpdate(model.findElement("1.0")); //$NON-NLS-1$
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_COMPLETE), createListenerErrorMessage());
 
         // Set selection so that the initial selection is not empty
 		fViewer.setSelection(new TreeSelection(new TreePath[] { model.findElement("1.0") })); //$NON-NLS-1$
@@ -209,11 +145,7 @@
         fListener.addLabelUpdate(_1_0_newElementPath);
         model.postDelta(rootDelta);
 
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE |  LABEL_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | LABEL_COMPLETE), createListenerErrorMessage());
 
 
         assertEquals(((IStructuredSelection)fViewer.getSelection()).getFirstElement(), _1_0_newElement);
@@ -221,7 +153,7 @@
 
     /**
      */
-    public void testContentRefresh() throws InterruptedException {
+	public void testContentRefresh() throws Exception {
         // Create test model with lots of children.
         TestModel model = largeSubtreeModel(1000);
 
@@ -231,11 +163,7 @@
         // Populate initial view content
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, true);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Turn off autoexpand
         fViewer.setAutoExpandLevel(0);
@@ -244,11 +172,7 @@
         fListener.reset();
         fListener.setFailOnRedundantUpdates(false);
 		fViewer.reveal(model.findElement("1"), 500); //$NON-NLS-1$
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Create delta to refresh the "1" element.
         TestElement rootElement = model.getRootElement();
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PerformanceTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PerformanceTests.java
index d38581f..b5c7314 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PerformanceTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PerformanceTests.java
@@ -11,83 +11,37 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
 import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.jface.viewers.ViewerFilter;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
 import org.eclipse.test.performance.Performance;
 import org.eclipse.test.performance.PerformanceMeter;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests to measure the performance of the viewer updates.
  */
-abstract public class PerformanceTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class PerformanceTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
+
+	protected VisibleVirtualItemValidator fVirtualItemValidator;
 
     public PerformanceTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
     @Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
+
+	@Override
 	protected void setUp() throws Exception {
 		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fVirtualItemValidator = new VisibleVirtualItemValidator(0, Integer.MAX_VALUE);
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open();
-    }
-
-    abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
+		fVirtualItemValidator = new VisibleVirtualItemValidator(0, Integer.MAX_VALUE);
     }
 
     /**
@@ -97,9 +51,8 @@
      */
     abstract protected int getTestModelDepth();
 
-    protected VisibleVirtualItemValidator fVirtualItemValidator;
 
-    public void testRefreshStruct() throws InterruptedException {
+	public void testRefreshStruct() throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
 		model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements(model, getTestModelDepth(), "model.")); //$NON-NLS-1$
@@ -111,11 +64,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         Performance perf = Performance.getDefault();
@@ -130,11 +79,7 @@
 
                 meter.start();
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
@@ -146,7 +91,7 @@
         }
     }
 
-    public void testRefreshStruct2() throws InterruptedException {
+	public void testRefreshStruct2() throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
 		model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements2(model, new int[] { 2, 3000, 1 }, "model.")); //$NON-NLS-1$
@@ -158,14 +103,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE))
-		 {
-			if (!fDisplay.readAndDispatch ())
-			 {
-				Thread.sleep(0);
-				//model.validateData(fViewer, TreePath.EMPTY);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         fVirtualItemValidator.setVisibleRange(0, 50);
 
@@ -182,17 +120,9 @@
 
                 meter.start();
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
@@ -205,7 +135,7 @@
     }
 
 
-    public void testRefreshStructReplaceElements() throws InterruptedException {
+	public void testRefreshStructReplaceElements() throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
 		model.setElementChildren(TreePath.EMPTY, TestModel.makeMultiLevelElements(model, getTestModelDepth(), "model.")); //$NON-NLS-1$
@@ -217,11 +147,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         Performance perf = Performance.getDefault();
@@ -236,11 +162,7 @@
 
                 meter.start();
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
@@ -253,7 +175,7 @@
     }
 
 
-    public void testRefreshList() throws InterruptedException {
+	public void testRefreshList() throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
         int numElements = (int)Math.pow(2, getTestModelDepth());
@@ -266,11 +188,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         Performance perf = Performance.getDefault();
@@ -285,11 +203,7 @@
 
                 meter.start();
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
@@ -301,7 +215,7 @@
         }
     }
 
-    public void testSaveAndRestore() throws InterruptedException {
+	public void testSaveAndRestore() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -313,11 +227,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Set a selection in view
@@ -338,11 +248,7 @@
 
                 meter.start();
                 fViewer.setInput(null);
-                while (!fListener.isFinished(STATE_SAVE_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE), createListenerErrorMessage());
 
                 // Set the viewer input back to the model.  When view updates are complete
                 // the viewer
@@ -350,11 +256,7 @@
                 fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false);
                 // TODO: add state updates somehow?
                 fViewer.setInput(model.getRootElement());
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
@@ -367,7 +269,7 @@
 
     }
 
-    public void testRefreshListFiltered() throws InterruptedException {
+	public void testRefreshListFiltered() throws Exception {
         TestModel model = new TestModel();
 		model.setRoot(new TestElement(model, "root", new TestElement[0])); //$NON-NLS-1$
         int numElements = (int)Math.pow(2, getTestModelDepth());
@@ -396,11 +298,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         Performance perf = Performance.getDefault();
@@ -415,11 +313,7 @@
 
                 meter.start();
                 model.postDelta(new ModelDelta(element, IModelDelta.CONTENT));
-                while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-					if (!fDisplay.readAndDispatch ()) {
-						Thread.sleep(0);
-					}
-				}
+				waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
                 meter.stop();
                 System.gc();
             }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PopupTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PopupTests.java
index 0850302..4ad73eb 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PopupTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/PopupTests.java
@@ -18,19 +18,16 @@
 
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests to verify that the viewer property updates when created
@@ -38,63 +35,32 @@
  *
  * @since 3.6
  */
-abstract public class PopupTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class PopupTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public PopupTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
     @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell, SWT.POP_UP);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
     protected IInternalTreeModelViewer getCTargetViewer() {
-        return (IInternalTreeModelViewer)fViewer;
+        return fViewer;
     }
 
+	@Override
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
+		return createViewer(display, shell, SWT.POP_UP);
+	}
 
-    abstract protected ITreeModelViewer createViewer(Display display, Shell shell, int style);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
+	abstract protected IInternalTreeModelViewer createViewer(Display display, Shell shell, int style);
 
     /**
      * This test verifies that content updates are still being performed.
      */
-    public void testRefreshStruct() throws InterruptedException {
+	public void testRefreshStruct() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -105,11 +71,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -124,18 +86,14 @@
 
         fListener.reset(elementPath, element, -1, true, false);
         model.postDelta(delta);
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
     /**
      * This test verifies that expand and select updates are being ignored.
      */
-    public void testExpandAndSelect() throws InterruptedException {
+	public void testExpandAndSelect() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
 
         // Create the listener
@@ -143,11 +101,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Create the delta
@@ -166,24 +120,15 @@
 
         // Validate the expansion state BEFORE posting the delta.
 
-        IInternalTreeModelViewer contentProviderViewer = (IInternalTreeModelViewer)fViewer;
+        IInternalTreeModelViewer contentProviderViewer = fViewer;
         assertFalse(contentProviderViewer.getExpandedState(path_root_3));
 
         model.postDelta(deltaRoot);
-        while (true) {
-            if (fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-                if (fListener.isFinished(CONTENT_SEQUENCE_STARTED)) {
-                    if (fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-                        break;
-                    }
-                } else {
-                    break;
-                }
-            }
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		TestUtil.processUIEvents();
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE)
+				&& (fListener.isFinished(CONTENT_SEQUENCE_STARTED)
+						|| !fListener.isFinished(CONTENT_SEQUENCE_STARTED) && !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)),
+				createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Validate the expansion state AFTER posting the delta.
@@ -199,9 +144,7 @@
         }
     }
 
-
-
-    public void testPreserveExpandedOnSubTreeContent() throws InterruptedException {
+	public void testPreserveExpandedOnSubTreeContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -213,11 +156,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Turn off auto-expansion
@@ -242,11 +181,7 @@
 
         // Post the sub-tree update
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true);
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/SelectionTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/SelectionTests.java
index 8cdc499..9d04884 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/SelectionTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/SelectionTests.java
@@ -14,72 +14,30 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
 import org.eclipse.jface.viewers.SelectionChangedEvent;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests to verify that the viewer properly handles selection changes.
  */
-abstract public class SelectionTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class SelectionTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public SelectionTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
     @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    abstract protected ITreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
     private static class SelectionListener implements ISelectionChangedListener {
 		private final List<SelectionChangedEvent> fEvents = new ArrayList<SelectionChangedEvent>(1);
@@ -90,16 +48,12 @@
         }
     }
 
-    private TestModel makeMultiLevelModel() throws InterruptedException {
+	private TestModel makeMultiLevelModel() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
         fViewer.setAutoExpandLevel(-1);
         fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
         return model;
     }
@@ -110,7 +64,7 @@
      * - verify that selection chagned listener is called
      * - verify that the selection is in the viewer is correct
      */
-    public void testSimpleSetSelection() throws InterruptedException {
+	public void testSimpleSetSelection() throws Exception {
         // Create the model and populate the view.
         TestModel model = makeMultiLevelModel();
 
@@ -133,7 +87,7 @@
      * from being set and verify that a FORCE flag can override the selection
      * policy.
      */
-    public void testSelectionPolicy() throws InterruptedException {
+	public void testSelectionPolicy() throws Exception {
         // Create the model and populate the view.
         final TestModel model = makeMultiLevelModel();
 
@@ -181,22 +135,14 @@
         ModelDelta delta_3_3_3 = model.getElementDelta(baseDelta, path_3_3_3, false);
         delta_3_3_3.setFlags(IModelDelta.SELECT);
         fViewer.updateViewer(baseDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         assertEquals(selection_3_3_1, fViewer.getSelection());
 
         // Add the *force* flag to the selection delta and update viewer again.
         // Verify that selection did change.
         delta_3_3_3.setFlags(IModelDelta.SELECT | IModelDelta.FORCE);
         fViewer.updateViewer(baseDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         assertEquals(selection_3_3_3, fViewer.getSelection());
     }
 
@@ -208,7 +154,7 @@
      * - update the view with remove delta
      * -> The selection should be re-set to empty.
      */
-    public void testSelectRemove() throws InterruptedException {
+	public void testSelectRemove() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         // Create the model and populate the view.
@@ -235,11 +181,7 @@
         // delta only wait for the delta to be processed.
         fListener.reset();
         model.postDelta(delta);
-        while (!fListener.isFinished(ITestModelUpdatesListenerConstants.MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ITestModelUpdatesListenerConstants.MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure the selection was made
         //assertTrue(listener.fEvents.size() == 1);
@@ -257,7 +199,7 @@
      * - then refresh the view.
      * -> The selection should be re-set to empty.
      */
-    public void testSelectRemoveRefreshStruct() throws InterruptedException {
+	public void testSelectRemoveRefreshStruct() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         // Create the model and populate the view.
@@ -286,11 +228,7 @@
 
         // Refresh the viewer
         model.postDelta( new ModelDelta(model.getRootElement(), IModelDelta.CONTENT) );
-        while (!fListener.isFinished(ITestModelUpdatesListenerConstants.ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ITestModelUpdatesListenerConstants.ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure the selection was made
         // Commented out until JFace bug 219887 is fixed.
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/StateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/StateTests.java
index 19a7dff..9c7a589 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/StateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/StateTests.java
@@ -15,20 +15,14 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.ITreeSelection;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests to verify that the viewer can save and restore correctly the expansion
@@ -36,69 +30,22 @@
  *
  * @since 3.6
  */
-abstract public class StateTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class StateTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public StateTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
     @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    abstract protected ITreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception, InterruptedException {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
     protected IInternalTreeModelViewer getInternalViewer() {
-        return (IInternalTreeModelViewer)fViewer;
+        return fViewer;
     }
 
-    public void testUpdateViewer() throws InterruptedException {
+	public void testUpdateViewer() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleMultiLevel();
@@ -111,11 +58,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Create the update delta
@@ -150,11 +93,7 @@
         fListener.addLabelUpdate(path3);
 
         fViewer.updateViewer(updateDelta);
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | LABEL_UPDATES), createListenerErrorMessage());
 
         // Extract the new state from viewer
         ModelDelta savedDelta = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -232,7 +171,7 @@
         return sel1Set.equals(sel2Set);
     }
 
-    static void expandAlternateElements(TestModelUpdatesListener listener, TestModel model, boolean waitForAllUpdates) throws InterruptedException {
+	static void expandAlternateElements(TestModelUpdatesListener listener, TestModel model, boolean waitForAllUpdates) throws Exception {
         listener.reset();
         listener.setFailOnRedundantUpdates(false);
 
@@ -262,14 +201,10 @@
         }
         model.postDelta(rootDelta);
 
-        while (!listener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE)) {
-			if (!Display.getDefault().readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		TestUtil.waitWhile(t -> !listener.isFinished(CONTENT_SEQUENCE_COMPLETE | MODEL_CHANGED_COMPLETE), null, 30000, t -> "Listener not finished: " + listener);
     }
 
-    public void testPreserveExpandedOnRemove() throws InterruptedException {
+	public void testPreserveExpandedOnRemove() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -280,11 +215,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         expandAlternateElements(fListener, model, true);
@@ -299,11 +230,7 @@
         // Remove delta should not generate any new updates
         fListener.reset();
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -317,7 +244,7 @@
         assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
     }
 
-    public void testPreserveExpandedOnInsert() throws InterruptedException {
+	public void testPreserveExpandedOnInsert() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -328,11 +255,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         expandAlternateElements(fListener, model, true);
@@ -350,11 +273,7 @@
         fListener.reset(path, (TestElement)path.getLastSegment(), 0, false, false);
         fListener.addChildreUpdate(TreePath.EMPTY, 0);
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -370,7 +289,7 @@
         assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
     }
 
-    public void testPreserveExpandedOnMultLevelContent() throws InterruptedException {
+	public void testPreserveExpandedOnMultLevelContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -381,11 +300,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         expandAlternateElements(fListener, model, true);
@@ -411,11 +326,7 @@
 
         // Post the multi-content update delta
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -434,7 +345,7 @@
     }
 
 
-    public void testPreserveExpandedOnSubTreeContent() throws InterruptedException {
+	public void testPreserveExpandedOnSubTreeContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -446,11 +357,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Turn off auto-expansion
@@ -475,11 +382,7 @@
 
         // Post the sub-tree update
         model.postDelta(rootDelta);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
         model.validateData(fViewer, TreePath.EMPTY, true);
@@ -492,7 +395,7 @@
         assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
     }
 
-    public void testPreserveExpandedOnContentStress() throws InterruptedException {
+	public void testPreserveExpandedOnContentStress() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -503,11 +406,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         expandAlternateElements(fListener, model, true);
@@ -529,11 +428,7 @@
             fListener.reset(false, false);
             fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-            while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
             // Validate data
             model.validateData(fViewer, TreePath.EMPTY, true);
@@ -553,11 +448,7 @@
             fListener.reset(false, false);
             fListener.addUpdates(getInternalViewer(), TreePath.EMPTY, model.getRootElement(), -1, ALL_UPDATES_COMPLETE);
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-            while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
             // Validate data
             model.validateData(fViewer, TreePath.EMPTY, true);
@@ -572,7 +463,7 @@
         }
     }
 
-    public void testPreserveLargeModelOnContent() throws InterruptedException {
+	public void testPreserveLargeModelOnContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(100);
 
@@ -583,14 +474,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE))
-		 {
-			if (!fDisplay.readAndDispatch ())
-			 {
-				Thread.sleep(0);
-//        model.validateData(fViewer, TreePath.EMPTY, true);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         expandAlternateElements(fListener, model, false);
 
@@ -605,11 +489,7 @@
         // Note: Re-expanding nodes causes redundant updates.
         fListener.reset(false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); //$NON-NLS-1$
@@ -627,11 +507,7 @@
         // Note: Re-expanding nodes causes redundant updates.
         fListener.reset(false, false);
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); //$NON-NLS-1$
@@ -649,7 +525,7 @@
      * following a content refresh, the state restore logic will
      * not override the selection requested by the model.
      */
-    public void testPreserveSelectionDeltaAfterContent() throws InterruptedException {
+	public void testPreserveSelectionDeltaAfterContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -661,11 +537,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Set a selection in view
@@ -677,11 +549,7 @@
         // Refresh content.
         // Note: Wait only for the processing of the delta, not for all updates
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Update the viewer with new selection delta to something new in the view
 		ModelDelta selectDelta = model.makeElementDelta(model.findElement("2.1"), IModelDelta.SELECT); //$NON-NLS-1$
@@ -689,25 +557,17 @@
         // Wait for the second model delta to process
         fListener.resetModelChanged();
         model.postDelta(selectDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Wait for all the updates to complete (note: we're not resetting the listener.
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure that the state restore didn't change the selection.
 		assertEquals(new TreeSelection(model.findElement("2.1")), fViewer.getSelection()); //$NON-NLS-1$
     }
 
-    public void testPreserveCollapseDeltaAfterContent() throws InterruptedException {
+	public void testPreserveCollapseDeltaAfterContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -719,11 +579,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Turn off auto-expand
@@ -735,11 +591,7 @@
         // Refresh content.
         // Note: Wait only for the processing of the delta, not for all updates
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Update the viewer to collapse an element
 		ModelDelta collapseDelta = model.makeElementDelta(model.findElement("3.1"), IModelDelta.COLLAPSE); //$NON-NLS-1$
@@ -760,25 +612,17 @@
 
         // Wait for the second model delta to process
         model.postDelta(collapseDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Wait for all the updates to complete (note: we're not resetting the listener.
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure that the state restore didn't change the selection.
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == false); //$NON-NLS-1$
     }
 
-    public void testPreserveExpandDeltaAfterContent() throws InterruptedException {
+	public void testPreserveExpandDeltaAfterContent() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -789,11 +633,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Reset the listener (ignore redundant updates)
@@ -802,11 +642,7 @@
         // Refresh content.
         // Note: Wait only for the processing of the delta, not for all updates
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Update the viewer to expand an element
 		ModelDelta expandDelta = model.makeElementDelta(model.findElement("3.1"), IModelDelta.EXPAND); //$NON-NLS-1$
@@ -814,26 +650,18 @@
         // Wait for the second model delta to process
         fListener.resetModelChanged();
         model.postDelta(expandDelta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Wait for all the updates to complete (note: we're not resetting the listener.
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure that the state restore didn't change the selection.
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("3.1")) == true); //$NON-NLS-1$
     }
 
 
-    public void testSaveAndRestore1() throws InterruptedException {
+	public void testSaveAndRestore1() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -844,11 +672,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Expand some, but not all elements
@@ -865,11 +689,7 @@
         fListener.reset(false, false);
         fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Set the viewer input back to the model.  When view updates are complete
         // the viewer
@@ -877,11 +697,7 @@
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false);
         // TODO: add state updates somehow?
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Extract the restored state from viewer
         ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -892,7 +708,7 @@
         }
     }
 
-    public void testSaveAndRestore2() throws InterruptedException {
+	public void testSaveAndRestore2() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -904,11 +720,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Set a selection in view
@@ -927,11 +739,7 @@
         fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
 
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Set the viewer input back to the model.  When view updates are complete
         // the viewer
@@ -939,11 +747,7 @@
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false);
         // TODO: add state updates somehow?
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Extract the restored state from viewer
         ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -954,7 +758,7 @@
         }
     }
 
-    public void testSaveAndRestoreInputInstance() throws InterruptedException {
+	public void testSaveAndRestoreInputInstance() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -965,11 +769,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Expand some, but not all elements
@@ -989,11 +789,7 @@
         // Note: disable redundant updates because the reveal delta triggers one.
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false);
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Extract the restored state from viewer
         ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -1004,7 +800,7 @@
         }
     }
 
-    public void testSaveAndRestoreInputInstanceEquals() throws InterruptedException {
+	public void testSaveAndRestoreInputInstanceEquals() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(6);
 
@@ -1015,11 +811,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // Expand some, but not all elements
@@ -1042,11 +834,7 @@
         fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false);
 
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
 
         // Extract the restored state from viewer
         ModelDelta restoredState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -1058,7 +846,7 @@
     }
 
 
-    public void testSaveAndRestoreLarge() throws InterruptedException {
+	public void testSaveAndRestoreLarge() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(100);
 
@@ -1069,11 +857,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         expandAlternateElements(fListener, model, false);
 
@@ -1091,22 +875,14 @@
         fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
 
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
         // Set the viewer input back to the model.  When view updates are complete
         // the viewer
         // Note: disable redundant updates because the reveal delta triggers one.
         fListener.reset();
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         // Validate data (only select visible elements).
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("1")) == true); //$NON-NLS-1$
@@ -1126,7 +902,7 @@
      * to contain much fewer elements.  The restore logic should discard the
      * rest of the saved state delta once all the elements are visible.
      */
-    public void testSaveAndRestorePartialStateLarge() throws InterruptedException {
+	public void testSaveAndRestorePartialStateLarge() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = alternatingSubsreesModel(100);
 
@@ -1137,11 +913,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE), createListenerErrorMessage());
 
         expandAlternateElements(fListener, model, false);
 
@@ -1159,11 +931,7 @@
         fListener.addStateUpdates(getInternalViewer(), originalState, IModelDelta.EXPAND | IModelDelta.SELECT | IModelDelta.REVEAL);
 
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE | STATE_UPDATES), createListenerErrorMessage());
 
 
         TestElement[] elements = model.getRootElement().getChildren();
@@ -1178,11 +946,7 @@
         fViewer.setInput(model.getRootElement());
 
         // MONITOR FOR THE STATE RESTORE TO COMPLETE
-        while (!fListener.isFinished(CONTENT_SEQUENCE_COMPLETE| STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_SEQUENCE_COMPLETE | STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Validate data
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("1")) == true); //$NON-NLS-1$
@@ -1197,7 +961,7 @@
         assertTrue( areTreeSelectionsEqual(originalSelection, (ITreeSelection)fViewer.getSelection()) );
     }
 
-    public void testPreserveCollapseAndSelectDeltaAfterSaveAndRestore() throws InterruptedException {
+	public void testPreserveCollapseAndSelectDeltaAfterSaveAndRestore() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
 
@@ -1209,11 +973,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
 		fViewer.setSelection(new TreeSelection(model.findElement("3"))); //$NON-NLS-1$
@@ -1224,11 +984,7 @@
         // Set the viewer input to null.  This will trigger the view to save the viewer state.
         fListener.reset(false, false);
         fViewer.setInput(null);
-        while (!fListener.isFinished(STATE_SAVE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_SAVE_COMPLETE), createListenerErrorMessage());
 
         // Set the viewer input back to the model.  When view updates are complete
         // the viewer
@@ -1243,44 +999,24 @@
         // Wait till we restore state of elements we want to collapse and select
         // Bug 372619 - Need to wait until proxy installed delta is processed before
         // posting the next delta.
-        while (!fListener.isFinished(STATE_RESTORE_STARTED | STATE_UPDATES | CHILDREN_UPDATES | MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_RESTORE_STARTED | STATE_UPDATES | CHILDREN_UPDATES | MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Post first collapse delta
         fListener.resetModelChanged();
 		model.postDelta(model.makeElementDelta(model.findElement("2"), IModelDelta.COLLAPSE)); //$NON-NLS-1$
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Post second collapse delta
         fListener.resetModelChanged();
 		model.postDelta(model.makeElementDelta(model.findElement("3"), IModelDelta.COLLAPSE)); //$NON-NLS-1$
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Post select delta
 		model.postDelta(model.makeElementDelta(model.findElement("1"), IModelDelta.SELECT)); //$NON-NLS-1$
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         // Wait for all the updates to complete (note: we're not resetting the listener).
-        while (!fListener.isFinished(STATE_RESTORE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(STATE_RESTORE_COMPLETE), createListenerErrorMessage());
 
         // Check to make sure that the state restore didn't change the selection.
 		assertTrue(getInternalViewer().getExpandedState(model.findElement("2")) == false); //$NON-NLS-1$
@@ -1292,7 +1028,7 @@
      * Test for bug 359859.<br>
      * This test verifies that RESTORE state is handled after SAVE previous state was completed
      */
-    public void testSaveRestoreOrder() throws InterruptedException {
+	public void testSaveRestoreOrder() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
         TestModel model = TestModel.simpleMultiLevel();
         model.setDelayUpdates(true);
@@ -1305,11 +1041,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY, true);
 
         // a new similar model
@@ -1319,11 +1051,7 @@
         fListener.reset();
         fListener.expectRestoreAfterSaveComplete();
         fViewer.setInput(copyModel.getRootElement());
-        while (!fListener.isFinished(STATE_RESTORE_STARTED)) {
-            if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-        }
+		waitWhile(t -> !fListener.isFinished(STATE_RESTORE_STARTED), createListenerErrorMessage());
 		assertTrue("RESTORE started before SAVE to complete", fListener.isFinished(STATE_SAVE_COMPLETE)); //$NON-NLS-1$
     }
 
@@ -1331,7 +1059,7 @@
      * This test tries to restore a viewer state while input == null.
      * See: Bug 380288 - NPE switching to the Breakpoints View
      */
-    public void testUpdateWithNullInput() throws InterruptedException {
+	public void testUpdateWithNullInput() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -1340,11 +1068,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         ModelDelta expandedState = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE);
@@ -1356,20 +1080,12 @@
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
         // Wait for the delta to be processed.
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES), createListenerErrorMessage());
 
         fViewer.setInput(null);
         fViewer.updateViewer(expandedState);
 
-        while (!fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING), createListenerErrorMessage());
 
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/UpdateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/UpdateTests.java
index 48e3c71..b665215 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/UpdateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/UpdateTests.java
@@ -16,21 +16,16 @@
 import java.util.LinkedList;
 import java.util.List;
 
-import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.tests.AbstractDebugTest;
+import org.eclipse.debug.tests.TestUtil;
 import org.eclipse.debug.tests.viewer.model.TestModel.TestElement;
 import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 import org.junit.Assert;
 
 /**
@@ -39,62 +34,16 @@
  *
  * @since 3.6
  */
-abstract public class UpdateTests extends AbstractDebugTest implements ITestModelUpdatesListenerConstants {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+abstract public class UpdateTests extends AbstractViewerModelTest implements ITestModelUpdatesListenerConstants {
 
     public UpdateTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
-
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    abstract protected ITreeModelViewer createViewer(Display display, Shell shell);
-
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
-    @Override
-	protected void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (Throwable t) {
-			throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-    }
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
     /**
       * This test:
@@ -102,7 +51,7 @@
      * - replaces the list of elements with a shorter list of elements
      * - refreshes the viewer
      */
-    public void testRemoveElements() throws InterruptedException {
+	public void testRemoveElements() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -111,11 +60,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -135,11 +80,7 @@
         fListener.reset(rootPath, root, -1, false, false);
 
         model.postDelta(new ModelDelta(root, IModelDelta.CONTENT));
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
@@ -149,7 +90,7 @@
      * - sets a list of children to one of the elements
      * - refreshes the viewer
      */
-    public void testAddNewChildren() throws InterruptedException {
+	public void testAddNewChildren() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -158,11 +99,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -184,34 +121,26 @@
 
         // Refresh the viewer
         model.postDelta(new ModelDelta(rootElement, IModelDelta.CONTENT));
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
 
-    private void removeElement(TestModel model, int index, boolean validate) throws InterruptedException {
+	private void removeElement(TestModel model, int index, boolean validate) throws Exception {
         ModelDelta delta = model.removeElementChild(TreePath.EMPTY, index);
 
         // Remove delta should generate no new updates, but we still need to wait for the event to
         // be processed.
         fListener.reset();
         model.postDelta(delta);
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         if (validate) {
             model.validateData(fViewer, TreePath.EMPTY);
         }
     }
 
-    private void addElement(TestModel model, String label, int position, boolean validate) throws InterruptedException {
+	private void addElement(TestModel model, String label, int position, boolean validate) throws Exception {
         ModelDelta delta = model.addElementChild(TreePath.EMPTY, null, position, new TestElement(model, label, new TestElement[0]));
 
         // Remove delta should generate no new updates, but we still need to wait for the event to
@@ -220,22 +149,14 @@
         model.postDelta(delta);
 
         if (validate) {
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
             model.validateData(fViewer, TreePath.EMPTY);
         } else {
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         }
     }
 
-    private void insertElement(TestModel model, String label, int position, boolean validate)  throws InterruptedException {
+	private void insertElement(TestModel model, String label, int position, boolean validate) throws Exception {
         ModelDelta delta = model.insertElementChild(TreePath.EMPTY, position, new TestElement(model, label, new TestElement[0]));
 
         // Remove delta should generate no new updates, but we still need to wait for the event to
@@ -244,22 +165,14 @@
         model.postDelta(delta);
 
         if (validate) {
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
             model.validateData(fViewer, TreePath.EMPTY);
         } else {
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
         }
     }
 
-    public void testRepeatedAddRemoveElement() throws InterruptedException {
+	public void testRepeatedAddRemoveElement() throws Exception {
         //TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer);
 
         TestModel model = TestModel.simpleSingleLevel();
@@ -270,11 +183,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Update the model
@@ -293,7 +202,7 @@
      * updates it initiates the model update sequence before it finishes processing
      * the delta.
      */
-    public void testNotifyUpdatesTartedOnModelChanged() throws InterruptedException {
+	public void testNotifyUpdatesTartedOnModelChanged() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -302,11 +211,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Refresh the viewer so that updates are generated.
@@ -314,11 +219,7 @@
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
         // Wait for the delta to be processed.
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
         Assert.assertTrue( fListener.isFinished(CONTENT_SEQUENCE_STARTED) );
     }
@@ -330,7 +231,7 @@
      * <br>
      * See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=304066">bug 304066</a>
      */
-    public void testContentPlusAddRemoveUpdateRaceConditionsElement() throws InterruptedException {
+	public void testContentPlusAddRemoveUpdateRaceConditionsElement() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -339,11 +240,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Create a listener to listen only to a children count update for the root.
@@ -357,11 +254,7 @@
             childrenCountUpdateListener.addChildreCountUpdate(TreePath.EMPTY);
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
             // Wait until the delta is processed
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
 
             removeElement(model, 5, false);
             removeElement(model, 4, false);
@@ -372,11 +265,7 @@
 
             // Wait until the children count update is completed using the count from
             // before elements were removed.
-            while (!childrenCountUpdateListener.isFinished(CHILD_COUNT_UPDATES)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !childrenCountUpdateListener.isFinished(CHILD_COUNT_UPDATES), createListenerErrorMessage());
 
 			insertElement(model, "1 - " + pass, 0, false); //$NON-NLS-1$
 			insertElement(model, "2 - " + pass, 1, false); //$NON-NLS-1$
@@ -385,11 +274,7 @@
 			insertElement(model, "5 - " + pass, 4, false); //$NON-NLS-1$
 			insertElement(model, "6 - " + pass, 5, false); //$NON-NLS-1$
 
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE), createListenerErrorMessage());
             model.validateData(fViewer, TreePath.EMPTY);
 
         }
@@ -404,7 +289,7 @@
      * <br>
      * See <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=304066">bug 304066</a>
      */
-    public void testInsertAtInvalidIndex() throws InterruptedException {
+	public void testInsertAtInvalidIndex() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -413,11 +298,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Insert element at the end of the list.
@@ -441,11 +322,7 @@
         fListener.reset();
         model.postDelta(delta);
 
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CONTENT_SEQUENCE_COMPLETE | LABEL_SEQUENCE_COMPLETE), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
     }
 
@@ -454,7 +331,7 @@
      * due to a remove event from the model.
      * @see org.eclipse.debug.internal.ui.viewers.model.ModelContentProvider#rescheduleUpdates
      */
-    public void testRescheduleUpdates() throws InterruptedException {
+	public void testRescheduleUpdates() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -463,11 +340,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         for (int i = 0; i < 5; i++) {
@@ -478,11 +351,7 @@
             model.postDelta(new ModelDelta(rootElement, IModelDelta.CONTENT));
 
             // Wait for the delta to be processed.
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES | CHILDREN_UPDATES_STARTED)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES | CHILDREN_UPDATES_STARTED), createListenerErrorMessage());
 
             // Update the model
             removeElement(model, 0, true);
@@ -497,7 +366,7 @@
      * - Process queued updates in order.<br>
      * </p>
      */
-    public void testCanceledUpdates1() throws InterruptedException {
+	public void testCanceledUpdates1() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -506,11 +375,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
 
@@ -522,20 +387,12 @@
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
             // Wait for the delta to be processed.
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED), createListenerErrorMessage());
 
         }
 
         model.setQeueueingUpdate(false);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
     }
 
@@ -546,7 +403,7 @@
      * - Process queued updates in REVERSE order.<br>
      * </p>
      */
-    public void testCanceledUpdates2() throws InterruptedException {
+	public void testCanceledUpdates2() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -555,11 +412,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
 
@@ -571,11 +424,7 @@
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
             // Wait for the delta to be processed.
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED)) {
-				if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
-			}
+			waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES_STARTED), createListenerErrorMessage());
 
         }
 
@@ -586,11 +435,7 @@
         }
 
         model.setQeueueingUpdate(false);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
     }
 
     /**
@@ -600,7 +445,7 @@
      * - Process queued updates in order.<br>
      * </p>
      */
-    public void testCanceledUpdates3() throws InterruptedException {
+	public void testCanceledUpdates3() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -609,11 +454,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
 
@@ -624,22 +465,18 @@
             fListener.reset();
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
+			long start = System.currentTimeMillis();
             // Wait for the delta to be processed.
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)) {
+			while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)
+					&& System.currentTimeMillis() - start < testTimeout) {
                 completeQueuedUpdatesOfType(model, IChildrenCountUpdate.class);
                 completeQueuedUpdatesOfType(model, IHasChildrenUpdate.class);
-                if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
+				TestUtil.processUIEvents();
             }
         }
 
         model.setQeueueingUpdate(false);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
 
     }
 
@@ -650,7 +487,7 @@
      * - Process queued updates in REVERSE order.<br>
      * </p>
      */
-    public void testCanceledUpdates4() throws InterruptedException {
+	public void testCanceledUpdates4() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -659,11 +496,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
 
@@ -674,13 +507,13 @@
             fListener.reset();
             model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
+            long start = System.currentTimeMillis();
             // Wait for the delta to be processed.
-            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)) {
+            while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILDREN_UPDATES_STARTED)
+            		&& System.currentTimeMillis() - start < testTimeout) {
                 completeQueuedUpdatesOfType(model, IChildrenCountUpdate.class);
                 completeQueuedUpdatesOfType(model, IHasChildrenUpdate.class);
-                if (!fDisplay.readAndDispatch ()) {
-					Thread.sleep(0);
-				}
+                TestUtil.processUIEvents();
             }
 
         }
@@ -692,11 +525,7 @@
         }
 
         model.setQeueueingUpdate(false);
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
     }
 
     /**
@@ -707,7 +536,7 @@
      * </p>
      * @see org.eclipse.debug.internal.ui.viewers.model.ModelContentProvider#rescheduleUpdates
      */
-    public void testCancelUpdatesOnRemoveElementWhileUpdatingSubTree() throws InterruptedException {
+	public void testCancelUpdatesOnRemoveElementWhileUpdatingSubTree() throws Exception {
         TestModel model = TestModel.simpleMultiLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -716,11 +545,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         // Refresh the viewer so that updates are generated.
@@ -731,21 +556,13 @@
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
         // Wait for the delta to be processed and child updates for "2" to get started.
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES | CHILDREN_UPDATES_RUNNING)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES | CHILDREN_UPDATES_RUNNING), createListenerErrorMessage());
 
         // Remove element "2"
         removeElement(model, 1, true);
 
         // Wait for all updates to finish.
-        while (!fListener.isFinished(ALL_UPDATES_COMPLETE)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+        waitWhile(t -> !fListener.isFinished(ALL_UPDATES_COMPLETE), createListenerErrorMessage());
     }
 
     /**
@@ -755,7 +572,7 @@
      * - Process queued updates in order.<br>
      * </p>
      */
-    public void testCanceledUpdatesOnSetInput() throws InterruptedException {
+	public void testCanceledUpdatesOnSetInput() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -764,11 +581,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         model.setQeueueingUpdate(false);
@@ -779,21 +592,13 @@
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
         // Wait for the delta to be processed.
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES), createListenerErrorMessage());
 
         TestModel model2 = new TestModel();
 		model2.setRoot(new TestElement(model2, "root", new TestElement[0])); //$NON-NLS-1$
         fViewer.setInput(model2.getRootElement());
 
-        while (!fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING), createListenerErrorMessage());
 
     }
 
@@ -804,7 +609,7 @@
      * - Process queued updates in order.<br>
      * </p>
      */
-    public void testCanceledUpdatesOnSetNullInput() throws InterruptedException {
+	public void testCanceledUpdatesOnSetNullInput() throws Exception {
         TestModel model = TestModel.simpleSingleLevel();
         fViewer.setAutoExpandLevel(-1);
 
@@ -813,11 +618,7 @@
 
         // Set the input into the view and update the view.
         fViewer.setInput(model.getRootElement());
-        while (!fListener.isFinished()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(), createListenerErrorMessage());
         model.validateData(fViewer, TreePath.EMPTY);
 
         model.setQeueueingUpdate(false);
@@ -828,19 +629,11 @@
         model.postDelta(new ModelDelta(model.getRootElement(), IModelDelta.CONTENT));
 
         // Wait for the delta to be processed.
-        while (!fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(MODEL_CHANGED_COMPLETE | CHILD_COUNT_UPDATES), createListenerErrorMessage());
 
         fViewer.setInput(null);
 
-        while (!fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING)) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
+		waitWhile(t -> !fListener.isFinished(CONTENT_COMPLETE | VIEWER_UPDATES_RUNNING), createListenerErrorMessage());
 
     }
 
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerLazyModeTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerLazyModeTests.java
index ac85112..b214725 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerLazyModeTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerLazyModeTests.java
@@ -11,15 +11,12 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
-import org.eclipse.debug.tests.AbstractDebugTest;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.PlatformUI;
 
 /**
  * Tests which verify the operation of the virtual viewer in the lazy mode.
@@ -28,61 +25,22 @@
  *
  *  @since 3.6
  */
-public class VirtualViewerLazyModeTests extends AbstractDebugTest {
-    Display fDisplay;
-    Shell fShell;
-    ITreeModelViewer fViewer;
-    TestModelUpdatesListener fListener;
+public class VirtualViewerLazyModeTests extends AbstractViewerModelTest {
 
     public VirtualViewerLazyModeTests(String name) {
         super(name);
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void setUp() throws Exception {
-		super.setUp();
-        fDisplay = PlatformUI.getWorkbench().getDisplay();
-        fShell = new Shell(fDisplay/*, SWT.ON_TOP | SWT.SHELL_TRIM*/);
-        fShell.setMaximized(true);
-        fShell.setLayout(new FillLayout());
+	@Override
+	protected TestModelUpdatesListener createListener(IInternalTreeModelViewer viewer) {
+		return new TestModelUpdatesListener(viewer, false, false);
+	}
 
-        fViewer = createViewer(fDisplay, fShell);
-
-        fListener = new TestModelUpdatesListener(fViewer, false, false);
-
-        fShell.open ();
-    }
-
-    /**
-     * @param display the display
-     * @param shell the shell
-     * @return the viewer
-     */
-    protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	@Override
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new VirtualTreeModelViewer(display, SWT.VIRTUAL, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 
-    /**
-     * @throws java.lang.Exception
-     */
-    @Override
-	protected void tearDown() throws Exception {
-        fListener.dispose();
-        fViewer.getPresentationContext().dispose();
-
-        // Close the shell and exit.
-        fShell.close();
-        while (!fShell.isDisposed()) {
-			if (!fDisplay.readAndDispatch ()) {
-				Thread.sleep(0);
-			}
-		}
-		super.tearDown();
-    }
-
     public void test() {
         // TODO
     }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerPopupTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerPopupTests.java
index cf0ef1c..06abf21 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerPopupTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerPopupTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
 import org.eclipse.swt.widgets.Display;
@@ -27,7 +27,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell, int style) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell, int style) {
 		return new VirtualTreeModelViewer(fDisplay, style, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerSelectionTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerSelectionTests.java
index f2585da..954894f 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerSelectionTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerSelectionTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
 import org.eclipse.swt.widgets.Display;
@@ -27,7 +27,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerStateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerStateTests.java
index 2530273..aa0ae46 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerStateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerStateTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
 import org.eclipse.swt.widgets.Display;
@@ -27,7 +27,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerUpdateTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerUpdateTests.java
index e0b2029..4ee0797 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerUpdateTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/viewer/model/VirtualViewerUpdateTests.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.eclipse.debug.tests.viewer.model;
 
-import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.IInternalTreeModelViewer;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
 import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer;
 import org.eclipse.swt.widgets.Display;
@@ -27,7 +27,7 @@
     }
 
     @Override
-	protected ITreeModelViewer createViewer(Display display, Shell shell) {
+	protected IInternalTreeModelViewer createViewer(Display display, Shell shell) {
 		return new VirtualTreeModelViewer(fDisplay, 0, new PresentationContext("TestViewer")); //$NON-NLS-1$
     }
 }