Preamble/Disclaimer
- Clover 1.1 has been released.
- I work for Cortex, the company that makes Clover.
About Clover
Clover is a code coverage tool, which you can use to examine how much of your code gets executed when you run your unit tests. This may not seem very interesting to you; so think about these statements before and after running Clover:
Before: “Oh, our unit tests are pretty good.”
After: “Oh dear; our unit tests are only touching
20% of our code base.”
Before: “Oh, our unit tests are pretty good.”
After: “What? 40% of our unit tests aren’t running. Oh,
we misconfigured Ant in our continuous-integration environment.”
Before: “We don’t know how good our unit tests are. We have the
green bar of goodness;
but we do occasionally find bugs that our tests didn’t detect.”
After +0: “Okay, we can now see exactly what we are not testing, lets put
some tests here, here and here.”
After +1: “Oh. Those new tests caused red-bar; better fix those bugs.”
After +2: “Got green-bar again, and our coverage is on the rise.
Addicted.”
Clovering Clover
So, as you might imagine, the Clover development team uses Clover when running the Clover unit tests. This is “eating our own dog food”.
obj: eating your own dog food – I like it
obj: interesting – I have to keep rejecting (or fixing) patches for Checkstyle, because they do not pass Checkstyle
So, OJB ensures that Checkstyle passes Checkstyle; but Clovering Clover is not that easy.
As part of it’s job, Clover needs to instrument the source code it is going
to be measuring. Then, at runtime, a class called CloverRecorder
is used to keep the tally of method, statement, etc. counts.
When each instrumented class is initialised in the JVM, a CloverRecorder
is configured for it (using a static variable, usually).
Now, when we are Clovering Clover, we naturally instrument CloverRecorder.
So, at runtime, as the CloverRecorder class gets initialised, it needs
to configure a CloverRecorder. But that class isn’t finished loading yet!
By Clovering Clover, we introduce a circular initialisation problem.
(The above explanation is a slightly simplified version of how Clover actually works; the point is that Clovering Clover causes some mind-bending circular dependencies!)
We came up with a fairly fun fix to this problem (which I have just spent the last day fine tuning). First, we build Clover the good old-fashioned way (and we quickly run the unit tests the old-fashioned way, too). This is our “real” Clover.
We then copy all the source (including unit tests)
to a temp directory
and use Ant’s <replace> to rename all the package/import statements
from com.cortex... to repackaged.com.cortex....
We compile this source using the “real” Clover, and run the “pretend” unit
tests over the “pretend” code. There are no circular dependencies, since there are no relationships
between the “real” code and the “pretend” code; they are disjoint.
Sometimes coding isn’t just about lines of code; it’s about bending your mind and bending it back again.
