How to close shells left open at the end of SWTBot test execution

aurelien.pupier's picture
aurelien.pupier
Blog Categories: 

When executing UI tests, such as SWTBot tests, when a test fails some popups might stay open and cause subsequent tests to fail. Even worse, the whole test suite execution can take longer than usual because all the following tests might fail after waiting for a timeout (of 5 seconds by default). It will also kill the visibility of the scope of impacted components. In order to avoid this quirk, we are using a custom TestSuite to register a custom JUnit Runlistener *on the *RunNotifier. After all these abstracts words, let’s see in details how to implement it.

Register the listener

You have to extend the Suite class and then to register the listener on the RunNotifier.

public class CloseShellSuite extends Suite {

    @Override
    public void run(final RunNotifier runNotifier) {
        final RunListener listener = CleanShellListener.getInstance();
        runNotifier.removeListener(listener);
        runNotifier.addListener(listener);
        super.run(runNotifier);
    }

    protected CloseShellSuite(final Class<?> klass, final Class<?>[] suiteClasses) throws InitializationError {
        super(klass, suiteClasses);
    }

Implement the RunListener

You need to override the testFinished method. Inside this method, you can access the SWTWorkbenchBot to be able to close all open shells - taking care not to close the main Eclipse shell - and also close all editors in order to get back to a clean environment.

public class CleanShellListener extends RunListener {

    @Override
    public void testFinished(final Description description) throws Exception {
        final SWTWorkbenchBot bot = new SWTWorkbenchBot();
        try {
            closeAllShells(bot, description);
            bot.saveAllEditors();
            bot.closeAllEditors();
        } catch (final Exception e) {
            BonitaStudioLog.log("| Fails to clean shells after test : " + description.getMethodName());
            BonitaStudioLog.log("|====================================================");
            return;
        }
    }

    private void closeAllShells(final SWTWorkbenchBot bot, final Description description) {
        final SWTBotShell[] shells = bot.shells();
        for (final SWTBotShell shell : shells) {
            if (shell.isOpen() && !isEclipseShell(shell)) {
                bot.captureScreenshot("screenshots/ShellOpenedAfter" + description.getMethodName() + ".jpg");
                shell.close();
                BonitaStudioLog.log("/!\\ Shell " + shell + " has been closed automatically,"
                        + "please fix the corresponding test (" + description.getMethodName() + ")"
                        + " to close it in @After (see screenshots)");
            }
        }
    }

    @SuppressWarnings("boxing")
    public static boolean isEclipseShell(final SWTBotShell shell) {
        return UIThreadRunnable.syncExec(new BoolResult() {

            public Boolean run() {
                return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
                        .getShell() == shell.widget;
            }
        });
    }

    private static CleanShellListener INSTANCE = null;

    private CleanShellListener() {
    };

    public static CleanShellListener getInstance() {
        if (INSTANCE == null) {
            synchronized (CleanShellListener.class) {
                if (INSTANCE == null) {
                    INSTANCE = new CleanShellListener();
                }
            }
        }
        return INSTANCE;
    }

Notice that we take a screenshot in the case of a remaining window. This is a big help for debugging.

This article is based on a simplified overview of code used at Bonitasoft. You can have a closer look on our Github repository.

Notifications