/******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.runtime.swt.condition.shell; import org.eclipse.swt.widgets.Display; import com.windowtester.runtime.internal.condition.IConditionWithIdle; import com.windowtester.runtime.swt.internal.widgets.DisplayReference; /** * Waits for a test condition to be true and then waits for the display to * finish processing all asynchronous messages. */ public abstract class TestAndWaitForIdleCondition implements IConditionWithIdle { private enum TestStates { INIT, TEST, WAIT, DONE } /** * The current state. Synchronize against {@link #LOCK} before accessing this field */ private TestStates state = TestStates.INIT; /** * Synchronize against this field before accessing {@link #state} */ private final Object LOCK = new Object(); private int waitForIdleCount = 0; /** * Determine if the condition has been satisfied and if the display has finished * processing all asynchronous messages. */ public final boolean testAndWaitForIdle() { switch (getState()) { case INIT : initTest(); setState(TestStates.TEST); case TEST : if (test()) { setState(TestStates.WAIT); startWaitForIdle(); } return false; case WAIT : return false; case DONE : return true; default : throw new IllegalStateException(); } } /** * Subclasses may override to perform first time condition testing initialization or * to log a one time trace message. */ protected void initTest() { // Do nothing } /** * Determine if the condition has been satisfied. * <p> * Note that this method is NOT guaranteed to be executed on the UI thread. In fact, * it is most likely to be executed on the test thread. If you want to conveniently * execute a test on the SWT UI thread, subclass * com.windowtester.runtime.swt.condition.SWTUIConditionAdapter. * * @return <code>true</code> if the condition is satisfied, else <code>false</code> * * @see com.windowtester.runtime.condition.ICondition#test() */ public abstract boolean test(); /** * Start the wait for idle process by queuing a {@link Display#asyncExec(Runnable)} * runnable to detect when the display has finished the shell activation process. If * this is called from the UI thread, then wait for idle won't work so don't wait and * simply set the {@link #state} to {@link TestStates#DONE} */ private void startWaitForIdle() { final Display display = DisplayReference.getDefault().getDisplay(); if (display.getThread() == Thread.currentThread()) setState(TestStates.DONE); else waitForIdle(display); } /** * Queue a {@link Display#asyncExec(Runnable)} runnable to detect when the display has * finished the shell activation process */ private void waitForIdle(final Display display) { if (++waitForIdleCount > 5) System.out.println(getClass().getSimpleName() + " waiting for idle " + waitForIdleCount); display.asyncExec(new Runnable() { public void run() { if (!display.isDisposed() && display.readAndDispatch()) waitForIdle(display); else setState(TestStates.DONE); } }); } private TestStates getState() { synchronized (LOCK) { return state; } } private void setState(TestStates state) { synchronized (LOCK) { this.state = state; } } }