/******************************************************************************* * 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 junit.framework.AssertionFailedError; import org.eclipse.swt.SWTException; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import com.windowtester.internal.debug.IRuntimePluginTraceOptions; import com.windowtester.internal.debug.LogHandler; import com.windowtester.internal.debug.TraceHandler; import com.windowtester.internal.runtime.junit.core.ITestCleanupHandler; import com.windowtester.internal.runtime.junit.core.AbstractExecutionMonitor.RunningState; import com.windowtester.runtime.swt.internal.finder.ShellFinder; import com.windowtester.runtime.swt.internal.operation.SWTShowMenuOperation; import com.windowtester.runtime.swt.internal.os.IAccessibleWindow; import com.windowtester.runtime.swt.internal.os.IOSDelegate; import com.windowtester.runtime.swt.internal.os.InvalidOSDelegate; import com.windowtester.runtime.swt.internal.os.OSDelegate; import com.windowtester.runtime.swt.internal.widgets.DisplayReference; import com.windowtester.runtime.util.ScreenCapture; import com.windowtester.runtime.util.TestMonitor; /** * Cleanup handler for SWT tests. * */ public class SWTCleanupHandler implements ITestCleanupHandler { /** * A constant used to specify how many levels of menu to dismiss in * {@link #dismissUnexpectedMenus()}. */ // private static final int MAX_MENU_DEPTH = 5; /** * A flag to indicate whether a test should FAIL when menus/shells * are dismissed/closed at the end of a test. */ private final boolean UNEXPECTED_SHELLS_MENUS_TREATED_AS_FAILURES = false; private final ExecutionEnvironment _environment; private final RunningState _runningState; // ////////////////////////////////////////////////////////////////////////// // // Cleanup // // ////////////////////////////////////////////////////////////////////////// public SWTCleanupHandler(ExecutionEnvironment environment, RunningState state) { _environment = environment; _runningState = state; } /* (non-Javadoc) * @see com.windowtester.runtime.test.exec.ICleanupHandler#cleanUp() */ public void cleanUp() { closeNativeDialogs(); dismissUnexpectedMenus(); closeUnexpectedShells(); } private void closeNativeDialogs() { /* * Added guard to catch and log exceptions generated by the native window services. */ try { IOSDelegate os = OSDelegate.getCurrent(); if (os instanceof InvalidOSDelegate) return; if (ShellFinder.getActiveShell(Display.getDefault()) != null) return; IAccessibleWindow[] dialogs = os.getWindowService().getNativeDialogs(); for (int i = 0; i < dialogs.length; i++) { dialogs[i].close(); } } catch(Throwable e) { LogHandler.log(e); } } /** * Called at the end of the test to forcefully close shells that should not be open. * and guarantee that no (blocking) dialogs are still open. By default, this method * closes any shells descendent from the root shell but not the root shell itself. This * is necessary so that the junit thread can proceed. Subclasses may override or * extend to close a different set of shells. * * @see #setRootShell(Shell) */ protected void closeUnexpectedShells() { closeDecendentShells(getRootShell()); } /** * Called at the end of the test to dismiss menus that were left open at the end of * the test. * */ protected void dismissUnexpectedMenus() { // //if a menu is open, take a screenshot and dismiss // if (MenuWatcher.getInstance(getDisplay()).isMenuOpen()) { // TraceHandler.trace(IRuntimePluginTraceOptions.UI_THREAD_MONITOR, "Dismissing menu left open at end of test"); // if (UNEXPECTED_SHELLS_MENUS_TREATED_AS_FAILURES) { // createScreenCapture("unexpected menu"); // //register an exception if there isn't one already //// if (_ite == null) //// _ite = new InvocationTargetException(new AssertionFailedError("Menu left open at end of test")); // _runningState.setException(new AssertionFailedError("Menu left open at end of test")); // } // // //TODO: this may be OS-specific... // for (int i= 0; i <= MAX_MENU_DEPTH; ++i) { // //getUIContext().keyClick(SWT.ESC); //close menu by hitting ESCAPE // new UIDriver().keyClick(SWT.ESC); //quick hack... fix me // } // } if (!getDisplay().isDisposed()) { try { DisplayReference.getDefault().closeAllMenus(); } catch (SWTException e) { // If display is disposed, then ignore exception because this is just cleanup // otherwise we want to know about any problems. if (!getDisplay().isDisposed()) throw e; } } } /** * Forcefully close any shells decendent from the specified shell but does not close * the specified shell itself. * * @param shell the shell */ protected void closeDecendentShells(final Shell shell) { // guard against case where display is already disposed Display display = getDisplay(); if (!display.isDisposed() && shell != null && !shell.isDisposed()) display.syncExec(new Runnable() { public void run() { if (!shell.isDisposed()) { // Close all blocking dialogs. Necessary, otherwise the junit // thread does not proceed. Shell[] shells = shell.getShells(); closeShells(shells); } } private void closeShells(final Shell[] shells) { for (int i = 0; i < shells.length; i++) { if (!shells[i].isDisposed()) { closeShells(shells[i].getShells()); } if (!shells[i].isDisposed()) { if (UNEXPECTED_SHELLS_MENUS_TREATED_AS_FAILURES) { // we specifically care about modal shells... if (ShellFinder.isModal(shells[i])) { createScreenCapture("forcing shell close: " + shells[i].getText()); /* * If there is no error associated, signal * one */ _runningState.setException(new AssertionFailedError( "Shell left open at end of test")); } } shells[i].close(); // shells[i].dispose(); } } } }); } Display getDisplay() { return _environment.getDisplay(); } Shell getRootShell() { return _environment.getRootShell(); } private void createScreenCapture(String desc) { TraceHandler.trace(IRuntimePluginTraceOptions.CONDITIONS, "Creating screenshot ("+ desc +") for testcase: " + getId()); ScreenCapture.createScreenCapture(getId()); } /** * The test identifier for screen captures * * @return the test id (not <code>null</code>) */ private String getId() { return TestMonitor.getInstance().getCurrentTestCaseID(); } }