Skip to content

Commit 7212400

Browse files
committed
Merge pull request #42 from Kwestor/feature/better-aggregation-task
Feature/better aggregation task
2 parents bc60746 + 10a38dd commit 7212400

17 files changed

+339
-63
lines changed

Diff for: README.md

+30-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ A plugin to enable the use of Scoverage in a gradle Scala project.
66

77
This has now been deployed to maven central.
88

9+
Using gradle-scoverage
10+
======================
11+
912
Getting started
1013
---------------
1114
```groovy
@@ -34,29 +37,47 @@ This creates an additional task testCoverage which will run tests against instru
3437
Then launch command :
3538
`gradle testScoverage` or `gradle checkScoverage`
3639

40+
Available tasks
41+
---------------
42+
43+
* testScoverage - Executes all tests and creates Scoverage XML report with information about code coverage
44+
* reportScoverage - Generates reports (see below).
45+
* aggregateScoverage - Aggregates reports from multiple sub-projects (see below).
46+
* checkScoverage - See below.
47+
* compileScoverageScala - Instruments code without running tests.
48+
49+
ReportScoverage
50+
---------------
51+
52+
You can configure output generated by `gradle reportScoverage` using flags:
53+
54+
| Flag name | Default value | Description |
55+
| ------------------------|---------------|-------------------------------------------------|
56+
| coverageOutputCobertura | true | Enables/disables cobertura.xml file generation. |
57+
| coverageOutputXML | true | Enables/disables scoverage XML output. |
58+
| coverageOutputHTML | true | Enables/disables scoverage HTML output. |
59+
| coverageDebug | false | Enables/disables scoverage debug output. |
60+
3761
Aggregating Reports
3862
-------------------
3963

40-
There is now experimental support for aggregating coverage statistics across subprojects.
64+
There is now experimental support for aggregating coverage statistics across sub-projects.
4165

4266
The project hosting the aggregation task **must** be configured as the sub-projects are;
4367
i.e. with the scoverage plugin applied and the scoverage dependencies configured.
4468

69+
You also have to declare this task:
70+
4571
```groovy
4672
task aggregateScoverage(type: org.scoverage.ScoverageAggregate)
4773
```
4874

49-
This will produce a report into _build_ / scoverage-aggregate
75+
This will produce a report into `build/scoverage-aggregate` directory.
5076

51-
Available tasks
52-
---------
53-
* testScoverage - Executes all tests and creates Scoverage XML report with information about code coverage
54-
* reportScoverage - Generates HTML report.
55-
* checkScoverage - See below.
56-
* compileScoverageScala - Instruments code without running tests.
77+
Aggregation uses same flags as reporting for enabling/disabling different output types.
5778

5879
CheckScoverage
59-
---------
80+
--------------
6081

6182
By default, when you launch `gradle checkScoverage` build fail if only 75% of project is covered by tests.
6283

Diff for: src/main/groovy/org/scoverage/AggregateReportApp.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,23 @@ public static void main(String... args) {
1313
File rootDir = new File(args[0]);
1414
File reportDir = new File(args[1]);
1515
Boolean clean = Boolean.parseBoolean(args[2]);
16-
reportDir.mkdirs();
16+
17+
Boolean coverageOutputCobertura = java.lang.Boolean.valueOf(args[3]);
18+
Boolean coverageOutputXML = java.lang.Boolean.valueOf(args[4]);
19+
Boolean coverageOutputHTML = java.lang.Boolean.valueOf(args[5]);
20+
Boolean coverageDebug = java.lang.Boolean.valueOf(args[6]);
21+
1722
Coverage coverage = CoverageAggregator.aggregate(rootDir, clean).get();
18-
new ScoverageHtmlWriter(rootDir, reportDir).write(coverage);
19-
new CoberturaXmlWriter(rootDir, reportDir).write(coverage);
23+
24+
ScoverageWriter.write(
25+
rootDir,
26+
reportDir,
27+
coverage,
28+
coverageOutputCobertura,
29+
coverageOutputXML,
30+
coverageOutputHTML,
31+
coverageDebug
32+
);
2033
}
2134

2235
}

Diff for: src/main/groovy/org/scoverage/ScoverageAggregate.groovy

+11-3
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,21 @@ class ScoverageAggregate extends JavaExec {
99

1010
@Override
1111
void exec() {
12+
def extension = ScoveragePlugin.extensionIn(project)
1213
setClasspath(ScoveragePlugin.extensionIn(project).pluginClasspath)
1314
setMain('org.scoverage.AggregateReportApp')
1415
def reportPath = reportDirOrDefault()
15-
setArgs([project.projectDir, reportPath.absolutePath, clean])
16+
setArgs([
17+
project.projectDir,
18+
reportPath.absolutePath,
19+
clean,
20+
// TODO - consider separate options for `report` and `aggregate` tasks
21+
extension.coverageOutputCobertura,
22+
extension.coverageOutputXML,
23+
extension.coverageOutputHTML,
24+
extension.coverageDebug
25+
])
1626
super.exec()
17-
def reportEntryPoint = new File(reportPath, 'index.html').absolutePath
18-
project.logger.lifecycle("Wrote aggregated scoverage report to ${reportEntryPoint}")
1927
}
2028

2129
def reportDirOrDefault() {

Diff for: src/main/groovy/org/scoverage/ScoverageExtension.groovy

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ class ScoverageExtension {
4848

4949
FileCollection pluginClasspath
5050

51+
/** Options for enabling and disabling output */
52+
boolean coverageOutputCobertura = true
53+
boolean coverageOutputXML = true
54+
boolean coverageOutputHTML = true
55+
boolean coverageDebug = false
56+
5157
ScoverageExtension(Project project) {
5258

5359
project.plugins.apply(JavaPlugin.class);

Diff for: src/main/groovy/org/scoverage/ScoverageReport.groovy

+9-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@ class ScoverageReport extends JavaExec {
1010
extension.reportDir.mkdirs()
1111
setClasspath(extension.pluginClasspath)
1212
setMain('org.scoverage.SingleReportApp')
13-
setArgs([extension.sources.absolutePath, extension.dataDir.absolutePath, extension.reportDir.absolutePath])
13+
setArgs([
14+
/* sourceDir = */ extension.sources.absolutePath,
15+
/* dataDir = */ extension.dataDir.absolutePath,
16+
/* reportDir = */ extension.reportDir.absolutePath,
17+
extension.coverageOutputCobertura,
18+
extension.coverageOutputXML,
19+
extension.coverageOutputHTML,
20+
extension.coverageDebug
21+
])
1422
super.exec()
1523
}
1624
}

Diff for: src/main/groovy/org/scoverage/ScoverageWriter.java

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.scoverage;
2+
3+
import scoverage.Constants;
4+
import scoverage.Coverage;
5+
import scoverage.report.CoberturaXmlWriter;
6+
import scoverage.report.ScoverageHtmlWriter;
7+
import scoverage.report.ScoverageXmlWriter;
8+
9+
import java.io.File;
10+
11+
/**
12+
* Util for generating and saving coverage files.
13+
* <p/>
14+
* Copied from sbt-scoverage and converted to Java to avoid dependency to Scala.
15+
*/
16+
public class ScoverageWriter {
17+
18+
/**
19+
* Generates all reports from given data.
20+
*
21+
* @param sourceDir directory with project sources
22+
* @param reportDir directory for generate reports
23+
* @param coverage coverage data
24+
* @param coverageOutputCobertura switch for Cobertura output
25+
* @param coverageOutputXML switch for Scoverage XML output
26+
* @param coverageOutputHTML switch for Scoverage HTML output
27+
* @param coverageDebug switch for Scoverage Debug output
28+
*/
29+
public static void write(File sourceDir,
30+
File reportDir,
31+
Coverage coverage,
32+
Boolean coverageOutputCobertura,
33+
Boolean coverageOutputXML,
34+
Boolean coverageOutputHTML,
35+
Boolean coverageDebug) {
36+
37+
System.out.println("[scoverage] Generating scoverage reports...");
38+
39+
reportDir.mkdirs();
40+
41+
if (coverageOutputCobertura) {
42+
new CoberturaXmlWriter(sourceDir, reportDir).write(coverage);
43+
System.out.println("[scoverage] Written Cobertura XML report to " +
44+
reportDir.getAbsolutePath() +
45+
File.separator +
46+
"cobertura.xml");
47+
}
48+
49+
if (coverageOutputXML) {
50+
new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ false).write(coverage);
51+
System.out.println("[scoverage] Written XML report to " +
52+
reportDir.getAbsolutePath() +
53+
File.separator +
54+
Constants.XMLReportFilename());
55+
if (coverageDebug) {
56+
new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ true).write(coverage);
57+
System.out.println("[scoverage] Written XML report with debug information to " +
58+
reportDir.getAbsolutePath() +
59+
File.separator +
60+
Constants.XMLReportFilenameWithDebug());
61+
}
62+
}
63+
64+
if (coverageOutputHTML) {
65+
new ScoverageHtmlWriter(sourceDir, reportDir).write(coverage);
66+
System.out.println("[scoverage] Written HTML report to " +
67+
reportDir.getAbsolutePath() +
68+
File.separator +
69+
"index.html");
70+
}
71+
72+
System.out.println("[scoverage] Coverage reports completed");
73+
}
74+
}

Diff for: src/main/groovy/org/scoverage/SingleReportApp.java

+27-14
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
import scoverage.Coverage;
66
import scoverage.IOUtils;
77
import scoverage.Serializer;
8-
import scoverage.report.CoberturaXmlWriter;
9-
import scoverage.report.ScoverageHtmlWriter;
10-
import scoverage.report.ScoverageXmlWriter;
118

129
import java.io.File;
1310
import java.util.Arrays;
@@ -21,20 +18,36 @@ public static void main(String... args) {
2118
File sourceDir = new File(args[0]);
2219
File dataDir = new File(args[1]);
2320
File reportDir = new File(args[2]);
24-
reportDir.mkdirs();
21+
22+
Boolean coverageOutputCobertura = java.lang.Boolean.valueOf(args[3]);
23+
Boolean coverageOutputXML = java.lang.Boolean.valueOf(args[4]);
24+
Boolean coverageOutputHTML = java.lang.Boolean.valueOf(args[5]);
25+
Boolean coverageDebug = java.lang.Boolean.valueOf(args[6]);
2526

2627
File coverageFile = Serializer.coverageFile(dataDir);
27-
File[] array = IOUtils.findMeasurementFiles(dataDir);
28-
// TODO: patch scoverage core to use a consistent collection type?
29-
Seq<File> measurementFiles = scala.collection.JavaConversions.asScalaBuffer(Arrays.asList(array));
3028

31-
Coverage coverage = Serializer.deserialize(coverageFile);
29+
if (!coverageFile.exists()) {
30+
System.out.println("[scoverage] Could not find coverage file, skipping...");
31+
} else {
32+
File[] array = IOUtils.findMeasurementFiles(dataDir);
33+
// TODO: patch scoverage core to use a consistent collection type?
34+
Seq<File> measurementFiles = scala.collection.JavaConversions.asScalaBuffer(Arrays.asList(array));
35+
36+
Coverage coverage = Serializer.deserialize(coverageFile);
37+
38+
Set<Object> measurements = IOUtils.invoked(measurementFiles);
39+
coverage.apply(measurements);
40+
41+
ScoverageWriter.write(
42+
sourceDir,
43+
reportDir,
44+
coverage,
45+
coverageOutputCobertura,
46+
coverageOutputXML,
47+
coverageOutputHTML,
48+
coverageDebug);
49+
}
50+
}
3251

33-
Set<Object> measurements = IOUtils.invoked(measurementFiles);
34-
coverage.apply(measurements);
3552

36-
new ScoverageXmlWriter(sourceDir, reportDir, false).write(coverage);
37-
new ScoverageHtmlWriter(sourceDir, reportDir).write(coverage);
38-
new CoberturaXmlWriter(sourceDir, reportDir).write(coverage);
39-
}
4053
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.scoverage
2+
3+
import org.gradle.tooling.BuildLauncher
4+
import org.gradle.tooling.GradleConnector
5+
import org.hamcrest.core.Is
6+
import org.junit.Assert
7+
8+
/**
9+
* Some utils for easy acceptance testing.
10+
*/
11+
class AcceptanceTestUtils {
12+
13+
14+
protected BuildLauncher setupBuild(File projectRoot, boolean useAnt) {
15+
return GradleConnector.
16+
newConnector().
17+
forProjectDirectory(projectRoot).
18+
connect().
19+
newBuild().
20+
withArguments("-PuseAnt=$useAnt")
21+
}
22+
23+
protected void checkFile(String description, File file, boolean shouldExist) throws Exception {
24+
Assert.assertThat(description + ' should be created at ' + file.absolutePath, file.exists(), Is.is(shouldExist))
25+
}
26+
27+
protected File reportDir(File baseDir) {
28+
return new File(baseDir, 'build/reports/scoverage')
29+
}
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.scoverage
2+
3+
import org.junit.Test
4+
5+
class AggregationAcceptanceTest extends AcceptanceTestUtils {
6+
7+
private static File aggregateReportDir(File baseDir) {
8+
return new File(baseDir, 'build/scoverage-aggregate')
9+
}
10+
11+
private testWater(boolean useAnt) throws Exception {
12+
File projectDir = new File('src/test/water')
13+
14+
def build = setupBuild(projectDir, useAnt)
15+
build.forTasks('clean', 'reportScoverage', 'aggregateScoverage').run()
16+
17+
def indexHtml = new File(aggregateReportDir(projectDir), 'index.html')
18+
checkFile('an aggregated index HTML file', indexHtml, true)
19+
20+
def cobertura = new File(aggregateReportDir(projectDir), 'cobertura.xml')
21+
checkFile('an aggregated cobertura XML file', cobertura, true)
22+
23+
def scoverageXml = new File(aggregateReportDir(projectDir), 'scoverage.xml')
24+
checkFile('an aggregated scoverage XML file', scoverageXml, true)
25+
26+
def krillsHtml = new File(aggregateReportDir(projectDir), 'krills.html')
27+
checkFile('a HTML file for \'krills\' sub-project', krillsHtml, true)
28+
29+
def whalesHtml = new File(aggregateReportDir(projectDir), 'whales.html')
30+
checkFile('a HTML file for \'whales\' sub-project', whalesHtml, true)
31+
}
32+
33+
@Test
34+
public void testMultiProjectAggregationWithAnt() throws Exception {
35+
testWater(true)
36+
}
37+
38+
@Test
39+
public void testMultiProjectAggregationWithZinc() throws Exception {
40+
testWater(false)
41+
}
42+
}

Diff for: src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy

-33
This file was deleted.

0 commit comments

Comments
 (0)