http://emma.sourceforge.net/userguide/ar01s03.html
This section introduces EMMA ANT tasks. It starts with an example of how to run an application from ANT so that coverage instrumentation is performed on-the-fly, as the classes are loaded by the JVM, and then repeats the same process by breaking it into distinct instrumentation/execution/reporting steps. The ANT logic sequence will be equivalent to what was described previously, in Section 2, “Getting Started (command line)”. Additionally, the ANTbuild.xml
files created below will have logic for enabling and disabling coverage through ANT command line.
The source code located in examples/src
directory of EMMA distribution is used for all examples in this tutorial. EMMA distribution also includes two sample ANT buildfiles, build-onthefly.xml
and build-offline.xml
, that correspond to the two approaches to collecting coverage. Although it would have been possible to keep everything in a single file, using distinct files allows for more clarity.
EMMA ANT tasks can be deployed in any of the many ways available for custom ANT taskdefs. This tutorial assumes that you have an EMMA distribution directory that contains both emma.jar
and emma_ant.jar
. The latter archive is what contains EMMA taskdefs, however, both archives need to be visible to ANT. The easiest way to accomplish this is with the following definitions in your build.xml
:
<!-- directory that contains emma.jar and emma_ant.jar: --> <property name="emma.dir" value="${basedir}/../lib" /> <path id="emma.lib" > <pathelement location="${emma.dir}/emma.jar" /> <pathelement location="${emma.dir}/emma_ant.jar" /> </path> <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
Even though this path definition can be merged into the <taskdef>, a path element with this id will come in handy later in the build. |
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 -------------------------------------------------------------------------------
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>:
List libraries in the JVM's classpath, not <emmajava>'s classpath. You do that by adding them to <emmajava>'s <emmajava enabled="${emma.enabled}" libclasspathref="allmylibs.path" classname="Main" classpathref="run.classpath" > </emmajava> |
|
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" > <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:
add EMMA task definitions
replace the necessary invocations of <java> with <emmajava>
configure coverage paths and inclusion/exclusion filters
configure coverage reports
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.
As convenient as the on-the-fly mode is, in many cases it is not sufficient. For example, running a commercial J2EE container in a custom instrumenting classloader is practically impossible. Certain (bad) coding practices also fail for code executing in a custom classloader. Finally, in large scale development there is a common need to collect and merge coverage data from multiple execution runs and multiple JVM processes.
This is where separate instrument/execute/report phases are a necessity. This section repeats the previous exercise using <emma> ANT task, which provides several subtasks for managing offline instrumentation: <instr>, <merge>, and <report>. In a typical ANT build each <emma> tag acts as a container for an arbitrary sequence of sub-tags. This design allows for a simple form of build flow control, whereby entire sequences of EMMA commands can be disabled at a single point.
Let's go back to the starting point of the previous section and assume that you have a build.xml
file with EMMA tasks imported and the following build infrastructure created:
<!-- 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" /> <!-- output directory used for EMMA work files and coverage reports: --> <property name="coverage.dir" value="${basedir}/coverage" /> <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" > </target>
In a real world project the actual application could be either an end user application or your test framework driver. Adding offline coverage instrumentation and reporting to this build is not much harder than it was in the command line tools case, in