How to get Cobertura and Ivy working together? - ant

I was just starting to integrate Cobertura into our main product's build process which uses Ivy as the dependency management tool. There are several libraries lib-a, lib-b and lib-c that do not have test cases and one project that depends on these libs and contains the unit and integration tests for all of these libraries.
Generally, instrumenting, running the instrumented tests and generating the Cobertura report works. However, there are several problems:
During instrumentation, there are several warnings (about 10) reported:
Problems instrumenting archive entry: a.b.c.MyClassFoo java.lang.RuntimeException: java.lang.ClassNotFoundException: a.b.c.MyClassBar
However, the reported class is exisiting. At the end of instrumentation, it reports
Saved information on 364 classes
When viewing the report, it shows all classes, but all classes of dependent libs are reported as 0% coverage.
When viewing details, it reports that no sources where found.
Now I think the problem might be, that Cobertura has problems with
- instrumenting the class files provided as jar files and
- Getting the sources from jar files
The instrumentation ant task in my build.xml does the following:
<cobertura-instrument todir="${build.dir}/instrumented-classes">
<includeClasses regex="com\.mycompany.*" />
<instrumentationClasspath>
<path refid="default.test.classpath" />
<pathelement location="${build.classes.dir}" />
</instrumentationClasspath>
</cobertura-instrument>
Should this be sufficient?
Im wondering, because of the reported warnings. All reported classes can be found within the jars.
For the second problem, I don't even know how to provide sources as jars to cobertura-report...
I tried
<cobertura-report destdir="${build.dir}/coverage">
<fileset dir="${src.dir}">
<include name="**/*.java" />
</fileset>
<ivy:cachefileset conf="runtime-test" type="sources"/>
</cobertura-report>
but it says cachefileset is not supported. I also tried using a pathid which I also can't provide in cobertura-report.
Do I have to unzip all sources before (which would be very time-consuming) and then provide them as a normal fileset?

Ok, while refactoring my Cobertura Ant tasks, I was able to solve the problem with dependency classes reported as 0% coverage.
At first my test task contained the following:
<junit printsummary="yes" haltonfailure="no">
<classpath>
<pathelement path="${build.test.classes.dir}"/>
<pathelement path="${build.dir}/instrumented-classes"/>
<pathelement path="${build.classes.dir}"/>
<path refid="default.test.classpath"/>
<path refid="cobertura.classpath"/>
</classpath>
(...)
</junit>
The problem was, that instrumenting the jar files provided by ivy results in jar files within the ${build.dir}/instrumented-classes directory. However, pathelement supposedly only looks for class files. So I added a fileset to include the instrumented jars as well:
<junit printsummary="yes" haltonfailure="no">
<classpath>
<pathelement path="${build.test.classes.dir}"/>
<pathelement path="${build.dir}/instrumented-classes"/>
<fileset dir="${build.dir}/instrumented-classes">
<include name="*.jar" />
</fileset>
<pathelement path="${build.classes.dir}"/>
<path refid="default.test.classpath"/>
<path refid="cobertura.classpath"/>
</classpath>
(...)
</junit>
The other problems still remain.

Related

ANT: ways to include libraries and license issues

