Clovering Clover
# 2003-03-11 13:22:48 -0500 | Java | 5 CommentsPreamble/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.
Great article - but I do not understand why you need "pretend" unit tests. Is it because your tests require access to package protected types/methods?
Oliver, the reason why we have "pretend" unit tests is
that it is just simpler.
Currently, our unit tests are in the same "package" as the
class they test (though they are in different source tree).
Therefore, they don’t need to import the class being tested.
Now, if we re-packaged the code but not the tests, then
the tests wouldn’t compile (the tests would need to import
the "pretend" classes explicitly in this instance)
What’s the code coverage % of clover’s unit tests? :)
You don’t have to just through all those hoops.
Simply create your own classloader class for loading the covered classes that "defines" the classes it loads itself.
This works because the VM identifies a class by using the <ClassLoaderObjectId, ClassName> pair.
So your coverage analyzer and any classes it analyzes will remain disjoint.
(It’s a little tricky writing your own classloader to do this - took me about a day.)
Noel, the problem is, that the very class doing the instrumenting is the one we want to instrument… You can get two different classloaders to load the same class twice, but you can’t get the same classloader to load the one class two "different" ways…