NetBeans IDE XDoclet Hibernate
Project Extensions

the NBXDoclet site
Home
Project Page
 

Code Coverage of Unittests

Code coverage measures how many classes, methods, blocks and lines of code was used during executing unitttests. Thanks to Ant's extension support code coverage can be measured in NetBeans IDE.

Tutorial how to use this feature

Create a simple J2SE project. A main class was created in the project. Add few lines to main method of the Main class.

package testapp;

public class Main {
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        if (true) {
            System.out.println("true");
         } else {
            System.out.println("false");
         }
    }

Create a simple UnitTest in Test packages folder.

package testapp;

import junit.framework.*;
public class MainTest extends TestCase {
    
    public MainTest(String testName) {
        super(testName);
    }

   void testMain() {
       Main.main(null);       
   }

}

Add Emma code coverage feature: Click to Tools|Ant's Feature in main menu. The Ant's Feature dialog is shown. Click to Add Feature button in the dialog. Add Ant's features dialog is be shown.

If Emma libraries are not installed in the IDE the downloading dialog will be shown. After successfull installation Emma Code Coverage panel is shown.

If you click to Run Tests button the tests will be run in emma. To show the code coverage report click to Show Results button.

Detail about Implementation

To add ant extension is very simply. This part is motivation and how to create your own extension. The code of Emma ant feature was moved to xdsuite/ExperimantalTemplates. Contribution to this module will be apreciated.

Part of layer.xml

 <folder name="velocity"> 
     <folder name="customizers">
         <file name="sf-netbeans-nbxdoclet-experimentaltemplates-EmmaCustomizer.instance" />
     </folder>
     <folder name="templates">
         <file name="emmaBuild.vm" url="emmaBuild.vm"/>
     </folder>
     <folder name="build">
         <file name="EmmaBuild.xml" url="emmaBuild.xml"/>
     </folder> 
 </folder>    

EmmaBuild.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
 Code coverage ant extension
-->
<featuredefs>
   <featuredef name="Emma-codeCoverage" customizerClassRef="sf-netbeans-nbxdoclet-experimentaltemplates-EmmaCustomizer.instance" >
       <title>Emma Code Coverage</title>
       <velocityTemplate vmFile="emmaBuild.vm"/>
          <classpath name="emma">
               <library name="emma-2.0.jar" repository="default"/>
               <library name="emma_ant-2.0.jar" repository="default"/>
          </classpath>  
   </featuredef>
</featuredefs>

Part code of Emma Customizer


    private void clearResultsButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                   
         feature.getBuildScript().executeTarget("emma-clean", null);
    }                                                  

    private void showResultsButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                  
            try {
                 FileObject fo = feature.getBuildScript().getProjectFile().getFileObject("build/coverage/coverage.html");
                 if (fo != null) { 
                     HtmlBrowser.URLDisplayer.getDefault ().showURL (fo.getURL ());
                 }
             } catch (FileStateInvalidException e) {
             }
 
    }                                                 

    private void runCoverageButtonActionPerformed(java.awt.event.ActionEvent evt) {                                                  
         feature.getBuildScript().executeTarget("emma-report", null);
         
    }                                                 

    public Feature getFeature() {
        return feature;
    }

    public String save() {
        // no persistent data
        return null;
    }

    public void setFeature(Feature feature) {
       this.feature = feature;
    }
    

