/******************************************************************************* * 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.internal.junit; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.widgets.Display; import com.windowtester.internal.debug.LogHandler; import com.windowtester.internal.runtime.Platform; import com.windowtester.internal.runtime.junit.core.AbstractExecutionMonitor; import com.windowtester.internal.runtime.junit.core.ITestIdentifier; import com.windowtester.runtime.IUIContext; import com.windowtester.runtime.WidgetSearchException; import com.windowtester.runtime.monitor.IUIThreadMonitor; import com.windowtester.runtime.swt.internal.settings.TestSettings; import com.windowtester.runtime.util.ScreenCapture; /** * Monitor for executing SWT tests. * */ public class SWTExecutionMonitor extends AbstractExecutionMonitor { private class ScreenShotHandler { private void takeScreenshotIfNecessary(Throwable e) { if (!previouslyHandled(e)) ScreenCapture.createScreenCapture(); } //widget search exceptions trigger screen shots before reaching here //TODO: a thought: we could tag exceptions as having an associated screen shot... private boolean previouslyHandled(Throwable e) { return e instanceof WidgetSearchException; } } private final ScreenShotHandler screenShotHandler = new ScreenShotHandler(); //used to cache test environment details private final ExecutionEnvironment environment; //a holder for cached automation values -- should probably get moved private boolean automatedMode; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Instance Creation // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public SWTExecutionMonitor() { //create environment info holder this.environment = new ExecutionEnvironment(); //build cleanup handler setCleanupHandler(new SWTCleanupHandler(environment, getState())); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Test Lifecycle // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ITestExecutionListener#testStarting(com.windowtester.runtime.test.TestIdentifier) */ public void testStarting(ITestIdentifier identifier) { //System.out.println("monitor starting test: " + identifier); //first cache environment environment.update(); //TODO: this should probably get moved to a listener setAutomationFlags(); //update settings scope TestSettings.getInstance().push(); //next inform listeners, etc. super.testStarting(identifier); } /** * Set automation flags. */ private void setAutomationFlags() { // wrapped call in syncExec, to prevent NPE while // setting flag Display.getDefault().syncExec(new Runnable(){ public void run(){ automatedMode = ErrorDialog.AUTOMATED_MODE; ErrorDialog.AUTOMATED_MODE = false; } }); } /** * Reset automation flags (to previous value). */ private void resetAutomationFlags() { ErrorDialog.AUTOMATED_MODE = automatedMode; } //@Override /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.AbstractExecutionMonitor#testFinished() */ public void testFinished() { //notify listeners super.testFinished(); //update settings scope TestSettings.getInstance().pop(); //reset automation mode resetAutomationFlags(); } /* (non-Javadoc) * @see com.windowtester.internal.runtime.junit.core.AbstractExecutionMonitor#exceptionCaught(java.lang.Throwable) */ public void exceptionCaught(Throwable e) { takeScreenShotIfNecessary(e); cleanUp(); //cleanup so that the exception gets properly propagated super.exceptionCaught(e); } private void takeScreenShotIfNecessary(Throwable e) { screenShotHandler.takeScreenshotIfNecessary(e); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Wait Loop Management // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //@Override /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.AbstractExecutionMonitor#doWaitForFinish() */ protected void doWaitForFinish() { Display display = getDisplay(); // if (noUI()) // return; try { if (!display.isDisposed() && !display.readAndDispatch()) { doSleep(display); } } catch (SWTException ex) { /* ignore device/widget disposal errors -- these are just unavoidable */ if (ex.code == SWT.ERROR_DEVICE_DISPOSED || ex.code == SWT.ERROR_WIDGET_DISPOSED) return; // Do nothing: rethrowing errors blocks the display thread. // One must rely on the fact of proper error handling of // display thread users. //!pq: this was the abbot assumption, but is it right? why not cache and fail later? LogHandler.log("Exception caught in SWTExecutionMonitor.waitUntilFinished():"); LogHandler.log(ex); } catch (Throwable t) { LogHandler.log("Exception caught in SWTExecutionMonitor.waitUntilFinished():"); LogHandler.log(t); } } private void doSleep(Display display) throws InterruptedException { //display.sleep is not safe in the pure SWT case if (!Platform.isRunning()) Thread.sleep(100); else display.sleep(); } //TODO: investigate // private boolean noUI() { // final Display display = getDisplay(); // Shell[] shells = (Shell[]) DisplayExec.sync(new RunnableWithResult(){ // public Object runWithResult() { // return display.getShells(); // } // }); // return shells.length == 0; // } //@Override /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.AbstractExecutionMonitor#terminateWaitForFinish() */ protected boolean terminateWaitForFinish() { Display display = getDisplay(); return display == null || display.isDisposed(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Test Monitor Instantiation // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * 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 IUIThreadMonitor getUIThreadMonitor() { return (IUIThreadMonitor) getUI().getAdapter(IUIThreadMonitor.class); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Accessors // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get the current display. * @return the current display */ private Display getDisplay() { return environment.getDisplay(); } /** * Get the UI Context. */ public IUIContext getUI() { return environment.getUI(); } }