blob: 80ec21ae0a0380f11433b5c3c5115971efbbe058 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 SAP AG, Walldorf.
* 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:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.platform.discovery.testutils.utils.abbot.util.internal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.platform.discovery.testutils.internal.plugin.TestPlugin;
import org.eclipse.platform.discovery.testutils.utils.abbot.ActiveSWTTestSuite;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import abbot.swt.Robot;
/**
* Tool class providing the functionality to clean up the UI after a test has
* been called. A typical scenario is to close all modal dialogs (which may have
* not been expected by the test and hence have made it fail) to ensure that the
* UI thread is not blocked and that following UI tests can start in a defined
* state. Note that however there is no mean to avoid all interference between
* UI test, just for the sheer complexity of the UI.
* <p>
* For the possible cleanup actions, see the
* {@link UICleanupManager.CleanupMethod} constants.
* </p>
* <p>
* This class is not public since it has inherited design flaws from legacy
* classes using this class. Specifically, the fact that calls to
* <code>setCleanupMethod</code> must be permitted after
* <code>registerUIState</code> entails a potentially inefficient
* implementation. However, not being public it can be easily refactored if
* functionality of this class shall be made available in other projects.
* </p>
*
* @see ActiveSWTTestSuite
*/
public class UICleanupManager /* implements ISideEffectCleanupManager */{
/** Selected cleanup method. */
private CleanupMethod cleanupMethod;
/** List of shells that were open before the test. */
private Set<Shell> shellsBeforeExecution = null;
/** Name of test using this manager (for debug output). */
private String testDebugName;
/**
* Approaches for cleaning up spurious UI elements (modal dialogs, context
* menus, etc.) after the test execution.
*/
public static enum CleanupMethod {
/**
* Register open shells before the test is executed and call
* <code>close()</code> on every shell that has been left open by the
* test, in addition to sending escapes.
* <p>
* Warning: This method is known to cause problems with the Graphical
* Editing Framework (GEF) as of Eclipse version 3.3, since GEF stores a
* shell in a static member.
* </p>
*/
SHELL_CLOSE_AND_ESCAPES,
/**
* Send escape keys as a heuristic to close dialogs and context menus
* after the test execution.
*/
ESCAPES
}
/**
* Constructs a manager instance using the most widely applicable cleanup
* method.
*/
public UICleanupManager() {
this(CleanupMethod.ESCAPES);
}
/**
* Constructs a manager instance with the specified cleanup method.
*
* @param the
* cleanup method to use. Must not be <code>null</code>.
*/
public UICleanupManager(final CleanupMethod method) {
if (method == null) {
throw new NullPointerException("The method parameter must not be null.");
}
this.cleanupMethod = method;
}
/**
* Call this method before executing a test.
*/
public void registerUIState() {
// register currently open shells; NB: this step must be done
// irrespective of the active cleanup method because users of this
// class require that setCleanupMethod may still be called after
// calling this method
final Display display = Display.getDefault();
display.syncExec(new Runnable() {
public void run() {
UICleanupManager.this.registerOpenShells(display);
}
});
}
/**
* Call this method after executing a test in order to provide a "clean" UI
* before the next test is started.
*
* @throws IllegalStateException
* if {@link #registerUIState()} has not yet been called.
*/
public void cleanUp() throws IllegalStateException {
if (shellsBeforeExecution == null)
throw new IllegalStateException("registerUIState() must be called before cleanUp().");
if (cleanupMethod == CleanupMethod.SHELL_CLOSE_AND_ESCAPES) {
// explicitly close those shells that were not open before the test method was run
final Display display = Display.getDefault();
display.syncExec(new Runnable() {
public void run() {
UICleanupManager.this.closeLeftOverShells(display);
}
});
}
switch (cleanupMethod) {
case SHELL_CLOSE_AND_ESCAPES:
case ESCAPES:
// attempt to close dialogs and context menus
final Robot robot = new Robot(PlatformUI.getWorkbench().getDisplay());
for (int i = 0; i < 25; i++) {
robot.key(SWT.ESC);
}
}
}
/**
* Stores the currently open shells. Must be called within the UI thread.
*
* @param display
* the default display.
*/
private void registerOpenShells(final Display display) {
final Shell[] currentShells = display.getShells();
shellsBeforeExecution = new HashSet<Shell>(Arrays.asList(currentShells));
}
/**
* Closes shells that were not open when {@link #registerUIState()} was
* called. Must be called within the UI thread.
*
* @param display
* the default display.
*/
private void closeLeftOverShells(final Display display) {
final Shell[] openShells = display.getShells();
for (final Shell s : openShells) {
if (!shellsBeforeExecution.contains(s)) {
TestPlugin.logWarning("WARNING: " + testDebugName
+ ": There is a shell open which has not been open when the test was started: " + s);
ActiveSWTTestSuite.closeShells(new Shell[] { s });
}
}
}
public CleanupMethod getCleanupMethod() {
return cleanupMethod;
}
public void setCleanupMethod(final CleanupMethod method) throws IllegalStateException {
this.cleanupMethod = method;
}
}