EmmaBuild.vm template

 <folder name="velocity"> 
     <folder name="customizers">
         <file name="sf-netbeans-nbxdoclet-experimentaltemplates-EmmaCustomizer.instance" />
     </folder>
     <folder name="templates">
         <file name="emmaBuild.vm" url="emmaBuild.vm"/>
     </folder>
     <folder name="build">
         <file name="EmmaBuild.xml" url="emmaBuild.xml"/>
     </folder> 
 </folder>   
   
  <target name="emma-init" depends="project-extension-init,compile" >
        <property name="emma.out.dir" value="${build.dir}/out" />
        <mkdir dir="${emma.out.dir}" />

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

        <!-- directory that contains emma.jar and emma_ant.jar: -->

        <!-- path element used by EMMA taskdef below: -->
        <path id="emma.lib" >
            <path path="${emma.classpath}"/> 
        </path>

        <!-- this loads <emma> and <emmajava> custom tasks: -->
        <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
        
        <path id="run.classpath" >
            <pathelement location="${build.classes.dir}" />
        </path>
        <property name="emma.enabled" value="true" />
        <!-- EMMA instr class output directory (it is important to create
        this property only when EMMA is enabled:
        -->
        <property name="out.instr.dir" value="${basedir}/${build.dir}/outinstr" />
        <mkdir dir="${out.instr.dir}" />
        <property name="emma.filter" value="" />
    </target>

    
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

    <!-- EMMA ANT tasks are implemented as pseudo-nested tasks: <emma>
    container task can contain an arbitrary sequence of <instr>,
    <report>, and <merge>. Both the container tag and each of the nested
    elements support an optional boolean 'enabled' attribute: setting it
    to 'false' will no-op the element. This is convenient for
    sandwhiching normal build tasks between EMMA tasks such that coverage
    instrumentation and reporting could be enabled on demand.
    -->
    <target name="emma-instrument" depends="init, compile,emma-init" description="runs the examples" >
        <!-- Note that EMMA takes regular ANT path elements as instrumentation
        input, which is exceedingly convenient:
        -->
            <echo message="build.classes.dir = ${build.classes.dir}"/>
            <echo message="out.instr.dir = ${out.instr.dir}"/>
        <emma enabled="${emma.enabled}" >
            <instr instrpath="${build.classes.dir}"
                destdir="${out.instr.dir}"             
                metadatafile="${emma.coverage.dir}/metadata.emma"
                merge="true"
                >
                <!-- note that coverage filters can be set through nested <filter>
                elements as well: many of EMMA setting are 'mergeable' in the
                sense that they can be specified multiple times and the result
                is a union of all such values. Here we are not merging several
                filters together but merely demonstrating that it is possible:
                --> 
                <filter value="${emma.filter}" />
            </instr>
        </emma>
    </target>
    <!-- run Main. In v2.0, EMMA coverage data is dumped on JVM exit. For
    this to happen the JVM must be forked:
    -->
    <target name="emma-run" depends="init,compile-test,emma-init,emma-instrument,compile-test,-pre-test-run"> 
        
        <junit showoutput="true" fork="true" dir="${basedir}" failureproperty="tests.failed" errorproperty="tests.failed">
            <batchtest todir="${build.test.results.dir}">
                <fileset dir="${test.src.dir}" />
            </batchtest>
            <classpath>
               <path location="${out.instr.dir}"/> 
               <path path="${run.test.classpath}"/>
               <path path="${emma.classpath}"/>
            </classpath>
            <syspropertyset>
                <propertyref prefix="test-sys-prop."/>
                <mapper type="glob" from="test-sys-prop.*" to="*"/>
            </syspropertyset>
            <formatter type="brief" usefile="false"/>
            <formatter type="xml"/>
            <jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
            <jvmarg value="-Demma.coverage.out.merge=false" />
        </junit>
 
    </target>

    <target name="emma-report" if="have.tests" depends="emma-run">
        <!-- if enabled, generate coverage report(s): -->
        <emma enabled="${emma.enabled}" >
            <report sourcepath="${src.dir}"
                sort="+block,+name,+method,+class"
                metrics="method:70,block:80,line:80,class:100"
                >
                <!-- collect all EMMA data dumps (metadata and runtime)
                [this can be done via nested <fileset> fileset elements
                or <file> elements pointing to a single file]:
                -->
                <fileset dir="${emma.coverage.dir}" >
                    <include name="*.emma" />
                </fileset>

                <!-- for every type of report desired, configure a nested
                element; various report parameters
                can be inherited from the parent <report>
                and individually overridden for each report type:
                -->
                <txt outfile="${emma.coverage.dir}/coverage.txt"
                depth="package"
                columns="class,method,block,line,name"
                />
                <xml outfile="${emma.coverage.dir}/coverage.xml"
                depth="package"
                />
                <html outfile="${emma.coverage.dir}/coverage.html"
                depth="method"
                columns="name,class,method,block,line"
                />
            </report>
        </emma>
    </target>

   <target name="emma-clean" >
   
   </target>