Skip to content

Feature/better aggregation task #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ A plugin to enable the use of Scoverage in a gradle Scala project.

This has now been deployed to maven central.

Using gradle-scoverage
======================

Getting started
---------------
```groovy
Expand Down Expand Up @@ -34,29 +37,47 @@ This creates an additional task testCoverage which will run tests against instru
Then launch command :
`gradle testScoverage` or `gradle checkScoverage`

Available tasks
---------------

* testScoverage - Executes all tests and creates Scoverage XML report with information about code coverage
* reportScoverage - Generates reports (see below).
* aggregateScoverage - Aggregates reports from multiple sub-projects (see below).
* checkScoverage - See below.
* compileScoverageScala - Instruments code without running tests.

ReportScoverage
---------------

You can configure output generated by `gradle reportScoverage` using flags:

| Flag name | Default value | Description |
| ------------------------|---------------|-------------------------------------------------|
| coverageOutputCobertura | true | Enables/disables cobertura.xml file generation. |
| coverageOutputXML | true | Enables/disables scoverage XML output. |
| coverageOutputHTML | true | Enables/disables scoverage HTML output. |
| coverageDebug | false | Enables/disables scoverage debug output. |

Aggregating Reports
-------------------

There is now experimental support for aggregating coverage statistics across subprojects.
There is now experimental support for aggregating coverage statistics across sub-projects.

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

You also have to declare this task:

```groovy
task aggregateScoverage(type: org.scoverage.ScoverageAggregate)
```

This will produce a report into _build_ / scoverage-aggregate
This will produce a report into `build/scoverage-aggregate` directory.

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

CheckScoverage
---------
--------------

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

Expand Down
19 changes: 16 additions & 3 deletions src/main/groovy/org/scoverage/AggregateReportApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,23 @@ public static void main(String... args) {
File rootDir = new File(args[0]);
File reportDir = new File(args[1]);
Boolean clean = Boolean.parseBoolean(args[2]);
reportDir.mkdirs();

Boolean coverageOutputCobertura = java.lang.Boolean.valueOf(args[3]);
Boolean coverageOutputXML = java.lang.Boolean.valueOf(args[4]);
Boolean coverageOutputHTML = java.lang.Boolean.valueOf(args[5]);
Boolean coverageDebug = java.lang.Boolean.valueOf(args[6]);

Coverage coverage = CoverageAggregator.aggregate(rootDir, clean).get();
new ScoverageHtmlWriter(rootDir, reportDir).write(coverage);
new CoberturaXmlWriter(rootDir, reportDir).write(coverage);

ScoverageWriter.write(
rootDir,
reportDir,
coverage,
coverageOutputCobertura,
coverageOutputXML,
coverageOutputHTML,
coverageDebug
);
}

}
14 changes: 11 additions & 3 deletions src/main/groovy/org/scoverage/ScoverageAggregate.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@ class ScoverageAggregate extends JavaExec {

@Override
void exec() {
def extension = ScoveragePlugin.extensionIn(project)
setClasspath(ScoveragePlugin.extensionIn(project).pluginClasspath)
setMain('org.scoverage.AggregateReportApp')
def reportPath = reportDirOrDefault()
setArgs([project.projectDir, reportPath.absolutePath, clean])
setArgs([
project.projectDir,
reportPath.absolutePath,
clean,
// TODO - consider separate options for `report` and `aggregate` tasks
extension.coverageOutputCobertura,
extension.coverageOutputXML,
extension.coverageOutputHTML,
extension.coverageDebug
])
super.exec()
def reportEntryPoint = new File(reportPath, 'index.html').absolutePath
project.logger.lifecycle("Wrote aggregated scoverage report to ${reportEntryPoint}")
}

def reportDirOrDefault() {
Expand Down
6 changes: 6 additions & 0 deletions src/main/groovy/org/scoverage/ScoverageExtension.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class ScoverageExtension {

FileCollection pluginClasspath

/** Options for enabling and disabling output */
boolean coverageOutputCobertura = true
boolean coverageOutputXML = true
boolean coverageOutputHTML = true
boolean coverageDebug = false

ScoverageExtension(Project project) {

project.plugins.apply(JavaPlugin.class);
Expand Down
10 changes: 9 additions & 1 deletion src/main/groovy/org/scoverage/ScoverageReport.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ class ScoverageReport extends JavaExec {
extension.reportDir.mkdirs()
setClasspath(extension.pluginClasspath)
setMain('org.scoverage.SingleReportApp')
setArgs([extension.sources.absolutePath, extension.dataDir.absolutePath, extension.reportDir.absolutePath])
setArgs([
/* sourceDir = */ extension.sources.absolutePath,
/* dataDir = */ extension.dataDir.absolutePath,
/* reportDir = */ extension.reportDir.absolutePath,
extension.coverageOutputCobertura,
extension.coverageOutputXML,
extension.coverageOutputHTML,
extension.coverageDebug
])
super.exec()
}
}
74 changes: 74 additions & 0 deletions src/main/groovy/org/scoverage/ScoverageWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.scoverage;

import scoverage.Constants;
import scoverage.Coverage;
import scoverage.report.CoberturaXmlWriter;
import scoverage.report.ScoverageHtmlWriter;
import scoverage.report.ScoverageXmlWriter;

import java.io.File;

/**
* Util for generating and saving coverage files.
* <p/>
* Copied from sbt-scoverage and converted to Java to avoid dependency to Scala.
*/
public class ScoverageWriter {

/**
* Generates all reports from given data.
*
* @param sourceDir directory with project sources
* @param reportDir directory for generate reports
* @param coverage coverage data
* @param coverageOutputCobertura switch for Cobertura output
* @param coverageOutputXML switch for Scoverage XML output
* @param coverageOutputHTML switch for Scoverage HTML output
* @param coverageDebug switch for Scoverage Debug output
*/
public static void write(File sourceDir,
File reportDir,
Coverage coverage,
Boolean coverageOutputCobertura,
Boolean coverageOutputXML,
Boolean coverageOutputHTML,
Boolean coverageDebug) {

System.out.println("[scoverage] Generating scoverage reports...");

reportDir.mkdirs();

if (coverageOutputCobertura) {
new CoberturaXmlWriter(sourceDir, reportDir).write(coverage);
System.out.println("[scoverage] Written Cobertura XML report to " +
reportDir.getAbsolutePath() +
File.separator +
"cobertura.xml");
}

if (coverageOutputXML) {
new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ false).write(coverage);
System.out.println("[scoverage] Written XML report to " +
reportDir.getAbsolutePath() +
File.separator +
Constants.XMLReportFilename());
if (coverageDebug) {
new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ true).write(coverage);
System.out.println("[scoverage] Written XML report with debug information to " +
reportDir.getAbsolutePath() +
File.separator +
Constants.XMLReportFilenameWithDebug());
}
}

if (coverageOutputHTML) {
new ScoverageHtmlWriter(sourceDir, reportDir).write(coverage);
System.out.println("[scoverage] Written HTML report to " +
reportDir.getAbsolutePath() +
File.separator +
"index.html");
}

System.out.println("[scoverage] Coverage reports completed");
}
}
41 changes: 27 additions & 14 deletions src/main/groovy/org/scoverage/SingleReportApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
import scoverage.Coverage;
import scoverage.IOUtils;
import scoverage.Serializer;
import scoverage.report.CoberturaXmlWriter;
import scoverage.report.ScoverageHtmlWriter;
import scoverage.report.ScoverageXmlWriter;

import java.io.File;
import java.util.Arrays;
Expand All @@ -21,20 +18,36 @@ public static void main(String... args) {
File sourceDir = new File(args[0]);
File dataDir = new File(args[1]);
File reportDir = new File(args[2]);
reportDir.mkdirs();

Boolean coverageOutputCobertura = java.lang.Boolean.valueOf(args[3]);
Boolean coverageOutputXML = java.lang.Boolean.valueOf(args[4]);
Boolean coverageOutputHTML = java.lang.Boolean.valueOf(args[5]);
Boolean coverageDebug = java.lang.Boolean.valueOf(args[6]);

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

Coverage coverage = Serializer.deserialize(coverageFile);
if (!coverageFile.exists()) {
System.out.println("[scoverage] Could not find coverage file, skipping...");
} else {
File[] array = IOUtils.findMeasurementFiles(dataDir);
// TODO: patch scoverage core to use a consistent collection type?
Seq<File> measurementFiles = scala.collection.JavaConversions.asScalaBuffer(Arrays.asList(array));

Coverage coverage = Serializer.deserialize(coverageFile);

Set<Object> measurements = IOUtils.invoked(measurementFiles);
coverage.apply(measurements);

ScoverageWriter.write(
sourceDir,
reportDir,
coverage,
coverageOutputCobertura,
coverageOutputXML,
coverageOutputHTML,
coverageDebug);
}
}

Set<Object> measurements = IOUtils.invoked(measurementFiles);
coverage.apply(measurements);

new ScoverageXmlWriter(sourceDir, reportDir, false).write(coverage);
new ScoverageHtmlWriter(sourceDir, reportDir).write(coverage);
new CoberturaXmlWriter(sourceDir, reportDir).write(coverage);
}
}
31 changes: 31 additions & 0 deletions src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.scoverage

import org.gradle.tooling.BuildLauncher
import org.gradle.tooling.GradleConnector
import org.hamcrest.core.Is
import org.junit.Assert

/**
* Some utils for easy acceptance testing.
*/
class AcceptanceTestUtils {


protected BuildLauncher setupBuild(File projectRoot, boolean useAnt) {
return GradleConnector.
newConnector().
forProjectDirectory(projectRoot).
connect().
newBuild().
withArguments("-PuseAnt=$useAnt")
}

protected void checkFile(String description, File file, boolean shouldExist) throws Exception {
Assert.assertThat(description + ' should be created at ' + file.absolutePath, file.exists(), Is.is(shouldExist))
}

protected File reportDir(File baseDir) {
return new File(baseDir, 'build/reports/scoverage')
}

}
42 changes: 42 additions & 0 deletions src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.scoverage

import org.junit.Test

class AggregationAcceptanceTest extends AcceptanceTestUtils {

private static File aggregateReportDir(File baseDir) {
return new File(baseDir, 'build/scoverage-aggregate')
}

private testWater(boolean useAnt) throws Exception {
File projectDir = new File('src/test/water')

def build = setupBuild(projectDir, useAnt)
build.forTasks('clean', 'reportScoverage', 'aggregateScoverage').run()

def indexHtml = new File(aggregateReportDir(projectDir), 'index.html')
checkFile('an aggregated index HTML file', indexHtml, true)

def cobertura = new File(aggregateReportDir(projectDir), 'cobertura.xml')
checkFile('an aggregated cobertura XML file', cobertura, true)

def scoverageXml = new File(aggregateReportDir(projectDir), 'scoverage.xml')
checkFile('an aggregated scoverage XML file', scoverageXml, true)

def krillsHtml = new File(aggregateReportDir(projectDir), 'krills.html')
checkFile('a HTML file for \'krills\' sub-project', krillsHtml, true)

def whalesHtml = new File(aggregateReportDir(projectDir), 'whales.html')
checkFile('a HTML file for \'whales\' sub-project', whalesHtml, true)
}

@Test
public void testMultiProjectAggregationWithAnt() throws Exception {
testWater(true)
}

@Test
public void testMultiProjectAggregationWithZinc() throws Exception {
testWater(false)
}
}
33 changes: 0 additions & 33 deletions src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy

This file was deleted.

Loading