January 22, 2013

HOWTO add Emma Support to Maven GWT


I've had to do this a couple times so I'm finally writing it down for future me. But for anyone else that might be interested, this posting will (hopefully) walk you through how to add Emma code coverage support to GWT via maven.

For those of you that might not be that familiar with code coverage tools, there are two common ways to generate code coverage. In both cases, your code is modified to include counters for each time a thread enters / exists a code block. This process is call instrumentation hence, in java there are two ways: re-write the code and compile the classes or re-write the classes directly.

So lets talk a little about why emma works. First of all, Google has a patched jar but the reason that emma works well is that emma instruments the classes not the source.  This is key to GWT because if you instrument the code, then GWT compile will not be able to find any of the instrumentation classes and it won't compile. So by instrumenting the classes the GWT compiler can compile your source but the classes files still get instrumented.

So you may be thinking, if GWT compiler compiles your code pre-instrumentation how can it generate the coverage information for you client code? The answer is simple, the unit test MUST run in development mode.  By doing this, the GWT compiler will run your application in java not javascript.




So here are the changes (ordered by maven lifecycle).

Add Emma dependency

The first thing you'll need to do is add the emma dependency. I was unable to find Googles patched jar in any publish maven repo so I just uploaded it to a local nexus server. If you're not using nexus you'll need to install it manually into the your local repo:


mvn install:install-file -Dfile=<path-to-file> -DgroupId=emmca -DartifactId=emma -Dversion=2.0.5312-patched -Dpackaging=jar

Then add the following dependency to your pom.xml

    <dependency>
      <groupId>emma</groupId>
      <artifactId>emma</artifactId>
      <version>2.0.5312-patched</version>
      <scope>test</scope>
    </dependency>

Instrument your classes.

I found that running emma via ant-run was the easier way to get it to do what I wanted.  Check out the emma document for other options but I followed there offline instructions


<plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.7</version>
        <executions>
          <execution>
            <id>Emma instrumentation</id>
            <phase>process-classes</phase>
            <configuration>
              <skip>${skipEmma}</skip>
              <target>
                
                <property name="test_classpath" refid="maven.test.classpath"/>
                <echo message="Emma - instrumentation" />
                <echo message="test classpath: ${test_classpath}"/>
                <java 
                   fork="true"
                   classpathref="maven.test.classpath"
                   classname="emma">
                  <arg value="instr" />
                  <arg value="-cp"/>
                  <arg value="target/classes"/>
                  <arg value="-m"/>
                  <arg value="overwrite"/>
                  <arg value="-out" />
                  <arg value="target/emma/emma.em"/>
                </java>
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>

.....

Update Surefire plugin

Next we need to update your surefire plugin to force the output file to get put into target. This isn't required but it cleaner with maven.


    <systemPropertyVariables>
            <emma.coverage.out.file>${project.basedir}/target/emma/emma.ec</emma.coverage.out.file>
    </systemPropertyVariables>

Here's my full surefire plugin in case there are any issues. 

   <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <useManifestOnlyJar>false</useManifestOnlyJar>
          <forkMode>always</forkMode>
          <argLine>-Xmx1024m</argLine>
          <redirectTestOutputToFile>true</redirectTestOutputToFile>
          <additionalClasspathElements>
              <!--  needed so gwt can read the source via classpath -->
              <additionalClasspathElement>src/main/java</additionalClasspathElement>
              <additionalClasspathElement>src/test/java</additionalClasspathElement>
          </additionalClasspathElements>
          <systemPropertyVariables>
            <emma.coverage.out.file>${project.basedir}/target/emma/emma.ec</emma.coverage.out.file>
          </systemPropertyVariables>
        </configuration>
  </plugin>

Generate the Report

Finally, you need to add this second execution to the ant-run plugin we defined above. This will then generate the report via the emma.em file and emma.ec file that were generated from the two previous steps.

It should also be noted that I call <fail> at the end of the ant run. This is just a safe guard so I don't generate a jar that contains the instrumented cases.

<execution>
            <id>Emma Report</id>
            <phase>prepare-package</phase>
            <configuration>
              <skip>${skipEmma}</skip>
              <target>

                <property name="test_classpath" refid="maven.test.classpath"/>
                <echo message="Emma - instrumentation" />
                <echo message="test classpath: ${test_classpath}"/>     
                <mkdir dir="${project.basedir}/target/emma-report"/>
                <java
                   dir="${project.basedir}/target/emma-report"
                   fork="true"
                   classpathref="maven.test.classpath"
                   classname="emma">
                  <arg value="report" />
                  <arg value="-sp" />
                  <arg value="${project.basedir}/src/main/java" />
                  <arg value="-r"/>
                  <arg value="html"/>
                  <arg value="-in" />
                  <arg value="${project.basedir}/target/emma/emma.em,${project.basedir}/target/emma/emma.ec" />
                </java>
                <fail message="Emma Complete - exiting build"/>
              
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>


9 comments:

Anonymous said...

Thank you,
This was very helpful for me.

Unknown said...
This comment has been removed by the author.
Anonymous said...

Thanks a lot for this post.
question: when I use 2.0.5312-patched it is complaining about missing artifact that means it is not present in maven central repo. Please help me out to resolve this issue

Anonymous said...

Sorry for the confusion. If you don't have a mvn repo (nexus) then you'll need to run the mvn install-file command to install it manually.

Anonymous said...

Thanks for your info.
When I used your pom information, I'm getting this error "plugin execution not covered by lifecycle configuration" with instrumentation and process-classes phase in eclipse. Could you please help me solve this issue?
Thanks in advance.

Unknown said...

You can simply ignore "plugin execution not covered" error in eclipse, or if you click on it, you can choose click fix: mark as ignored. It's not important to run that plugin from eclipse.

Unknown said...

Ahh, and I wanted to thank the author for the blog post also :)

Kevin said...

I just added a posting that will help you with the "plugin execution not covered" problem.

http://my-tech-notes.blogspot.com/2014/09/how-to-execute-maven-plugin-within.html

Anonymous said...

Hi Kevin,
Thanks for your post.It was really helpful.
I followed the steps given by you.but I am getting below error.

[mkdir] Created dir: C:\Workspace\CoverageSample\target\emma-report

[java] EMMA: processing input files ...
java.io.IOException: input file does not exist: [C:\Workspace\CoverageSample\target\emma\emma.em].