Sunday, July 18, 2010

WizardDialogTestRunner for SWTBot

I think SWTBot is a great framework for test on SWT Application like Eclipse.
So I always use this framework for my plugins development.
But there are no WizardDialog Helper class.
So I implemented it below.
package org.eclipse.swtbot.jface;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.eclipse.swtbot.swt.finder.results.VoidResult;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.TestClass;
public class WizardDialogTestRunner extends SWTBotJunit4ClassRunner {
private IWizard newWizard = null;
public WizardDialogTestRunner(Class<?> klass) throws Exception {
super(klass);
}
@Override
protected void runChild(final FrameworkMethod method, final RunNotifier notifier) {
Shell parentShell = new Shell();
try {
newWizard = newWizard();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
final boolean[] finishPressed = new boolean[1];
final WizardDialog dialog = new WizardDialog(parentShell, newWizard) {
@Override
protected void finishPressed() {
super.finishPressed();
finishPressed[0] = true;
}
};
final Exception[] ex = new Exception[1];
final boolean[] done = new boolean[1];
Runnable r = new Runnable() {
public void run() {
try {
WizardDialogTestRunner.super.runChild(method, notifier);
} catch (Exception e) {
ex[0] = e;
} finally {
if (dialog.getShell() != null && dialog.getShell().isDisposed() == false) {
UIThreadRunnable.syncExec(new VoidResult() {
public void run() {
dialog.close();
}
});
}
done[0] = true;
}
}
};
Thread nonUIThread = new Thread(r);
nonUIThread.setName("Runnning Test Thread");//$NON-NLS-1$
nonUIThread.start();
dialog.open();
waitForNonUIThreadFinished(parentShell, nonUIThread);
if (ex[0] != null) {
throw new IllegalStateException(ex[0]);
}
}
private void waitForNonUIThreadFinished(Shell parentShell, Thread nonUIThread) {
Display display = parentShell.getDisplay();
while (nonUIThread.isAlive()) {
try {
if (!display.readAndDispatch()) {
display.sleep();
}
} catch (Throwable e) {
}
}
}
@Override
protected Object createTest() throws Exception {
Object test = super.createTest();
if (newWizard != null) {
try {
Field field = test.getClass().getField("wizard");
field.set(test, newWizard);
} catch (NoSuchFieldException e) {
}
}
return test;
}
private IWizard newWizard() throws InstantiationException, IllegalAccessException {
TestClass testClass = getTestClass();
Annotation[] annotations = testClass.getAnnotations();
IWizard newWizard = null;
for (Annotation annotation : annotations) {
if (annotation instanceof WithWizard) {
WithWizard wizardAnnotation = (WithWizard) annotation;
Class<? extends IWizard> value = wizardAnnotation.value();
newWizard = value.newInstance();
}
}
return newWizard;
}
}

package org.eclipse.swtbot.jface;
import java.lang.annotation.*;
import org.eclipse.jface.wizard.IWizard;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface WithWizard {
Class<? extends IWizard> value();
}
view raw WithWizard.java hosted with ❤ by GitHub


And this is a sample code how to implemente bu using these class.
package org.kompiro.jamcircle.kanban.ui.wizard;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.File;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swtbot.jface.WithWizard;
import org.eclipse.swtbot.jface.WizardDialogTestRunner;
import org.eclipse.swtbot.swt.finder.SWTBot;
import org.eclipse.swtbot.swt.finder.waits.Conditions;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotText;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kompiro.jamcircle.kanban.ui.Messages;
@RunWith(WizardDialogTestRunner.class)
@WithWizard(CSVExportWizard.class)
public class CSVExportWizardTest {
private SWTBot bot;
private SWTBotShell target;
public CSVExportWizard wizard;
private IRunnableWithProgress runner;
@Before
public void before() {
bot = new SWTBot();
target = bot.shell(Messages.CSVExportWizard_title);
runner = mock(IExportRunnableWithProgress.class);
wizard.setRunner(runner);
target.activate();
}
@Test
public void show() throws Throwable {
assertThat(wizard, is(notNullValue()));
bot.button(IDialogConstants.CANCEL_LABEL).click();
assertThat(Conditions.shellCloses(target).test(), is(true));
}
@Test
public void not_close_wizard_when_click_finish_and_file_is_empty() throws Throwable {
assertThat(bot.button(IDialogConstants.FINISH_LABEL).isEnabled(), is(false));
}
@Test
public void finish_is_enabled_when_file_form_is_exists_path() throws Exception {
SWTBotText fileText = bot.textWithLabel(Messages.CSVExportWizard_output_label);
assertThat(fileText.getText(), is(""));
File tmpFile = File.createTempFile("tmp", ".txt");
String path = tmpFile.getAbsolutePath();
fileText.setText(path);
assertThat(bot.button(IDialogConstants.FINISH_LABEL).isEnabled(), is(true));
}
@Test
public void runner_is_called_when_finish_is_pushed() throws Exception {
SWTBotText fileText = bot.textWithLabel(Messages.CSVExportWizard_output_label);
assertThat(fileText.getText(), is(""));
File tmpFile = File.createTempFile("tmp", ".txt");
String path = tmpFile.getAbsolutePath();
fileText.setText(path);
bot.button(IDialogConstants.FINISH_LABEL).click();
verify(runner, times(1)).run((IProgressMonitor) any());
}
public static void main(String[] args) {
final Shell parentShell = new Shell();
IWizard newWizard = new CSVExportWizard();
WizardDialog dialog = new WizardDialog(parentShell, newWizard);
dialog.open();
}
}

How do you think about it?