<< Defining Java modules | ^ Site index ^ | ^ Tutorial index ^ | Static code analysis report using PMD >>

This chapter continues from where Defining Java modules left us.

Test coverage report using jacoco

In addition to compiling, running automated tests is one of the main tasks of build scripts.

But since we want to keep things declarative instead of imperative, we don't want to tell iwant to compile or run tests. Instead we wish for paths to targets that include the results of compiling and running tests.

The result of a compilation as a noun is easy, and many build solutions manage to keep the vocabulary declarative there. But how to avoid being imperative with test runs?

The result of a test run is basically a boolean: the tests either all passed or there was a failure. But we can do better than that: in addition to the success of the tests, we are also interested in the code coverage they give us.

So, with iwant, "running tests" means wishing for a coverage report.

Let's see how this is done. First we'll enable the needed plugin, jacoco, and refresh eclipse settings. (There is also a plugin for emma, but since that is a dead project, jacoco is recommended instead.)

~/iwant-tutorial $ $EDITOR "as-iwant-tutorial-developer/i-have/wsdefdef/src/main/java/com/example/wsdefdef/IwantTutorialWorkspaceProvider.java"
package com.example.wsdefdef;
import net.sf.iwant.api.javamodules.JavaSrcModule;
import net.sf.iwant.api.wsdef.IwantWorkspaceProvider;
import net.sf.iwant.api.wsdef.WorkspaceDefinitionContext;
public class IwantTutorialWorkspaceProvider implements IwantWorkspaceProvider {
@Override
public JavaSrcModule workspaceModule(WorkspaceDefinitionContext ctx) {
return JavaSrcModule
.with()
.name("iwant-tutorial-wsdef")
.locationUnderWsRoot("as-iwant-tutorial-developer/i-have/wsdef")
.mainJava("src/main/java").mainDeps(ctx.iwantApiModules())
.mainDeps(ctx.wsdefdefModule())
.mainDeps(ctx.iwantPlugin().jacoco().withDependencies())
.mainDeps(ctx.iwantPlugin().javamodules().withDependencies())
.end();
}
@Override
public String workspaceClassname() {
return "com.example.wsdef.IwanttutorialWorkspace";
}
}
~/iwant-tutorial $ as-iwant-tutorial-developer/with/bash/iwant/side-effect/eclipse-settings/effective
(0/1 S~ net.sf.iwant.api.javamodules.JavaClasses iwant-tutorial-wsdefdef-main-classes)
(0/1 D! net.sf.iwant.core.download.SvnExported iwant-plugin-jacoco-main-java)
svn exporting (may take a while) https://svn.code.sf.net/p/iwant/code/trunk/optional/iwant-plugin-jacoco/src/main/java@796
(0/1 D! net.sf.iwant.core.download.Downloaded commons-io-1.3.2.jar)
(0/1 D! net.sf.iwant.api.javamodules.JavaClasses iwant-plugin-jacoco)
(0/1 D~ net.sf.iwant.api.javamodules.JavaClasses iwant-tutorial-wsdef-main-classes)
(0/1 D~ net.sf.iwant.api.core.Concatenated eclipse-settings.bin-refs)
(example-hello)
( .project)
( .classpath)
( .settings/org.eclipse.jdt.core.prefs)
( .settings/org.eclipse.jdt.ui.prefs)
(example-helloutil)
( .project)
( .classpath)
( .settings/org.eclipse.jdt.core.prefs)
( .settings/org.eclipse.jdt.ui.prefs)
(as-iwant-tutorial-developer/i-have/wsdef)
( .project)
( .classpath)
( .settings/org.eclipse.jdt.core.prefs)
( .settings/org.eclipse.jdt.ui.prefs)
(as-iwant-tutorial-developer/i-have/wsdefdef)
( .project)
( .classpath)
( .settings/org.eclipse.jdt.core.prefs)
( .settings/org.eclipse.jdt.ui.prefs)

Then we define the jacoco-report target.

