4.9. Measurement: Test coverage with Emma

Emma, like JBlanket, is an open source tool for computing coverage information about Java systems. Which tool is more suited to your environment depends upon your needs. JBlanket supports the concept of Extreme Coverage, which Emma does not. Emma supports line and block-level coverage, which JBlanket does not. Finally, Emma appears to be more actively maintained and have a larger user community than JBlanket. In the Hackystat project, we used JBlanket successfully for several years, but have recently switched to Emma on a trial basis.

4.9.1. Emma tool installation

To install Emma for use in StackyHack, download the .zip file containing the latest release from the Emma home page into a directory such as "java-lib", and unzip it. Then, define an environment variable called EMMA_HOME to point to the distribution directory. For example, "c:\java-lib\emma-2.0.5312". (Note that the file path should have no embedded spaces in it.)

4.9.2. The emma.build.xml file

Example 4.16, “emma.build.xml” shows the emma.build.xml file, which defines the Ant targets used to invoke the Emma tool and the Emma sensor.

Example 4.16. emma.build.xml

<project name="stackyhack.emma" default="emma">
  <description>
  Provides the Emma tool and the Hackystat Emma sensor.
  </description>

  <import file="build.xml"/>
  <property environment="env"/>
  <property name="emma.dir" location="${build.dir}/emma" />
  <property name="junit.dir" location="${build.dir}/junit" />

  <target name="emma" depends="emma.tool, emma.sensor" description="Runs Emma, followed by the Emma sensor."/>

  <target name="emma.tool" depends="clean, compile" 
    description="Cleans, compiles, instruments byte codes, runs unit tests, generates Emma report.">
    <!-- Verify that Emma is installed. -->
    <available file="${env.EMMA_HOME}/lib/emma.jar" property="emma.available"/>
    <fail unless="emma.available"  message="Error: EMMA_HOME not set or emma.jar not found, indicating Emma is not installed."/>
    
    <!-- Define the path and taskdef containing the emma binaries. -->
    <property name="emma.lib.dir" value="${env.EMMA_HOME}/lib" />
    <path id="emma.lib.dir" >
      <pathelement location="${emma.lib.dir}/emma.jar" />
      <pathelement location="${emma.lib.dir}/emma_ant.jar" />
    </path>
    <taskdef resource="emma_ant.properties" classpathref="emma.lib.dir" />

    <!-- Instrument the .class files. -->     
    <mkdir dir="${emma.dir}" />
    <emma enabled="true" >
      <instr instrpath="${build.dir}/classes"
             destdir="${build.dir}/classes"
             metadatafile="${emma.dir}/metadata.emma"
             merge="true" mode="overwrite">
      </instr>
    </emma>
    
    <!-- Run JUnit with the instrumented class files. --> 
    <mkdir dir="${junit.dir}" />
    <junit printsummary="withOutAndErr" fork="yes">
      <classpath path="${build.dir}/classes;${java.class.path}">
        <path refid="emma.lib.dir" />
        <path refid="compile.classpath"/> 
      </classpath>
      <sysproperty key="emma.coverage.out.file" value="${build.dir}/emma/coverage.emma" />
      <sysproperty key="emma.coverage.out.merge" value="true" />
      <formatter type="xml" />
      <batchtest todir="${junit.dir}">
        <fileset dir="${src.dir}">
          <include name="**/Test*.java" />
        </fileset>
      </batchtest>
    </junit>
    
    <!-- Generate Emma reports. -->
    <emma enabled="true" >
      <report sourcepath="${src.dir}"
              sort="+name"
              metrics="method:70,block:80,line:80,class:100">
        <fileset dir="${emma.dir}" >
          <include name="*.emma" />
        </fileset>
        <xml outfile="${emma.dir}/coverage.xml" depth="method" />
        <html outfile="${emma.dir}/coverage.html"
              depth="method" columns="name,class,method,block,line" />
      </report>
    </emma>
    
    <!-- Now delete the instrumented .class files. -->
    <delete dir="${build.dir}/classes" />
  </target>

  <target name="emma.sensor" description="Sends Emma coverage data to Hackystat using the Emma sensor.">
    <!-- Define the emma sensor taskdef, failing the build if the sensor is not installed. -->
    <available classname="org.hackystat.sensor.emma.EmmaSensor" property="emma.sensor.available"/>
    <fail unless="emma.sensor.available" message="Error: Emma sensor not installed."/>
    <taskdef name="hacky-emma" classname="org.hackystat.sensor.emma.EmmaSensor"/>
    
    <!-- Send Coverage data to Hackystat using the Emma sensor. --> 
    <hacky-emma verbose="true" emmaReportXmlFile="${emma.dir}/coverage.xml">
      <fileset dir="${src.dir}" includes="**/*.java" />
    </hacky-emma>
  </target>
  
