Tag Archive | maven

Testing HTML5 canvas applications with sikuli and arquillian

HTML5 introduces a great new element that can be used to draw arbitrary content on a pane: the canvas element. What has been a standard feature for fat client applications for decades is now introduced to the world of web applications. Web developers no longer need to use proprietary plugins to draw images or charts in their applications.

But when it comes for testing, this new feature imposes new challenges to the web development community. How to test that the canvas element is in an appropriate state at some point in time? Standard technologies like selenium focus on the markup that is generated by the web server and not on the pixels drawn on the canvas.

More promising in this field are technologies that use image processing to verify that an application renders its data correctly. One of these frameworks is sikuli. Sikuli is an open-source research project that was started at the MIT and is now maintained by Raimund Hocke.

To give a more practical introduction, let’s assume we have a simple web application that uses the HTML5 canvas element to implement some simple image processing functionality like a grayscale, a brighten and a threshold filter as well as an undo button (the code for this application can be found as usual on github):
html5-canvas-screenshot

The installation of sikuli is (of course) platform dependent. The installer, which can be downloaded from the sikuli download page, is a Java Swing application that asks you about your typical usage pattern. As we do not want to use the python IDE, we choose option 4 from the list of options. The actual jar file is then downloaded and prepared for our OS. After the installation process has finished, we find an OS dependent jar file within the installation directory. As our example project uses maven as build system, we have to introduce a system scope dependency after we have copied the library into the lib folder:

<dependency>
    <groupId>org.sikuli</groupId>
    <artifactId>sikuli</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${basedir}/lib/sikuli-java.jar</systemPath>
</dependency>

When sikuli is used for the first time, it extracts some native libraries into a new folder, here in our example into ${basedir}/lib/libs. This folder has to be added to the user’s path environment variable.

Now that we have installed sikuli, let’s setup arquillian so that we can write our first unit test. How to setup arquillian is described for example here. As I don’t want to repeat everything, in the following you will find only the unit test class:

@RunWith(Arquillian.class)
public class FilterTest {
    public static final String WEBAPP_SRC = "src/main/webapp";
    @ArquillianResource
    URL deploymentURL;
    private Screen screen;

    @Before
    public void before() throws URISyntaxException, IOException {
        screen = new Screen();
        if (Desktop.isDesktopSupported()) {
            Desktop.getDesktop().browse(deploymentURL.toURI());
        } else {
            fail();
        }
    }

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class, "html5-sikuli-webapp.war")
                .addClasses(HomeBackingBean.class)
                .addAsWebResource(new File(WEBAPP_SRC, "home.xhtml"))
                .addAsWebResource(new File(WEBAPP_SRC, "resources/css/style.css"), "resources/css/style.css")
                .addAsWebResource(new File(WEBAPP_SRC, "resources/images/rom.jpg"), "resources/images/rom.jpg")
                .addAsWebResource(new File(WEBAPP_SRC, "resources/js/html5Sikuli.js"), "resources/js/html5Sikuli.js")
                .addAsWebResource(new File(WEBAPP_SRC, "resources/js/jquery-2.0.3.js"), "resources/js/jquery-2.0.3.js")
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
                .setWebXML(new File(WEBAPP_SRC, "WEB-INF/web.xml"));
    }

The createDeployment() method sets up the war archive, which is deployed by arquillian to JBoss AS 7.1.1.Final (see arquillian.xml file). In our @Before method we use the SDK class Desktop to open the default browser and point it to the deployment URL. Here we also create an instance of sikuli’s class Screen. This class provides all methods needed to perform the interaction with our application. Let’s look at this in more detail:

@Test
@RunAsClient
public void testGrayScale() throws FindFailed {
    screen.wait(getFullPath("originalImage.png"));
    screen.find(getFullPath("btnUndo_disabled.png"));
    screen.click(getFullPath("btnGrayscale.png"));
    screen.find(getPattern("grayscaleImage.png", 0.9f));
    screen.click(getFullPath("btnUndo_enabled.png"));
    screen.click(getPattern("originalImage.png", 0.9f));
}

private Pattern getPattern(String path, float similarity) {
    Pattern p = new Pattern(getFullPath(path));
    return p.similar(similarity);
}

private String getFullPath(String path) {
    return "src/test/resources/img/" + path;
}

