Summary: A Sample Cobertura Ant build script.
I still haven't gotten around to writing a Cobertura code-coverage tutorial, but in lieu of that, I thought I'd include an ant build script here that does a lot of powerful things, including a task that generates Cobertura code-coverage reports.
If you've never seen a sample Cobertura report, there's a sample report here.
If you've never heard some of the benefits of code coverage in general, I wrote a short blog about the benefits of code coverage here.
I'll just provide the entire ant build script here, and then discuss some of the features afterwards:
<project name="WikiTeX Editor" basedir=".." default="package">
<!-- project-specific variables -->
<property name="package.name" value="wikitex.jar" />
<property name="properties.file" value="wikitex.properties" />
<property name="manifest.main.class" value="com.devdaily.wikitex.Main" />
<property name="manifest.built.by" value="Alvin J. Alexander" />
<property name="manifest.created.by" value="DevDaily.com" />
<property name="version.number" value="1.0" />
<property name="build.number" value="1984" />
<property name="lib.dir" value="lib" />
<property name="lib.cobertura.dir" value="${lib.dir}/cobertura-1.9" />
<property name="cobertura.jar.file" value="${lib.dir}/cobertura-1.9/cobertura.jar" />
<property name="src.tests.dir" value="src-tests" />
<property name="cob.ser.file" value="cobertura.ser" />
<property environment="env" />
<property name="build.dir" value="build" />
<property file="${build.dir}/build.${env.HOSTNAME}" />
<property name="src.dir" value="src" />
<property name="reports.dir" value="reports" />
<property name="resources.dir" value="resources" />
<property name="dest.dir" value="target" />
<property name="dest.dir.classes" value="${dest.dir}/classes" />
<property name="dest.dir.lib" value="${dest.dir.classes}/lib" />
<property name="package.file" value="${dest.dir}/${package.name}" />
<!-- cobertura properties -->
<property name="cobertura.dir" value="cobertura" />
<property name="cob.instrumented.dir" value="${cobertura.dir}/instrumented" />
<property name="cob.reports.dir" value="${cobertura.dir}/reports" />
<property name="cob.reports.xml.dir" value="${cob.reports.dir}/junit-xml" />
<property name="cob.reports.html.dir" value="${cob.reports.dir}/junit-html" />
<property name="cob.coverage.xml.dir" value="${cob.reports.dir}/cobertura-xml" />
<property name="cob.coverage.html.dir" value="${cob.reports.dir}/cobertura-html" />
<path id="build.class.path">
<fileset dir="lib">
<include name="**/*.jar" />
</fileset>
</path>
<target name="clean">
<delete dir="${dest.dir}" />
<delete dir="${reports.dir}" />
<delete dir="${cobertura.dir}" />
<delete file="${cob.ser.file}" />
</target>
<target name="prepare" depends="clean">
<mkdir dir="${dest.dir}" />
<mkdir dir="${dest.dir.classes}" />
<mkdir dir="${dest.dir.classes}/META-INF" />
<mkdir dir="${reports.dir}" />
<!-- cobertura directories -->
<mkdir dir="${cobertura.dir}" />
<mkdir dir="${cob.instrumented.dir}" />
<mkdir dir="${cob.reports.xml.dir}" />
<mkdir dir="${cob.reports.html.dir}" />
<mkdir dir="${cob.coverage.xml.dir}" />
<mkdir dir="${cob.coverage.html.dir}" />
</target>
<target name="compile" depends="prepare">
<echo>=== COMPILE ===</echo>
<echo>Compiling ${src.dir} files ...</echo>
<javac debug="on" srcdir="${src.dir}" destdir="${dest.dir.classes}" includes="com/**">
<classpath refid="build.class.path" />
<!-- classpath refid="cobertura.classpath" -->
</javac>
<!-- compile files on the src-tests path -->
<echo>Compiling ${src.tests.dir} files ...</echo>
<javac debug="on" srcdir="${src.tests.dir}" destdir="${dest.dir.classes}" includes="com/**">
<classpath refid="build.class.path" />
<!-- classpath refid="cobertura.classpath" -->
</javac>
</target>
<target name="package" depends="compile">
<echo>=== PACKAGE ===</echo>
<!-- convert classpath to flat list for use in manifest task -->
<pathconvert property="mf.classpath" pathsep=" ">
<path refid="build.class.path" />
<flattenmapper />
</pathconvert>
<tstamp/><!-- needed for TODAY -->
<manifest file="MANIFEST.MF">
<attribute name="Built-By" value="${manifest.built.by}"/>
<attribute name="Created-By" value="${manifest.created.by}"/>
<attribute name="Main-Class" value="${manifest.main.class}"/>
<attribute name="Implementation-Version" value="${version.number}-b${build.number}"/>
<attribute name="Built-Date" value="${TODAY}"/>
<attribute name="Class-Path" value="${mf.classpath}" />
</manifest>
<jar basedir="${dest.dir.classes}"
destfile="${package.file}"
includes="**/*.*"
excludes="**/*Test*"
manifest="MANIFEST.MF" />
<copy todir="${dest.dir}">
<fileset dir="${lib.dir}">
<exclude name="${lib.cobertura.dir}" />
<exclude name="junit*" />
<include name="*.jar"/>
<include name="*.zip"/>
</fileset>
</copy>
<!-- move this file before the 'jar' task (and put it in the 'classes' dir) if you want to
include it in the jar -->
<copy file="${resources.dir}/log4j.properties" tofile="${dest.dir}/log4j.properties" overwrite="true" />
<copy file="${resources.dir}/${properties.file}" tofile="${dest.dir}/${properties.file}" overwrite="true" />
<delete dir="${dest.dir.classes}" />
</target>
<!-- INSTALL (no install target is needed) -->
<!-- classpath for test needs to include the dist/classes directory -->
<path id="classpath.test">
<pathelement location="${dest.dir.classes}" />
<fileset dir="lib">
<include name="**/*.jar" />
</fileset>
</path>
<!-- may want to modify this, and only allow deployment if the tests run -->
<target name="test" description="Run JUnit Tests" depends="compile">
<echo>=== UNIT TESTS ===</echo>
<delete>
<fileset dir="${reports.dir}" includes="**/TEST-*.txt"/>
</delete>
<junit dir="./" printsummary="yes" fork="yes" haltonfailure="yes">
<formatter type="plain"/>
<batchtest fork="yes" todir="${reports.dir}">
<fileset dir="${dest.dir.classes}">
<include name="**/Test*"/>
<include name="**/*Tests*"/>
<exclude name="**/AllTests*"/>
<exclude name="**/TestingUtils*"/>
</fileset>
</batchtest>
<classpath>
<path refid="classpath.test"/>
</classpath>
</junit>
</target>
<!-- cobertura task definition -->
<path id="cobertura.classpath">
<fileset dir="${lib.dir}">
<include name="${cobertura.jar.file}" />
<include name="**/*.jar" />
</fileset>
</path>
<taskdef classpathref="cobertura.classpath" resource="tasks.properties"/>
<target name="instrument" depends="compile">
<cobertura-instrument todir="${cob.instrumented.dir}">
<fileset dir="${dest.dir.classes}">
<include name="**/*.class"/>
</fileset>
</cobertura-instrument>
</target>
<target name="cover-test" depends="instrument">
<junit dir="./" failureproperty="test.failure" printsummary="yes"
fork="true" haltonerror="true">
<classpath location="${cobertura.jar.file}"/>
<classpath location="${cob.instrumented.dir}"/>
<classpath>
<path refid="build.class.path"/>
</classpath>
<batchtest fork="yes" todir="${reports.dir}">
<fileset dir="${cob.instrumented.dir}">
<include name="**/Test*"/>
<include name="**/*Tests*"/>
<include name="org/jaxen/javabean/*Test*" />
<exclude name="**/TestingUtils*"/>
<exclude name="**/AllTests*"/>
</fileset>
</batchtest>
</junit>
</target>
<!-- run this target to generate the coverage reports -->
<target name="coverage-report" depends="cover-test">
<cobertura-report srcdir="${src.dir}" destdir="${cobertura.dir}"/>
</target>
</project>
I could write about this build script for a long time, but I don't have that much time today, so here are the highlights, especially the Cobertura-related highlights:
- Any Ant build script like this is dependent on your directory structure. If you're used to Ant you can infer my directory names from the properties I set early in the script, but to summarize them here, my project directories are build, classes, lib, src, src-test, and reports. Also, this build.xml file is kept in the build subdirectory, and I run Ant from that directory.
- The tasks I typically run are
testorcompile(depending on where I am in the development process), orcoverage-reportwhen I want to see my code coverage results. - The
packagetask creates an executable jar file that can be easily run by users with thejava-jarcommand. - The executable jar file includes a manifest file, and that manifest file includes a classpath that I create dynamically, based on the jar files in my
libdirectory.