</project>

The emma.build.xml defines the <emma.tool> target to obtain coverage information. The <emma.sensor> target uses the Emma sensor to read in the XML files generated by Emma and send the output to Hackystat. The <emma> target simply invokes both of these targets in sequence.

4.9.3. Running Emma to produce test coverage data

As the Ant build file illustrates, the <emma.tool> target produces coverage data using a three-step process. First, it performs a pass over all of the compiled .class files in StackyHack, inserting instrumentation bytecodes into these files to record information as the system is executed. Second, it invokes the JUnit tests on the instrumented bytecode, resulting in a set of files that provide information about what methods were invoked (and not invoked) during the test runs. Finally, it processes these data files to produce a summary report.

Example 4.17, “emma.tool invocation” shows the output from invoking the <emma.tool> target.

Example 4.17. emma.tool invocation

C:\svn-csdl\StackyHack>ant -f emma.build.xml emma.tool
Buildfile: emma.build.xml

clean:
   [delete] Deleting directory C:\svn-csdl\StackyHack\build

compile:
    [mkdir] Created dir: C:\svn-csdl\StackyHack\build\classes
    [javac] Compiling 5 source files to C:\svn-csdl\StackyHack\build\classes

emma.tool:
    [mkdir] Created dir: C:\svn-csdl\StackyHack\build\emma
    [instr] processing instrumentation path ...
    [instr] instrumentation path processed in 140 ms
    [instr] [5 class(es) instrumented, 0 resource(s) copied]
    [instr] metadata merged into [C:\svn-csdl\StackyHack\build\emma\metadata.emma] {in 16 ms}
    [mkdir] Created dir: C:\svn-csdl\StackyHack\build\junit
    [junit] Running edu.hawaii.stack.TestClearStack
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.25 sec
    [junit] Running edu.hawaii.stack.TestStack
    [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.25 sec
   [report] processing input files ...
   [report] 2 file(s) read and merged in 0 ms
   [report] writing [xml] report to [C:\svn-csdl\StackyHack\build\emma\coverage.xml] ...
   [report] writing [html] report to [C:\svn-csdl\StackyHack\build\emma\coverage.html] ...
   [delete] Deleting directory C:\svn-csdl\StackyHack\build\classes

BUILD SUCCESSFUL
Total time: 3 seconds

4.9.4. Running the Emma sensor to send coverage data to Hackystat

After Emma has been invoked to generate data regarding the results of testing, the Emma sensor can be invoked to read in the resulting XML files produced by Emma and send the resulting data to Hackystat as Coverage sensor data.

To accomplish this, you must first install the Emma sensor, which can process the XML files output by Emma. General instructions on installing sensors are provided in Chapter 2, Client-side configuration: Tool sensor installation; instructions for specific sensors are provided in Chapter 26, Sensors. After installation, your hackyInstaller Emma configuration window should look something like the screen image in Figure 4.10, “ HackyInstaller configuration screen for Emma sensor ”.

Figure 4.10.  HackyInstaller configuration screen for Emma sensor


HackyInstaller configuration screen for Emma sensor

Example 4.18, “emma.sensor invocation” illustrates the invocation of the Emma sensor.

Example 4.18. emma.sensor invocation

C:\svn-csdl\StackyHack>ant -f emma.build.xml emma.sensor
Buildfile: emma.build.xml

emma.sensor:
[hacky-emma] Sensor enabled?: true
[hacky-emma] Processing file: C:\svn-csdl\StackyHack\build\emma\coverage.xml
[hacky-emma] Hackystat data on 20 coverage entries sent to http://hackystat.ics.hawaii.edu/ (0 secs.)

BUILD SUCCESSFUL
Total time: 1 second

Once you've invoked the sensor, you can verify that this data was received at the server by using the List Sensor Data command. Figure 4.11, “ List Sensor Data with StackyHack Emma data ” illustrates what this page might look like after receiving the StackyHack data.

Figure 4.11.  List Sensor Data with StackyHack Emma data


List Sensor Data with StackyHack Emma data