/*******************************************************************************
* 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();
}
}