Thursday, August 29, 2013

Create Test Suite for Tycho after login

Sometimes tests fail when they are not actually running. This typically happens in case of integration-tests. When something goes wrong(like login failed) the consequent tests all get failed and the build server starts flooding with the infamous All tests failed! mails!

What to do in such case?
Should we run the tests if the login failed even if they were supposed to run after a successful login?

We faced this recently with our RCP application which connects to a remote server to properly function.
We have around 50 SWTBot ui-tests for testing the features. The tests are run in our CI server.
SWTBot first logs onto the server using a login dialog.
But when the login fails we don't want to run the tests at all, here's what we did:

We created a TestSuite which returns all tests only when login succeeds.
Of course it can be customized further to give more logic to load each test case (like different test cases for different platforms!)

package package.of.test.suite;

import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;

import org.apache.log4j.Logger;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;

/*
 * A test suite which creates tests depending on whether the login succeeded or not.
 * Return all tests if login succeeds
 * Else return a suite containing no tests.
 */
public class AllTests extends TestSuite {

    private static boolean loggedIn = initializeProductForTesting();
    private static Logger  mLogger  = Logger.getLogger(AllTests.class);
    private static SWTWorkbenchBot bot;
    public static boolean initializeProductForTesting() {
        System.out.println("in Beforeclass method before all tests!");
        Utils.login();
        return Utils.isLoggedIn();
    }

    public static TestSuite suite() {
        TestSuite suite = new TestSuite();
        if (loggedIn) {
            suite.addTest(new JUnit4TestAdapter(Test1.class));
            suite.addTest(new JUnit4TestAdapter(Test2.class));
            suite.addTest(new JUnit4TestAdapter(Test3.class));
        }
        else {
            mLogger.debug("Can't log into server!");
            mLogger.debug("No tests will run !");
            Display.getDefault().close();
            System.exit(0);
        }
        return suite;
    }
}

We configure tycho-surefire plugin to run this suite
<configuration>
                    ...
                    <testSuite>bundle.symbolic.name.of.test.plugin</testSuite>
                    <testClass>package.of.test.suite.AllTests</testClass>
                    ...
</configuration>

Tuesday, August 27, 2013

How to stop massive logging by Birt Chart

While config for birt report gives direct api(EngineConfig) to change logging level birt chart doesn't.
Here's an alternative
            PlatformConfig config = new PlatformConfig( );
            config.setProperty(PluginSettings.PROP_LOGGING_DIR, null);
            config.setProperty(PluginSettings.PROP_LOGGING_LEVEL, Level.SEVERE);

This should stop mass logging by birt chart.

Tuesday, August 20, 2013

How to cancel an SWT key event?

KeyEvent in SWT has a doit member variable.
Setting it to false cancels any further handling of the event.

public void keyPressed(KeyEvent e) {
              handleKeyPress(e);
              e.doit = false;// cancels the event
}

Sunday, August 18, 2013

SWTUtils doesn't support invoking methods with parameter

SWTUtils.invokeMethod allows you to invoke only methods with no parameters.

Here's a customized version of the method that allows you to invokes methods on objects with parameters

    public static Object invokeMethod(final Object object, String methodName,
            final Object[] params) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException {
        Class[] paramTypes = getParamTypes(params);
        final Method method = object.getClass().getMethod(methodName,
                paramTypes);
        Widget widget = null;
        final Object result;
        if (object instanceof Widget) {
            widget = (Widget) object;
            result = UIThreadRunnable.syncExec(widget.getDisplay(),
                    new Result<Object>() {
                        @Override
                        public Object run() {
                            try {
                                return method.invoke(object, params);
                            } catch (Exception niceTry) {
                            }
                            return null;
                        }
                    });
        } else {
            result = method.invoke(object, params);
        }

        return result;
    }

Still this method only supports Object[] parameter, it doesn't support any primitive parameter like int, boolean.



VoidResult and its use

asyncExec method of UIThreadRunnable takes a VoidResult parameter.

It's useful for running code in UIThread which involves inter thread communication.

VoidResult interface has a method run().
Put the code in run() you want to execute in UI Thread.
for example you want to call setMaximized(..) method on a Shell you can do this.

Maximize Eclipse active window from SWTBot

Here's a simple way of achieving above
   
private void maximizeActiveWindow() {
        final Shell activeShell = bot.activeShell().widget;
        VoidResult maximizeShell = new VoidResult() {
            @Override
            public void run() {
                    activeShell.setMaximized(true);
            }
        };
        syncExec(maximizeShell);
    }

There's another way of doing above here.