/******************************************************************************* * 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.internal.runtime.junit.core; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.windowtester.runtime.WaitTimedOutException; import com.windowtester.runtime.monitor.IUIThreadMonitor; import com.windowtester.runtime.monitor.IUIThreadMonitorListener; import com.windowtester.runtime.util.ScreenCapture; import com.windowtester.runtime.util.TestMonitor; /** * Base class for execution monitor implementations. * */ public abstract class AbstractExecutionMonitor implements IExecutionMonitor, IUIThreadMonitorListener { /** * Manages and maintains running state information. * */ public class RunningState { /** * Flag used to signal when the execution is finished. */ private boolean _isRunning; private TestExceptionCache _exceptionCache = new TestExceptionCache(); void setIsRunning(boolean isRunning) { if (_isRunning && isRunning) throw new IllegalStateException("test is already running"); if (!_isRunning && !isRunning) throw new IllegalStateException("test is already stopped"); _isRunning = isRunning; } public void setException(Throwable e) { _exceptionCache.cache(e); } TestExceptionCache getExceptions() { return _exceptionCache; } public boolean isRunning() { return _isRunning; } public boolean isExceptional() { return _exceptionCache.hasException(); } public void throwException() throws Throwable { _exceptionCache.throwException(); } } /** * Used to track whether a test is already running or not. */ private final RunningState _runningState = new RunningState(); /** * Collection of execution listeners. */ private final List<ITestExecutionListener> _listeners = new ArrayList<ITestExecutionListener>(); /** * Used to cleanup post test run. */ private ITestCleanupHandler _cleanupHandler; /** * Set the cleanup handler. * @param handler the handler */ protected void setCleanupHandler(ITestCleanupHandler handler) { _cleanupHandler = handler; } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.IExecutionMonitor#addListener(com.windowtester.runtime.test.exec.ITestExecutionListener) */ public void addListener(ITestExecutionListener listener) { _listeners.add(listener); } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.IExecutionMonitor#removeListener(com.windowtester.runtime.test.exec.ITestExecutionListener) */ public void removeListener(ITestExecutionListener listener) { _listeners.remove(listener); } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#testFinished() */ public void testFinished() { //notify listeners for (Iterator<ITestExecutionListener> iter = _listeners.iterator(); iter.hasNext();) { ITestExecutionListener listener = iter.next(); listener.testFinished(); } //update monitors stopTestMonitors(); //<-- note: could be listeners? } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#testFinishing() */ public void testFinishing() { //notify listeners for (Iterator<ITestExecutionListener> iter = _listeners.iterator(); iter.hasNext();) { ITestExecutionListener listener = iter.next(); listener.testFinishing(); } //THEN: cleanup try { cleanUp(); } finally { //finally, update state _runningState.setIsRunning(false); } } /** * Cleanup test. Called post test finish. * */ protected void cleanUp() { if (_cleanupHandler != null) _cleanupHandler.cleanUp(); } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#testStarting(com.windowtester.runtime.test.TestIdentifier) */ public void testStarting(ITestIdentifier identifier) { //update state _runningState.setIsRunning(true); //notify listeners for (Iterator<ITestExecutionListener> iter = _listeners.iterator(); iter.hasNext();) { ITestExecutionListener listener = iter.next(); listener.testStarting(identifier); } } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#testStarted(com.windowtester.runtime.test.TestIdentifier) */ public void testStarted(ITestIdentifier identifier) { /* * TODO[pq]: This is not getting called which means monitors are not being * started here. --- For Junit4 support to work, this will need to get remedied! */ //update monitors startTestMonitors(); //<-- note: could be listeners? } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#exceptionCaught(java.lang.Throwable) */ public void exceptionCaught(Throwable e) { //update state _runningState.setException(e); //notify listeners for (Iterator<ITestExecutionListener> iter = _listeners.iterator(); iter.hasNext();) { ITestExecutionListener listener = iter.next(); listener.exceptionCaught(e); } } /** * Get the active running state of this execution. * @return the running state */ protected RunningState getState() { return _runningState; } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.AbstractExecutionMonitor#waitUntilFinished() */ public void waitUntilFinished() throws Throwable { RunningState state = getState(); while (state.isRunning() && !state.isExceptional() &&!terminateWaitForFinish()/* && !display.isDisposed() */) { //System.out.println("calling doWait"); doWaitForFinish(); } //rethrow any caught exceptions state.throwException(); } /** * Do the wait in the <code>waitUntilFinished</code> loop. * <p> * To be provided by subclasses. */ protected abstract void doWaitForFinish(); /** * Signal that the wait loop should be terminated. Used in exceptional conditions: for instance, the display is disposed. * <p> * To be provided by subclasses. * @return <code>true</code> if the <code>waitUntilFinished</code> loop should be terminated. */ protected abstract boolean terminateWaitForFinish(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Test Monitor Management // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Start test monitors. */ private void startTestMonitors() { //TODO ...move this monitor to runtime and refactor //TestMonitor.getInstance().beginTestCase(this); startUIMonitor(); } /** * Stop test monitors. */ private void stopTestMonitors() { //?:: TestMonitor.getInstance().endTestCase(); stopUIMonitor(); } /** * Start monitoring the UI for responsiveness. */ protected void startUIMonitor() { getUIThreadMonitor().setListener(this); } /** * Stop monitoring the UI for responsiveness. */ protected void stopUIMonitor() { getUIThreadMonitor().setListener(null); } /** * Answer the user interface thread monitor used to determine if the user interface * thread is idle or unresponsive longer than some expected time. * * @return the user interface thread monitor (not <code>null</code>) */ protected abstract IUIThreadMonitor getUIThreadMonitor(); //TODO: implement this! /* (non-Javadoc) * @see com.windowtester.swt.monitor.IUIThreadMonitorListener#uiTimeout(boolean) */ public void uiTimeout(boolean isResponsive) { //System.out.println("timeout happened"); //from UITestCase: // // do a screenshot before shutting down // ScreenCapture.createScreenCapture(getId()); // // TODO [author=Dan] How can shells be closed or the test be stopped if UI thread is busy? // // TODO [author=Dan] Can close shells be done by the closeUnexpectedShells method? // if (isIdle) // new ExceptionHandlingHelper(getDisplay(), true).closeOpenShells(); //cache exception --- note that this will abort wait loop getState().setException(new InvocationTargetException(new WaitTimedOutException("UI thread idle timeout"))); //do a screenshot before shutting down -- can we push this into the cleanup handler? ScreenCapture.createScreenCapture(); //we can do better than this... //NOTE: shouldn't need to do the closing of shells, it will be handled later.... } /** * Get the exception cache for this monitor. */ public TestExceptionCache getExceptionCache() { return getState().getExceptions(); } }