As sikuli is based on image processing, we can define where to click and what to verify with screenshots we have taken before. In this simple example I have stored all screenshots as png files within the src/test/resources/img folder of our project. More advanced projects may need a more sophisticated folder hierarchy. As you can see, we first wait for the application to show up. Once sikuli has found the first screenshot, we verify that the button “Undo” is disabled. This is done by calling the method find() with an image of the disabled button. Now we can click on the button “Grayscale” (again specified by an image of the button) and then verify that the grayscale version of the image is found on the screen.

Sikuli does not only compare both images pixel by pixel, but if you like it computes the similarity of the found screen region with the requested region. This helps when you need to be more tolerant (e.g. if you want to test the application in different browsers and these render the buttons slightly different). The default value for the similarity attribute is 0.7f, but if you increase it to 1.0f you have a simple pixel by pixel comparison.

But this is not all. With sikuli you can do nearly all things you could do as human interactor:

  • Type characters using screen.type()
  • Double click with screen.doubleClick()
  • Perform drag and drop operations with screen.dragDrop()
  • Use the mouse wheel

Conclusion: Sikuli is a powerful and easy to use tool to perform integration tests for web applications that heavily rely on HTML5’s canvas object. The same is of course true for standard fat client applications (Swing, JavaFX). Together with arquillian you can setup comprehensive test suites that cover a lot of “real” use cases.

Setting up your application server with maven

In many cases there is no way to deploy an application without the need to setup your application before. In JBoss AS 7.x you may want to configure for example your database connection. Or you have to configure a security realm. Maybe you also want to adjust the SLSB pool… In any of these cases all developers in the team have to share a common or at least a similar configuration.
Often this information can be found in sporadically sent emails or on some wiki page. But what happens sometime after a release, when you have to check out a branch to fix some bug or to add a new feature? You will have to reconstruct the configuration that was valid for this branch. So why not add the configuration files to your version control system and together with the mere configuration files, a maven configuration that sets up the whole application server?
Let’s try to keep it simple and use only public available and commonly used plugins. First of all let’s add all versions that we will need in the following to the properties part of the pom.xml:

	<properties>
		<jboss.install.dir>${project.build.directory}/jboss</jboss.install.dir>
		<jboss.version>7.2.0.Final</jboss.version>
		<app.version>${project.version}</app.version>
		<ojdbc.version>11.2.0.1.0</ojdbc.version>
	</properties>

We also define here the installation directory of the JBoss AS. This way we can change it, if we want, using the command line option -D. Now we add a new profile, such that we have to explicitly switch the setup procedure on and that it is not part of the normal build:

<profile>
	<id>setupAs</id>
	<build>
		<plugins>
		...
				</plugins
		</build>
</profile>

If we have the current JBoss version as a maven artifact deployed in our maven repository, we can use the maven-dependency-plugin to download and unpack the JBoss to the installation directory given above:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-dependency-plugin</artifactId>
	<version>2.8</version>
	<executions>
		<execution>
			<id>unpack-jboss</id>
			<phase>package</phase>
			<goals>
				<goal>unpack</goal>
			</goals>
			<configuration>
				<artifactItems>
					<artifactItem>
						<groupId>org.jboss</groupId>
						<artifactId>jboss-as</artifactId>
						<version>${jboss.version}</version>
						<type>zip</type>
						<outputDirectory>${project.build.directory}/jboss</outputDirectory>
					</artifactItem>
				</artifactItems>
			</configuration>
		</execution>

Now that the application server is unpacked, we have to add the JDBC driver as well as our application (or anything else you need). We set this up by adding another execution block to the maven dependency plugin:

<execution>
	<id>copy</id>
	<phase>package</phase>
	<goals>
		<goal>copy</goal>
	</goals>
	<configuration>
		<artifactItems>
			<artifactItem>
				<groupId>our-company</groupId>
				<artifactId>our-application-ear</artifactId>
				<version>${app.version}</version>
				<type>ear</type>
				<outputDirectory>${jboss.install.dir}/jboss-as-${jboss.version}/standalone/deployments</outputDirectory>
			</artifactItem>
			<artifactItem>
				<groupId>com.oracle</groupId>
				<artifactId>ojdbc6</artifactId>
				<version>${ojdbc.version}</version>
				<outputDirectory>${jboss.install.dir}/jboss-as-${jboss.version}/standalone/deployments</outputDirectory>
				<destFileName>ojdbc6.jar</destFileName>
			</artifactItem>
		</artifactItems>
	</configuration>