I have been trying to use Ant to compile and ready a project for distribution. I have encountered several problems along the way that I have been finally able to solve but the solution leaves me very unsatisfied. First, let me explain the set-up of the project and its dependencies.
I have a project, lets call it Primary which depends on a couple of libraries such as the fantastic Guava. It also depends on another project of mine, lets call it Secondary. The Secondary project also features some dependencies, for example, JDOM2. I have referenced the Jar I build with Ant in Primary.
Let me give you the interesting bits of the build.xml so you can get a picture of what I am doing:
<project name="Primary" default="all" basedir=".">
<property name='build' location='dist' />
<property name='application.version' value='1.0'/>
<property name='application.name' value='Primary'/>
<property name='distribution' value='${application.name}-${application.version}'/>
<path id='compile.classpath'>
<fileset dir='libs'>
<include name='*.jar'/>
</fileset>
</path>
<target name='compile' description='Compile source files.'>
<javac includeantruntime="false" srcdir="src" destdir="bin">
<classpath refid='compile.classpath'/>
</javac>
<target>
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<jar destfile='${build}/${distribution}.jar'>
<fileset dir="bin"/>
<zipgroupfileset dir="libs" includes="*.jar"/>
</jar>
</target>
The Secodnary project's build.xml is nearly identical except that it features a manifest as it needs to run:
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<jar destfile='${dist}/${distribution}.jar' basedir="${build}" >
<fileset dir="${build}"/>
<zipgroupfileset dir="libs" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="lu.tudor.ssi.kiss.climate.ClimateChange"/>
</manifest>
</jar>
</target>
After I got it working, trying for many hours to not include that dependencies as class files but as Jars, I don't have the time or insight to go back and try to figure out what I did wrong. Furthermore, I believe that including these libraries as class files is bad practice as it could give rise to licensing issues while not packaging them and merely including them in a directory along the build Jar would most probably not (And if it would you could choose not to distribute them yourself).
I think my inability to correctly assemble the class path, I always received NoClassDefFoundError for classes or libraries in the Primary project when launching Second's Jar, is that I am not very experienced with Ant. Would I require to specify a class path for both projects? Specifying the class path as . should have allowed me to simply add all dependencies to the same folder as Secondary's Jar, should it not?
You may use the MANIFEST.MF "Class-Path: " to cross-reference your jars.
If they are all in the same directory this will probably work as follows (using it in both projects!):
<target name='jar' description='Create a jar file for distribution.' depends="compile">
<pathconvert property="manifest.classpath" pathsep=" ">
<path refid="compile.classpath" />
<flattenmapper />
</pathconvert>
<jar destfile='${build}/${distribution}.jar'>
<fileset dir="bin"/>
<manifest>
<attribute name="Class-Path" value="${manifest.classpath}"/>
</manifest>
</jar>
</target>
This way you can tell the java runtime environment that your jar needs others to work, expecting them to be in the same directory as the jar you are trying to run.
As a result your primary.jar should have secondary.jar in it's classpath and secondary.jar should have guava.jar in it's classpath.
Another way to create the string may be ants manifestclasspath task (https://ant.apache.org/manual/Tasks/manifestclasspath.html) that can handle subdirectories.
If you are goin to use more and more libraries, you may want to have a closer look at ivy or even maven.

How to display the classpath build in an Ant Junit task

An Ant junit task that has worked for months is suddenly failing with a NoClassDefFoundError for classes that used to be found. Is there a way to display the classpath that is built in the junit task?
<target name="basic-junit-test" description="Run a single JUnit test. ">
<junit printsummary="yes" fork="no" haltonfailure="yes">
<classpath>
<pathelement location="target/WEB-INF/lib/log4j-1.2.16.jar"/>
.
. many other pathelements
.
</classpath>
<test name="com.mycompany.command.TestUNLOCKACCOUNTCommand" outfile="${report.dir}/junit_test_results" />
</junit>
</target>
I'm a big fan of declaring my Ant paths at the top of my build and using classpath references in the various tasks.
To pathconvert task can be used to print the classpath contents as a property:
<path id="test.path">
<pathelement location="target/WEB-INF/lib/log4j-1.2.16.jar"/>
.
. many other pathelements
.
</path>
<target name="echo-path" description="Echo test path">
<pathconvert targetos="unix" property="test.path.unix" refid="test.path">
<echo message="Test path: ${test.path.unix}"/>
</target>
<target name="basic-junit-test" depends="echo-path" description="Run a single JUnit test. ">
<junit printsummary="yes" fork="no" haltonfailure="yes">
<classpath>
<path refid="test.path"/>
</classpath>
<test name="com.mycompany.command.TestUNLOCKACCOUNTCommand" outfile="${report.dir}/junit_test_results" />
</junit>
</target>
Update
Just occurred to me: an even simpler solution might be to run Ant in debug mode.
This is not really the answer to my question, Mark O'Connor and Rebse gave excellent answers, instead this is a more thorough explanation of what happened to cause me to ask the question in the first place. I had an ANT Junit task that I had used to develop about 100 controller classes. I used it for the first time in several months and every test failed with a classNotFound exception. The class that was not found was one that I was sure should be on the classpath, it was a locally created jar file that is automatically picked up for the build. I thought that somehow the classpath was at fault so I wanted to display it when a test was run.
After many attempts to figure out what was going on I put a try block around the code that was producing the classNotFound exception and I saw that the local class was not the class that was not being found. This lead me to a search of the lib directory and eventually (after about six hours) I realized that the problem was that I had replaced an older version of slf4j-api with a newer version. There was a dependency on a method that was in the older version but not in the newer.
Mark's answer just helped me, but just a quick note as I needed to close the pathconvert xml element when copying the example in the answer.
<pathconvert targetos="unix" property="test.path.unix" refid="test.path" />

I wonder if there is something wrong with my ant junit task

I'm writing my build.xml. However, it seems there is something go wrong with my junit task. when I run my junit task. I can build success, but the junit report only show that run 1 test with error. But I have more than 10 tests. So I wonder if there is something run with my junit task. Here is my code.
<property name="src.dir" value="src"/>
<property name="bin.dir" value="bin"/>
<property name="dest.dir" value="dest"/>
<property name="test.dir" value="test/>
<property name="lib.dir" value="lib"/>
<path id="classpath">
<pathelement location="${lib.dir}/junit-4.11.jar"/>
<pathelement location="${lib.dir}/ant-junit4.jar"/>
</path>
<target name="test" depends="compile">
<junit printsummary="yes" haltonfailure="no">
<classpath>
<pathelement location="${bin.dir}"/>
<path refid="classpath"/>
</classpath>
<formatter type="plain" usefile="false"/>
<batchtest fork="yes">
<fileset dir="${test.dir}" includes="*Test*.java"/>
</batchtest>
</junit>
</target>
I cannot figure out what is wrong so could somebody help me out?
And what is happening? Do you get any error messages?
You usually need to do the following:
Compile your normal code. The resulting *.class files should be placed inside a directory such as target/classes or build/classes. Use the destdir parameter of the <javac> task to do this.
Compile your JUnit tests.
In your classpath, you need all of the jars you needed to compile your normal classes
You need a reference to the destdir where your normal classes were compiled to.
You need the JUnit jar.
These should be compiled to a different directory from your normal jars. Normally, this is target/test-classes or build/test-classes.
Once you've compiled the JUnit tests, you may run them. You can use the <junit> task like you did.
You need to make sure that the includeantruntime parameter is set to true
You should also set fork to true.
You need the same classpath (with all three elements you had) when you compiled the test classes. More jars might be needed, but usually not.
You run the tests on the compiled JUnit test classfiles (the ones you saved to target/test-classes or build/test-classes. In your example, you're trying to run them against the source.
I use the Maven standards for my directory layout. That means my Java source is under src/main/java while my JUnit Java files are under src/test/java. Any XML or properties or other none source files needed are stored in src/main/resources. The regular source is compiled to target/classes while the Junit sources are compiled to target/test-classes.
This makes it easy to compile your code and test code separately without worrying about **/test/**, **/Test/**, **/JUnit/** exceptions in directory compiling since everything is separate.
Hope this helps.

Compiling a build.xml file using Ant

I recently installed Ant 1.8.4 and JasperReports 4.6.0 on my Ubuntu machine.
The following environmental variables were set on my account:
PATH=$PATH:/opt/ant/bin
export PATH
export ANT_HOME=/opt/ant
export JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64
When I try to run a demo build file in the JasperReports demo samples directory using the command ant I get the following error:
Buildfile: build.xml
BUILD FAILED
/opt/jasperreports-4.6.0/demo/samples/antcompile/build.xml:3: The following
error occurred while executing this line:
jar:file:/opt/ant/lib/ant.jar!/org/apache/tools/ant/antlib.xml:37: Problem: failed to create task or type componentdef
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
Any help in solving this problem will be super helpful.
The snippet of build.xml file:
<project name="antcompile" default="test" basedir=".">
<description>Shows how multiple JRXML files can be compiled in batch mode using ANT.</description>
<path id="classpath">
<pathelement location="../../../build/classes"/>
<fileset dir="../../../lib">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="runClasspath">
<path refid="classpath"/>
<pathelement location="../../fonts"/>
<pathelement location="./build/classes"/>
</path>
<taskdef name="jrc" classname="net.sf.jasperreports.ant.JRAntCompileTask">
<classpath refid="classpath"/>
</taskdef>
<target name="javac" description="Compiles the Java source files used in the report designs.">
<mkdir dir="./build/classes"/>
<javac srcdir="./src" destdir="./build/classes" debug="true" optimize="false" deprecation="false"/>
</target>
<target name="compile1" description="Compiles report designs specified using the "srcdir" in the <jrc> tag."> <!-- 27 row # -->
<mkdir dir="./build/reports"/>
<jrc
srcdir="./reports"
destdir="./build/reports"
tempdir="./build/reports"
keepjava="true"
xmlvalidation="true">
<classpath refid="runClasspath"/>
<include name="**/*.jrxml"/>
</jrc>
</target>
This Ant script is using custom task jrc.
As you can see from the snippet below (this is build.xml file from the jasperreports-4.6.0/demo/samples/antcompile folder), this task's definition refers the classpath from the same build file.
<path id="classpath">
<pathelement location="../../../build/classes"/>
<fileset dir="../../../lib">
<include name="**/*.jar"/>
</fileset>
</path>
...
<taskdef name="jrc" classname="net.sf.jasperreports.ant.JRAntCompileTask">
<classpath refid="classpath"/>
</taskdef>
You should check the ../../../build/classes folder (in JasperReports package's folder structure which contains samples) - the net.sf.jasperreports.ant.JRAntCompileTask class must be there.
In other words you should put this class (or jasperreports-4.6.0.jar) to the classpath (path id="classpath").
Another probable source of your problem is the version of Ant package.
You can read about Project#createTask complains it wouldn't find task componentdef issue on Ant's bugtracker and project.createTask() not working with ant-1.8.2 post.
I made it work by changing the following element in my CLASSPATH, /opt/jasperreports-4.6.0/lib/ant-1.7.1.jar to /opt/ant/lib/ant.jar.
Thanks to Alex for posting the helpful links!
Anjan
You're going have to help us out a bit here...
Are you building JasperReports-4.6.0? Or, are you using JasperReports as part of your build.xml? Is this a test build.xml demoing JasperReports?
The error says Check that any custom tasks/types have been declared, so what is the Ant task in line #37? Is there a in the build.xml? Does it have a classpath defined? If you have a taskdef, please let us see what it is, and what the custom task is.
I'm downloading iReport to see if I can figure out what you're doing, but it's taking 15 minutes. I bet you're supposed to put some jar into $ANT_HOME/lib. Maybe that JasperReports or iReport jarfile.
As soon as I can download iReport and see what you're talking about, I'll update my answer.
Meanwhile, include the relevant code around line #35 in your build.xml and the taskdef task in your build.xml.
Finished downloading iReport and there is no build.xml file in it. You're going to have to post your code, so we can look at it.
Again, my assumption is that there's some jar file that they assumed you'd stick in /opt/ant/lib and didn't.

Ant javadoc generation of tests code cannot find symbol from src code

In my project, I have a src folder with code source of the application and test folder with code source of the application tests.
In my Ant build, I would like to separate javadoc generation of these source codes. For the src code javadoc generation, there is no problem but for the tests code javadoc generation, I've got a problem because test code uses src code.
My Ant task to generate javadoc is like that :
<path id="classpath-test">
<pathelement path="." />
<pathelement path="${testclasses.home}" />
<pathelement path="${classes.home}" />
<fileset dir="${lib.home}" includes="*.jar" />
<fileset dir="${libtest.home}" includes="*.jar" />
</path>
<target name="compile" ... > // compiles src code of the project in ${classes.home}
<target name="compile-tests" depends="compile">
<javac srcdir="${test.home}"
destdir="${testclasses.home}"
target="1.5"
source="1.5"
debug="true"
>
<classpath refid="classpath-test" />
</javac>
<copy todir="${testclasses.home}">
<fileset dir="${test.home}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="generate-javadoc-tests" depends="compile-tests" >
<javadoc sourcepath="${test.home}" packagenames="*"
destdir="${test-javadoc.home}" verbose="false"
linksource="true" encoding="${encoding}">
<classpath refid="classpath-test" />
</javadoc>
</target>
The ${test.home} variable is test folder. In the classpath-test, I put jar from junit to avoid error about annotation specifics to junit during javadoc generation. This jar is contained in ${libtest.home}.
When I generate javadoc, I have several warnings about code from test folder that using code from src folder which is normal. The errors are like that :
[javadoc] E:\workspace\app\test\com\app\MyClass.java:9: package com.app.SrcClass does not exist
[javadoc] symbol : class MyClass
[javadoc] location: class com.app.MyClass
So, someone knows a way to includes src classes in classpath to avoid these warnings but without having source code javadoc included in test code javadoc.
Or may be a way to disable these warnings because the verbose option of javadoc task to false doesn't disable these warnings.
So, someone knows a way to includes src classes in classpath to avoid
these warnings but without having source code javadoc included in test
code javadoc.
Make sure the classes on which your tests depend are on the classpath. You might want to make your javadoc generation target dependent on the target which compiles code from src and builds a jar file. Then make sure that the classpath, referenced by refid classpath-test includes that jar.

Resources