EMMA ANT使用

来源:互联网 发布:李连杰纵欲知乎 编辑:程序博客网 时间:2024/05/16 01:52

http://emma.sourceforge.net/userguide/ar01s03.html


3. Getting Started (ANT)

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.

Sample source code and ANT build files

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.

3.1. Adding EMMA tasks to your ANT build

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" >1    <pathelement location="${emma.dir}/emma.jar" />    <pathelement location="${emma.dir}/emma_ant.jar" />  </path>  <taskdef resource="emma_ant.properties" classpathref="emma.lib" />

1

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.



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 runBuildfile: build.xmlinit:    [mkdir] Created dir: .../examples/outcompile:    [javac] Compiling 4 source files to .../examples/outrun:     [java] main(): running doSearch()...     [java] main(): doneBUILD SUCCESSFULTotal 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 runBuildfile: build.xmlemma:init:    [mkdir] Created dir: .../examples/outcompile:    [javac] Compiling 4 source files to .../examples/outrun: [emmajava] main(): running doSearch()... [emmajava] main(): done [emmajava] EMMA: writing [txt] report to [.../coverage.txt] ...BUILD SUCCESSFULTotal 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 classesOVERALL STATS SUMMARY:total packages: 2total classes:  3total methods:  7total executable files: 3total executable lines: 29COVERAGE BREAKDOWN BY PACKAGE:[class, %]      [method, %]     [block, %]      [line, %]       [name]100% (2/2)      100% (4/4)      91%  (64/70)    100% (18/18)    search100% (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.


3.3. Offline mode: separating instrumentation and execution

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