madbean

Clovering Clover

11 Mar 2003

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".

mlq: I'm writing a blog about clover... will be interesting (hopefully): clovering clover
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.

  • Home
  • Blog