Measurement under Java
The following sections describe some special features of the instrumentation and coverage measurement process, which are provided by the Java instrumenter shipped together with CodeCover.
Coverage criteria
CodeCover can measure a number of so called coverage criteria. The basics are explained on the feature page Code Coverage and in the Specification. There are some specifics for Java, that are necessary to mention, which is done in the next few lines.
Statement coverage
Statement coverage is only measured for the following statement types:
- assignments and method calls (
a = b
orSystem.out.println(person.getName)
) break
continue
return
throw
- empty statements
;
- variable and member declarations with an assignment (
int i = 0
)
In consequence, all conditional statements (e.g. if
) and looping statements (e.g. while
) are not considered for statement coverage. If a statement is executed and it causes an exception – e.g. a NullPointerException
– the statement will nevertheless be covered. But the next statement will be uncovered, which can be a hint for an exception.
Branch coverage
Branches are created by:
if
andelse
switch
-branches:case
anddefault
try
has a branch for eachcatch
and one single branch for an uncatched exception and the successful execution of the wholetry
-block
The branch coverage, that is measured for Java, does not consider the body of a looping statement as a branch. Moreover the branches, created by possible exceptions – e.g. a NullPointerException
– are ignored too.
Condition coverage
Boolean terms within the following statements are considered:
if
for
, if it is no enhanced for statementwhile
do .. while
Boolean expressions in assignments, method parameters and ternary operators (a ? b : c
) are ignored.
Our approach for conditional coverage needs a special instrumentation of Java boolean terms. Unfortunately this approach makes a boolean expression more complex. If assignments are used in boolean terms of if
, while
and so on, the compilers might get problems, to decide about the definite assignment when analyzing the control flow. Take a look at the following example:
public class B {
public Object bar() { return null; }
public Object foo(Object b) throws Exception {
Object a;
if (b == null || ((a = bar()) == null)) { throw new Exception();}
return a.toString();
}
}
Getting to know, whether a
is definitely assigned before its usage is very complex after instrumentation. For this reason, CodeCover does not consider boolean expression, when there occurs at least one assignment (a = b
) operator.
Loop coverage
Loop coverage is measured for the following statements:
for
, even if it has a fixed number of executionswhile
do .. while
(the coverable item for zero-execution is not required!)
Test case selection
All coverage data collected during a test run is called a test session. You can subdivide this test session into test cases. There is a number of methods to do this.
One test case
If you just instrument your classes and run it on your own, only one test case containing all coverage data will be produced. This test case has the name UNNAMED TESTCASE.
Comments
You can use specific comments in source files, that will be instrumented. These comments will be translated into method calls during the instrumentation process. For this reason, it is important, that you place these comments at such positions in your source files, where a method call is allowed. Otherwise you will receive compiler errors.
The possible comments are:
// startTestCase("NAME");
// startTestCase("NAME", "COMMENT");
// endTestCase();
// endTestCase("NAME");
// endTestCase("NAME", "COMMENT");
// finishTestSession();
You have to ensure, that a comment looks exactly like one of these patterns. Otherwise a comment will be ignored.
The start and end test case comments explicitly start or end a test case. The start of a test case implicitly ends a prior test case, if it has not ended yet. So two test cases can not overlap.
The finish test session comment forces the coverage measurement to stop, flush all remaining coverage counters and close the log. No more coverage results will be collected after this call. You can use this method for example to finish the coverage measurement of a Java dynamic web project. Here the ShutdownHook
approach of CodeCover might not catch the shutdown of the server or webapp
.
Method calls
If you like the method of the comments, but want to use dynamic Strings
as parameters, you can use specific method calls in your own test suite. For this approach, you need to add a special jar
file to your class path:
%CodeCoverRelease%/lib/java-protocol-dummy.jar
And you have to call one of the methods of the class:
org.codecover.instrumentation.java.measurement.Protocol
Here is an example for this approach:
try {
Person person = null;
for (int i = 0; i < MAX; i++ {
person = persons[i];
Protocol.startTestCase(person.getName());
person.prepare();
person.callTestMethods();
// throws Exception
String result = person.checkResults();
Protocol.endTestCase(person.getName(), result);
}
} catch (Exception e) {
Protocol.endTestCase(person.getName(),
"An Exception occurred " + e.getMessage()) ;
}
JUnit
CodeCover supports JUnit for the test case selection too. For this approach you don't need to instrument your JUnit TestCases
. But you have to use so called TestRunners
provided by CodeCover in the jar:
JUnit-TestRunner.jar
There are a number of TestRunners
available:
org.codecover.junit3.swing.TestRunner:
a Swing UI for JUnit 3.x
org.codecover.junit3.awt.TestRunner:
an AWT UI for JUnit 3.x
org.codecover.junit3.text.TestRunner:
a command line UI for JUnit 3.x
org.codecover.junit4.core.TestRunner:
a command line UI for JUnit 4.x
These test runners can be called by:
java -cp junit.jar:JUnit-TestRunner.jar:bin
org.codecover.junit.swing.TestRunner
[-methodsAsTestCases]
de.myprogram.AllTests
Where AllTests
is a JUnit test suite. There is an optional argument: -methodsAsTestCases
.
This tells CodeCover whether to use JUnit test cases or the methods of JUnit test cases as test cases in the understanding of the software. CodeCover will explicitly start end end test cases synchronously to JUnit. If there are own calls of start test case, using one of the methods described above, they might be ignored, because JUnit test cases have a higher priority.
Coverage result
The coverage results of a test run is stored in a so called coverage log
file. Per default its file name has the following format:
coverage-log-yyyy-MM-dd-HH-mm-ss-SSS.clf
This file is stored in the current directory. To change the target of this coverage log file, you can use one of the following methods:
- System property:
You can set a so called system property when starting your instrumented program. The name of the property is:
org.codecover.coverage-log-file.
An example call:java -Dorg.codecover.coverage-log-file=archiv/coverage1.clf -cp bin:library.jar Main
- Environment variable:
You can set a system variable with the name:
org.codecover.coverage-log-file
CoverageLogPath:
After having instrumented your source files, additionally classes have been added. One of them is:org.codecover.instrumentation.java.measurement.CoverageLogPath
You just can change the return value of the methodgetDefaultPath()
to another file name or another path. Afterwards you have to recompile this class.
If a coverage log file with the same name already exists, the name of the new coverage log
file will be extended by (1). If you want to enable overwriting, set the following system variable to true
or on
:
org.codecover.coverage-log-overwrite
Example:
java -Dorg.codecover.coverage-log-file=archiv/coverage2.clf
-Dorg.codecover.coverage-log-overwrite=true
-cp bin:library.jar
Main
Note: Only if there was at least one coverage data—e.g. one statement executed—the coverage log file is produced. Otherwise this file is not created, indicating that no coverage was made.
Special characteristics
When instrumenting your Java source files, some additional files will be added. They are in the following package:
org.codecover
You have to compile them and add them to your class path when executing your program, because these files are needed for the coverage measurement.
The coverage measurement under Java is not threadsafe. For this reason, the number of executions for a statement might not be correct, when your program uses multithreading. Nevertheless, CodeCover gets to know when a statement is covered correctly.
The measurement approach under Java uses a so called ShutdownHook
, to guarantee, that all measurement results are captured. If you use an own ShutdownHook
its effected coverage data might not be captured.
If you use an own ClassLoader
you have to ensure, that this classloader is not instrumented because the instrumentation causes problems in this specific case.