~/iwant-tutorial $ $EDITOR "as-iwant-tutorial-developer/i-have/wsdef/src/main/java/com/example/wsdef/IwanttutorialWorkspace.java"
package com.example.wsdef;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import net.sf.iwant.api.core.Concatenated;
import net.sf.iwant.api.core.Concatenated.ConcatenatedBuilder;
import net.sf.iwant.api.core.HelloTarget;
import net.sf.iwant.api.javamodules.JavaBinModule;
import net.sf.iwant.api.javamodules.JavaSrcModule;
import net.sf.iwant.api.javamodules.JavaSrcModule.IwantSrcModuleSpex;
import net.sf.iwant.api.model.Path;
import net.sf.iwant.api.model.SideEffect;
import net.sf.iwant.api.model.Target;
import net.sf.iwant.api.wsdef.IwantWorkspace;
import net.sf.iwant.api.wsdef.SideEffectDefinitionContext;
import net.sf.iwant.core.download.TestedIwantDependencies;
import net.sf.iwant.eclipsesettings.EclipseSettings;
import net.sf.iwant.plugin.jacoco.JacocoDistribution;
import net.sf.iwant.plugin.jacoco.JacocoTargetsOfJavaModules;
import net.sf.iwant.plugin.javamodules.JavaModules;
public class IwanttutorialWorkspace implements IwantWorkspace {
class ExampleModules extends JavaModules {
@Override
protected IwantSrcModuleSpex commonSettings(IwantSrcModuleSpex m) {
return super.commonSettings(m).testDeps(junit);
}
final JavaBinModule asmAll = binModule("org/ow2/asm", "asm-all",
"5.0.1");
final JavaBinModule hamcrestCore = binModule("org/hamcrest",
"hamcrest-core", "1.3");
final JavaBinModule junit = binModule("junit", "junit", "4.11",
hamcrestCore);
final JavaSrcModule helloUtil = srcModule("example-helloutil")
.noMainResources().end();
final JavaSrcModule hello = srcModule("example-hello")
.noMainResources().mainDeps(helloUtil).end();
}
private final ExampleModules modules = new ExampleModules();
@Override
public List<? extends Target> targets() {
return Arrays.asList(new HelloTarget("hello", "hello from iwant\n"),
classpathStringOfAll());
jacocoReport(), classpathStringOfAll());
}
private Target jacocoReport() {
return JacocoTargetsOfJavaModules
.with()
.jacocoWithDeps(jacoco(), modules.asmAll.mainArtifact())
.antJars(TestedIwantDependencies.antJar(),
TestedIwantDependencies.antLauncherJar())
.modules(modules.allSrcModules()).end()
.jacocoReport("jacoco-report");
}
private static JacocoDistribution jacoco() {
return JacocoDistribution.newestTestedVersion();
}
private Target classpathStringOfAll() {
ConcatenatedBuilder cp = Concatenated.named("all-as-cp");
cp.string(".");
for (Path jar : JavaModules.mainArtifactJarsOf(modules.allSrcModules())) {
cp.string(File.pathSeparator).nativePathTo(jar);
}
return cp.end();
}
@Override
public List<? extends SideEffect> sideEffects(
SideEffectDefinitionContext ctx) {
return Arrays.asList(EclipseSettings.with().name("eclipse-settings")
.modules(ctx.wsdefdefJavaModule(), ctx.wsdefJavaModule())
.modules(modules.allSrcModules()).end());
}
}

We list targets to refresh the wish scripts and then wish for the coverage report.

~/iwant-tutorial $ as-iwant-tutorial-developer/with/bash/iwant/list-of/targets
(0/1 S~ net.sf.iwant.api.javamodules.JavaClasses iwant-tutorial-wsdef-main-classes)
hello
jacoco-report
all-as-cp
~/iwant-tutorial $ as-iwant-tutorial-developer/with/bash/iwant/target/jacoco-report/as-path
(0/1 D! net.sf.iwant.core.download.Downloaded jacoco-0.7.2.201409121644.zip)
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoDistribution jacoco-0.7.2.201409121644)
Expanding: /home/hacker/.net.sf.iwant/cached/UnmodifiableUrl/http%3A/%2Frepo1.maven.org/maven2/org/jacoco/jacoco/0.7.2.201409121644/jacoco-0.7.2.201409121644.zip into /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/jacoco-0.7.2.201409121644
(0/1 D! net.sf.iwant.core.download.Downloaded asm-all-5.0.1.jar)
(0/1 D~ net.sf.iwant.api.javamodules.JavaClasses example-hello-test-classes)
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoInstrumentation example-hello-main-classes.jacoco-instr)
Buildfile: /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/temp/w-0/example-hello-main-classes.jacoco-instr.xml

