3.2. <emmajava>: instrumenting Java classes on-the-fly

Let's create a simple ANT build file for the source code in examples/src. Add this after EMMA task definitions:

  <!-- root directory for the example source code: --> 
  <property name="src.dir" value="${basedir}/src" />

  <!-- javac class output directory: -->
  <property name="out.dir" value="${basedir}/out" />

  <target name="init" >
    <mkdir dir="${out.dir}" />
    <path id="run.classpath" >
      <pathelement location="${out.dir}" />
    </path>
  </target>

  <target name="compile" depends="init" description="compiles the example source code" >
    <javac debug="on" srcdir="${src.dir}" destdir="${out.dir}" />
  </target>

  <target name="run" depends="init, compile" description="runs the examples" >
    <java classname="Main"
          classpathref="run.classpath"
    >
    </java>
  </target>

You can now compile and run the example:

>ant run
Buildfile: build.xml

init:
    [mkdir] Created dir: .../examples/out

compile:
    [javac] Compiling 4 source files to .../examples/out

run:
     [java] main(): running doSearch()...
     [java] main(): done

BUILD SUCCESSFUL
Total time: 5 seconds

<emmajava> is an EMMA extension of ANT stock <java> task that is an ANT adapter to the same instrumenting application runner as used by EMMA emmarun command line tool. Upgrading your build to do code coverage on-the-fly is very easy: just replace <java> tags with <emmajava> tags for tasks that run your application or test cases. Don't worry, your build is not now in a permanent coverage-enabled mode: <emmajava> becomes a pass-through to the normal <java> when its enabled attribute is set to false:

  <target name="emma" description="turns on EMMA's on-the-fly instrumentation mode" >
    <property name="emma.enabled" value="true" />
  </target>

  <target name="run" depends="init, compile" description="runs the examples" >
    <emmajava enabled="${emma.enabled}" libclasspathref="emma.lib" 
              classname="Main"
              classpathref="run.classpath"
    >
    </emmajava>
  </target>

Now, whenever you insert emma before any run targets on ANT's command line, you enable on-the-fly coverage instrumentation and reporting (but ANT commands without emma continue to function as before):

>ant emma run
Buildfile: build.xml

emma:

init:
    [mkdir] Created dir: .../examples/out

compile:
    [javac] Compiling 4 source files to .../examples/out

run:
 [emmajava] main(): running doSearch()...
 [emmajava] main(): done
 [emmajava] EMMA: writing [txt] report to [.../coverage.txt] ...

BUILD SUCCESSFUL
Total time: 7 seconds

The default text coverage report is generated in the current directory:

[EMMA v2.0.3611 report, generated Sun Jan 11 14:18:08 CST 2004]
-------------------------------------------------------------------------------
OVERALL COVERAGE SUMMARY:

[class, %]      [method, %]     [block, %]      [line, %]       [name]
100% (3/3)      100% (7/7)      95%  (116/122)  100% (29/29)    all classes

OVERALL STATS SUMMARY:

total packages: 2
total classes:  3
total methods:  7
total executable files: 3
total executable lines: 29

COVERAGE BREAKDOWN BY PACKAGE:

[class, %]      [method, %]     [block, %]      [line, %]       [name]
100% (2/2)      100% (4/4)      91%  (64/70)    100% (18/18)    search
100% (1/1)      100% (3/3)      100% (52/52)    100% (11/11)    default package
-------------------------------------------------------------------------------

Other report types

By default, <emmajava> generates a plain-text report only. The default report's depth is all which means to show the overall coverage summary followed by breakdown by package. You can increase the default depth to include package and source file summaries. This and many other aspects of EMMA report generation can be configured using various attributes and nested elements that <emmajava> adds to <java>. These extensions only take effect when the task is in enabled state and have no impact on the build otherwise. See Chapter3, EMMA Property Reference in the reference manual for full details on EMMA configuration.

<emmajava> application runner uses an instrumenting classloader to add bytecode instrumentation to Java classes as they are being loaded by the JVM. For efficiency reasons, <emmajava> does not scan your entire classpath before it starts running. This has the side effect of only reporting on the classes that got loaded by the application. If your intent is to base coverage metrics on the full set of classes in the classpath, you can set fullmetadata task attribute to true. Here is an example that also adds some extra reports and makes sure the HTML report generator has access to the source code by setting sourcepath="${src.dir}":

  <!-- output directory used for EMMA coverage reports: -->
  <property name="coverage.dir" value="${basedir}/coverage" />

  <target name="run" depends="init, compile" description="runs the examples" >
    <emmajava enabled="${emma.enabled}" libclasspathref="emma.lib" 
              fullmetadata="yes" sourcepath="${src.dir}"
              classname="Main"
              classpathref="run.classpath"
    >
      <txt outfile="${coverage.dir}/coverage.txt" />
      <xml outfile="${coverage.dir}/coverage.xml" />
      <html outfile="${coverage.dir}/coverage.html"  />
    </emmajava>
  </target>

Although this was not the case with this tutorial's sample code, chances are your application has third-party library dependencies and you are not interested in their coverage metrics. There are two ways to handle this with <emmajava>:

1

List libraries in the JVM's classpath, not <emmajava>'s classpath. You do that by adding them to <emmajava>'s emmaclasspath attribute instead of the usual classpath:

    <emmajava enabled="${emma.enabled}" libclasspathref="allmylibs.path"1 
              classname="Main"
              classpathref="run.classpath"
    >
    </emmajava>

1

Use a coverage filter to make sure that only the classes of interest are instrumented:

    <emmajava enabled="${emma.enabled}" libclasspathref="emma.lib" 
              classname="Main"
              classpathref="run.classpath"
    >
      1<filter includes="Main, search.*" />
    </emmajava>

If these techniques are not sufficient (e.g., you need to exclude testcases from coverage and they are in the same Java packages as the application code and do not follow a sensible naming pattern), you can always switch to offline instrumentation as described next. Offline instrumentation does not keep everything in memory and ultimately gives you much more control over what gets instrumented.

To summarize, an existing build.xml can be converted to use EMMA's on-the-fly instrumentation mode by following these steps:

  1. add EMMA task definitions

  2. replace the necessary invocations of <java> with <emmajava>

  3. configure coverage paths and inclusion/exclusion filters

  4. configure coverage reports

  5. make sure there is a way to turn coverage instrumentation off

Futher reading. This has been a quick intro to EMMA's on-the-fly ANT instrumentation mode. For further details see Section2, <emmajava>/emmarun in the reference manual.