</execution>

Last but not least, we also want to adjust the standard configuration files to our needs. We can use the maven-resources-plugin to substitute variable values within each file. Therefore we add templates for these files to the resources folder of our JBoss module and call the goal copy-resources:

<plugin>
	<artifactId>maven-resources-plugin</artifactId>
	<version>2.6</version>
	<executions>
		<execution>
			<id>copy-jboss-configuration</id>
			<phase>package</phase>
			<goals>
				<goal>copy-resources</goal>
			</goals>
			<configuration>
				<outputDirectory>${jboss.install.dir}/jboss-as-${jboss.version}/standalone/configuration</outputDirectory>
				<resources>
					<resource>
						<directory>src/main/resources/jboss/standalone/configuration</directory>
						<filtering>true</filtering>
					</resource>
				</resources>
			</configuration>
		</execution>
		<execution>
			<id>copy-jboss-bin</id>
			<phase>package</phase>
			<goals>
				<goal>copy-resources</goal>
			</goals>
			<configuration>
				<outputDirectory>${jboss.install.dir}/jboss-as-${jboss.version}/bin</outputDirectory>
				<resources>
					<resource>
						<directory>src/main/resources/jboss/bin</directory>
						<filtering>true</filtering>
					</resource>
				</resources>
			</configuration>
		</execution>
	</executions>
</plugin>

The values for the filtering can be given on the command line with the -D option. If the team has more than a few members, it is also possible to create for each user a properties file that contains his/her specific configuration values. If we use the OS user as filename, we can easily choose the file by the name of the currently logged in user. This way each team member can easily setup its own completely configured application server instance by simply running:

mvn clean install -PsetupAs

In order to prevent that the newly configured server is deleted with the next clean invocation, we disable the maven clean plugin for the normal build:

<plugin>
	<artifactId>maven-clean-plugin</artifactId>
	<version>2.5</version>
	<configuration>
		<skip>false</skip>
	</configuration>
</plugin>

Within the setupAs profile created above, we have to enable it of course, such that we can delete the whole installation just by calling “mvn clean -PsetupAs”. Now switching to an older branch is easy as we don’t lose any time searching for the right configuration…

Developing your own maven plugin to verify the bytecode of your artifact

In this article the goal is to develop your own maven plugin that accesses at build time the artifact of your project and verifies the class files. I used this concept for my library jb5n to verify that for each MessageResource interface an appropriate key/value pair is available in the underlying resource bundle.
The first step is of course to create a new maven module or project using the mojo archetype:

mvn archetype:generate \
  -DgroupId=sample.plugin \
  -DartifactId=hello-maven-plugin \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-plugin

This will create a ready-to-run maven project of type maven-plugin for you:

  <groupId>sample.plugin</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>maven-plugin</packaging>

All necessary dependencies to develop your own maven plugin are already available. The archetype also creates a simple Mojo class:

/**
 * @goal verify
 * @phase verify
 */
public class MyMojo extends AbstractMojo {

    /**
     * @parameter default-value="${project}"
     */
    private MavenProject mavenProject;

The maven goal as well as the default phase of the lifecycle the plugin runs in is given by the javadoc elements @goal and @phase. Newer versions of maven let you define these values with annotations. To access the outcome of the current build process we let our plugin run in the verify phase. We can access the file of our artifact by accessing the member variable mavenProject, which is injected by maven into our plugin with the above definition:

File artifactFile = mavenProject.getArtifact().getFile();

The File object from the above snippet will point to the jar file (in case we have chosen jar packaging for our artifact). Therefore we can open the jar file using Java’s SDK classes JarFile and JarEntry:

JarFile jarFile = new JarFile(artifactFile);
Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) {
	JarEntry jarEntry = entries.nextElement();
	String jarEntryName = jarEntry.getName();
	if(jarEntryName != null && jarEntryName.endsWith(".class")) {
		getLog().debug(String.format("Processing jar file entry '%s'.", jarEntryName));
		...
	}
}

Now we can use a library like javassist or Java’s reflection API to analyze each class file within the artifact.