example-hello-main-classes.jacoco-instr:
[jacoco:instrument] Instrumented 1 classes to /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/example-hello-main-classes.jacoco-instr

BUILD SUCCESSFUL
Total time: 2 seconds
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoInstrumentation example-helloutil-main-classes.jacoco-instr)
Buildfile: /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/temp/w-0/example-helloutil-main-classes.jacoco-instr.xml

example-helloutil-main-classes.jacoco-instr:
[jacoco:instrument] Instrumented 1 classes to /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/example-helloutil-main-classes.jacoco-instr

BUILD SUCCESSFUL
Total time: 1 second
(0/1 D! net.sf.iwant.api.core.ClassNameList example-hello-test-class-names)
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoCoverage example-hello.jacococoverage)
Buildfile: /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/temp/w-0/example-hello.jacococoverage.xml

example-hello.jacococoverage:
[java] JUnit version 4.11
[java] .
[java] Time: 0,586
[java]
[java] OK (1 test)
[java]

BUILD SUCCESSFUL
Total time: 2 seconds
(0/1 D! net.sf.iwant.api.javamodules.JavaClasses example-helloutil-test-classes)
(0/1 D! net.sf.iwant.api.core.ClassNameList example-helloutil-test-class-names)
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoCoverage example-helloutil.jacococoverage)
Buildfile: /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/temp/w-0/example-helloutil.jacococoverage.xml

example-helloutil.jacococoverage:
[java] JUnit version 4.11
[java] .
[java] Time: 0,718
[java]
[java] OK (1 test)
[java]

BUILD SUCCESSFUL
Total time: 2 seconds
(0/1 D! net.sf.iwant.plugin.jacoco.JacocoReport jacoco-report)
Buildfile: /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/temp/w-0/jacoco-report.xml

jacoco-report:
[echo] Generating /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/jacoco-report
[jacoco:report] Loading execution data file /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/example-hello.jacococoverage
[jacoco:report] Loading execution data file /home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/example-helloutil.jacococoverage
[jacoco:report] Writing bundle 'jacoco-report' with 2 classes

BUILD SUCCESSFUL
Total time: 2 seconds
/home/hacker/iwant-tutorial/as-iwant-tutorial-developer/.i-cached/target/jacoco-report

Let's see what is in the report. Here we already benefit from our declarative wish: since we didn't tell iwant to run anything and we haven't touched anything, we can just make the same wish again without having to wait for another test run.

~/iwant-tutorial $ as-iwant-tutorial-developer/with/bash/iwant/target/jacoco-report/as-path | xargs ls
com.example.hello
com.example.helloutil
index.html
report.csv
report.xml

You are probably more interested in the html version, but here we'll take a look at the csv file.

~/iwant-tutorial $ cat as-iwant-tutorial-developer/.i-cached/target/jacoco-report/report.csv
GROUP,PACKAGE,CLASS,INSTRUCTION_MISSED,INSTRUCTION_COVERED,BRANCH_MISSED,BRANCH_COVERED,LINE_MISSED,LINE_COVERED,COMPLEXITY_MISSED,COMPLEXITY_COVERED,METHOD_MISSED,METHOD_COVERED
jacoco-report,com.example.helloutil,HelloUtil,3,11,0,0,1,1,1,1,1,1
jacoco-report,com.example.hello,HelloMain,10,4,0,0,3,1,2,1,2,1
Output asserted
<< Defining Java modules | ^ Site index ^ | ^ Tutorial index ^ | Static code analysis report using PMD >>