Friday, September 17, 2010

maven clover2 plugin adventures


Today at my day job I ran into a strange problem with running clover2 plugin. I have been tweaking the assembly descriptors so that:

the assembly plugin creates a zip file containing the project's jar and all the depedendencies
the project's jar is zipped in the main "directory" of the zip file
the dependencies are zipped to the lib/ subdir of the zip file

I have inherited an assembly descriptor that seemed to work well, until...

SOMEONE RAN MVN 3.0


and then it turned out that the assembly descriptor worked... by accident (more likely because of some bug). The original assembly descriptor looked like this:


 ${artifactId}
 
  zip
 
 true
 
 
  
   true
   lib
   
    ${artifactId}*.jar
   
  
  
   true
   
    ${artifactId}*.jar
   
  
 
 
  
   conf
   conf
  
 


What happened is that in maven 3.0 all the jar files got zipped to the lib subdirectory. This was awkward. Naturally, I wanted to debug the reality... or for the assembly to work the same way in mvn 2.x and 3.0. So I came up with this descriptor:


 distribution
 
  zip
 
 true
 
 
  
   true
   lib
   
    ${groupId}:${artifactId}
   
  
 
 
  
   ${project.build.directory}
   
   
    ${artifactId}-${version}.jar
   
  
  
   conf
   conf
  
 

Now this all seemed to work well, until...

I RAN CLOVER


which caused the build to fail. What? How come? The interesting thing is that the project was essentially empty - I was creating a new project and setting it up in maven, svn, hudson and whatnot. And by empty I mean - no code (yet). So I fired up the commandline:

mvn clover2:instrument clover2:clover
[INFO] [clover2:instrumentInternal]
[WARNING] No Clover instrumentation done on source files as no matching sources files found
[INFO] [resources:resources]
[INFO] [compiler:compile]
[INFO] [resources:testResources]
[INFO] [compiler:testCompile]
[INFO] [surefire:test]
...
[INFO] [jar:jar]
[INFO] [assembly:single {execution: make-assembly}]
[INFO] Reading assembly descriptor: /src/main/assembly/assembly.xml
[INFO] Processing DependencySet (output=lib)
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to create assembly: Error creating assembly archive distribution: You must set at least one file.
Wait, WTF? Why on earth is this running an assembly?!?!? Oh, wait, it also runs all the phases of the default lifecycle before that - WHAT? I began thinking and fired up again:

mvn package

That worked - huh? Now, this seemed VERY suspicious. So I googled for some documentation and figured out why the clover2:instrument runs the package phase - because it will install the instrumented artifacts! You might wonder why would you want to install instrumented artifacts. So do I - if you have an answer - let me know!

And of course because of that the above invocation is NOT the best way to test your coverage, because:
  • it forks the lifecycle, which can result in launching the same tests twice (once for just testing, and once for coverage, which does not make much sense)
  • the assembly plugin behaves differently in the forked lifecycle
  • the forked lifecycle executes up to install phase, which means that every time you run this, a snapshot version of the instrumented artifacts is installed into the local repo >8-O
All of this might seem obvious, but I figured out from this documentation page that the best way to run clover is

mvn clover2:setup test clover2:clover

This does not fork the lifecycle, the assembly plugin does not get launched and all goes well. I am still wondering about why the assembly plugin works in a different way in the forked lifecycle, though...