From aa31181bcf82570e8d3749e9f3a1fd6e267f8b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerzy=20M=C3=BCller?= Date: Mon, 9 Feb 2015 11:30:35 +0100 Subject: [PATCH 1/6] Updating report task to be on par with sbt plugin In sbt-scoverage plugin, report task is much more detailed. This commit updates gradle version of report task to be on par with it's sbt counterpart. Updated version of task brings 4 new configuration parameters: - coverageOutputCobertura - coverageOutputXML - coverageOutputHTML - coverageDebug All could be used to enable/disable different outputs. All but last one defaults to true. Readme is updated with this information. Code in `ScoverageWriter.java` is re-written from sbt-scoverage and converted to Java. It could be moved to scalac-scoverage-runtime to be used by all plugins. It's not inlined in `SingleReportApp.java` because it'll be used also in ScoverageAggregate task (in next PR). If there is no test data in project report task now only issues a warning and not crash (it's important for multi-project setups without test code/code to test in some sub-projects). --- README.md | 35 ++++++--- .../org/scoverage/ScoverageExtension.groovy | 6 ++ .../org/scoverage/ScoverageReport.groovy | 10 ++- .../groovy/org/scoverage/ScoverageWriter.java | 74 +++++++++++++++++++ .../groovy/org/scoverage/SingleReportApp.java | 41 ++++++---- 5 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 src/main/groovy/org/scoverage/ScoverageWriter.java diff --git a/README.md b/README.md index 2f438c8..bba8f53 100644 --- a/README.md +++ b/README.md @@ -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 @@ -34,10 +37,31 @@ 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. @@ -48,15 +72,8 @@ task aggregateScoverage(type: org.scoverage.ScoverageAggregate) This will produce a report into _build_ / scoverage-aggregate -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. - CheckScoverage ---------- +-------------- By default, when you launch `gradle checkScoverage` build fail if only 75% of project is covered by tests. diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 240ee3c..4d4c943 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -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); diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index 58061a1..ab1f1b7 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -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() } } diff --git a/src/main/groovy/org/scoverage/ScoverageWriter.java b/src/main/groovy/org/scoverage/ScoverageWriter.java new file mode 100644 index 0000000..0d8ab2f --- /dev/null +++ b/src/main/groovy/org/scoverage/ScoverageWriter.java @@ -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. + *

+ * 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"); + } +} diff --git a/src/main/groovy/org/scoverage/SingleReportApp.java b/src/main/groovy/org/scoverage/SingleReportApp.java index bf3a4c5..998cb83 100644 --- a/src/main/groovy/org/scoverage/SingleReportApp.java +++ b/src/main/groovy/org/scoverage/SingleReportApp.java @@ -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; @@ -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 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 measurementFiles = scala.collection.JavaConversions.asScalaBuffer(Arrays.asList(array)); + + Coverage coverage = Serializer.deserialize(coverageFile); + + Set measurements = IOUtils.invoked(measurementFiles); + coverage.apply(measurements); + + ScoverageWriter.write( + sourceDir, + reportDir, + coverage, + coverageOutputCobertura, + coverageOutputXML, + coverageOutputHTML, + coverageDebug); + } + } - Set 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); - } } \ No newline at end of file From 10a38dda5d9ae1d9d10a5618c3fda9bbd7975c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerzy=20M=C3=BCller?= Date: Mon, 9 Feb 2015 15:32:05 +0100 Subject: [PATCH 2/6] Improved aggregation task Aggregate task now use settings to configure which outputs should be aggregated (HTML, XML, debug XML and cobertura). Tests for aggregation added using dummy test project ('water'). --- README.md | 6 ++- .../org/scoverage/AggregateReportApp.java | 19 +++++++-- .../org/scoverage/ScoverageAggregate.groovy | 14 +++++-- .../org/scoverage/AcceptanceTestUtils.groovy | 31 ++++++++++++++ .../AggregationAcceptanceTest.groovy | 42 +++++++++++++++++++ .../org/scoverage/PluginAcceptanceTest.groovy | 33 --------------- .../SimpleReportAcceptanceTest.groovy | 31 ++++++++++++++ .../src/main/scala/whales/BlueWhale.scala | 5 +++ .../src/test/scala/whales/BlueWhaleTest.scala | 11 +++++ src/test/water/build.gradle | 29 +++++++++++++ .../src/main/scala/krills/NorthernKrill.scala | 5 +++ .../test/scala/krills/NorthernKrillTest.scala | 11 +++++ src/test/water/settings.gradle | 1 + 13 files changed, 198 insertions(+), 40 deletions(-) create mode 100644 src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy create mode 100644 src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy delete mode 100644 src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy create mode 100644 src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy create mode 100644 src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala create mode 100644 src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala create mode 100644 src/test/water/build.gradle create mode 100644 src/test/water/krill/src/main/scala/krills/NorthernKrill.scala create mode 100644 src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala create mode 100644 src/test/water/settings.gradle diff --git a/README.md b/README.md index bba8f53..a0c31f6 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,15 @@ There is now experimental support for aggregating coverage statistics across sub 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. + +Aggregation uses same flags as reporting for enabling/disabling different output types. CheckScoverage -------------- diff --git a/src/main/groovy/org/scoverage/AggregateReportApp.java b/src/main/groovy/org/scoverage/AggregateReportApp.java index fdb28e1..914bc89 100644 --- a/src/main/groovy/org/scoverage/AggregateReportApp.java +++ b/src/main/groovy/org/scoverage/AggregateReportApp.java @@ -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 + ); } } \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy index 5d9d3a2..8703d09 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -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() { diff --git a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy new file mode 100644 index 0000000..306da17 --- /dev/null +++ b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy @@ -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') + } + +} diff --git a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy new file mode 100644 index 0000000..a0ac493 --- /dev/null +++ b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy @@ -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) + } +} diff --git a/src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy b/src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy deleted file mode 100644 index 21cade1..0000000 --- a/src/test/groovy/org/scoverage/PluginAcceptanceTest.groovy +++ /dev/null @@ -1,33 +0,0 @@ -package org.scoverage - -import org.gradle.tooling.GradleConnector -import org.junit.Test - -import static org.junit.Assert.assertThat -import static org.hamcrest.core.Is.is - -class PluginAcceptanceTest { - - static def checkHappyDay(boolean useAnt) { - def projectRoot = "src/test/happy day" - def build = GradleConnector. - newConnector(). - forProjectDirectory(new File(projectRoot)). - connect().newBuild(). - withArguments("-PuseAnt=$useAnt") - build.forTasks('clean', 'checkScoverage').run() - - def html = new File("$projectRoot/build/reports/scoverage/index.html") - assertThat('an HTML file should be created at ' + html.absolutePath, html.exists(), is(true)) - } - - @Test - public void testAntProjectWithCompleteCoverage() throws Exception { - checkHappyDay(true) - } - - @Test - public void testZincProjectWithCompleteCoverage() throws Exception { - checkHappyDay(false) - } -} diff --git a/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy new file mode 100644 index 0000000..e5a9522 --- /dev/null +++ b/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy @@ -0,0 +1,31 @@ +package org.scoverage + +import org.junit.Test + +class SimpleReportAcceptanceTest extends AcceptanceTestUtils { + + private testHappyDay(boolean useAnt) throws Exception { + File projectRoot = new File('src/test/happy day') + def build = setupBuild(projectRoot, useAnt) + + build.forTasks('clean', 'checkScoverage').run() + + def html = new File(reportDir(projectRoot), 'index.html') + checkFile('an index HTML file', html, true) + def cobertura = new File(reportDir(projectRoot), 'cobertura.xml') + checkFile('a cobertura XML file', cobertura, true) + def scoverageXml = new File(reportDir(projectRoot), 'scoverage.xml') + checkFile('a scoverage XML file', scoverageXml, true) + } + + @Test + public void testAntProjectWithCompleteCoverage() throws Exception { + testHappyDay(true) + } + + @Test + public void testZincProjectWithCompleteCoverage() throws Exception { + testHappyDay(false) + } + +} diff --git a/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala b/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala new file mode 100644 index 0000000..2953e26 --- /dev/null +++ b/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala @@ -0,0 +1,5 @@ +package whales + +class BlueWhale { + def swim(): String = "I'm swimming!" +} \ No newline at end of file diff --git a/src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala b/src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala new file mode 100644 index 0000000..2e3d0be --- /dev/null +++ b/src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala @@ -0,0 +1,11 @@ +package whales + +import org.junit.Test +import org.junit.Assert + +class BlueWhaleTest { + + @Test def bob(): Unit = { + Assert.assertEquals("Whale cannot swim :(", new BlueWhale().swim(), "I'm swimming!") + } +} \ No newline at end of file diff --git a/src/test/water/build.gradle b/src/test/water/build.gradle new file mode 100644 index 0000000..dd11037 --- /dev/null +++ b/src/test/water/build.gradle @@ -0,0 +1,29 @@ +description = 'a multi-project setup for gradle-scoverage' + +buildscript { + repositories { + // need to get up to the working directory of gradle-plugins build + flatDir dir: "${project.projectDir}/../../../build/libs" + } + dependencies { + classpath name: 'gradle-scoverage', version: '+' + } +} + +task aggregateScoverage(type: org.scoverage.ScoverageAggregate) + +allprojects { + repositories { + mavenCentral() + } + + apply plugin: 'scoverage' + + dependencies { + scoverage 'org.scoverage:scalac-scoverage-plugin_2.11:1.0.4', + 'org.scoverage:scalac-scoverage-runtime_2.11:1.0.4' + compile 'org.scala-lang:scala-library:2.11.5' + + testCompile 'junit:junit:4.11' + } +} diff --git a/src/test/water/krill/src/main/scala/krills/NorthernKrill.scala b/src/test/water/krill/src/main/scala/krills/NorthernKrill.scala new file mode 100644 index 0000000..16fdf22 --- /dev/null +++ b/src/test/water/krill/src/main/scala/krills/NorthernKrill.scala @@ -0,0 +1,5 @@ +package krills + +class NorthernKrill { + def swim(): String = "I can only float :(" +} \ No newline at end of file diff --git a/src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala b/src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala new file mode 100644 index 0000000..bcb4dd7 --- /dev/null +++ b/src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala @@ -0,0 +1,11 @@ +package krills + +import org.junit.Test +import org.junit.Assert + +class NorthernKrillTest { + + @Test def bob(): Unit = { + Assert.assertEquals("Krill can swim", new NorthernKrill().swim(), "I can only float :(") + } +} \ No newline at end of file diff --git a/src/test/water/settings.gradle b/src/test/water/settings.gradle new file mode 100644 index 0000000..a978eb9 --- /dev/null +++ b/src/test/water/settings.gradle @@ -0,0 +1 @@ +include 'bluewhale', 'krill' \ No newline at end of file From 03efcb06d7d2722a5160712da14f1e33ed2afd3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerzy=20M=C3=BCller?= Date: Mon, 9 Feb 2015 17:14:38 +0100 Subject: [PATCH 3/6] Tests for tests in seperate project Currently report is generated, but coverage is wrong, so tests fail. --- .../org/scoverage/AcceptanceTestUtils.groovy | 26 +++++++++ .../SeparateTestsAcceptanceTest.groovy | 41 ++++++++++++++ .../src/test/scala/hello/HelloTest.scala | 12 +++++ .../a/src/main/scala/hello/Hello.scala | 5 ++ src/test/separate-tests/build.gradle | 53 +++++++++++++++++++ src/test/separate-tests/settings.gradle | 1 + 6 files changed, 138 insertions(+) create mode 100644 src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy create mode 100644 src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala create mode 100644 src/test/separate-tests/a/src/main/scala/hello/Hello.scala create mode 100644 src/test/separate-tests/build.gradle create mode 100644 src/test/separate-tests/settings.gradle diff --git a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy index 306da17..8c93a76 100644 --- a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy +++ b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy @@ -5,11 +5,32 @@ import org.gradle.tooling.GradleConnector import org.hamcrest.core.Is import org.junit.Assert +enum CoverageType { + Line('cobertura.xml', 'line-rate'), + Statement('scoverage.xml', 'statement-rate'), + Branch('scoverage.xml', 'branch-rate') + + String fileName + String paramName + + CoverageType(String fileName, String paramName) { + this.fileName = fileName + this.paramName = paramName + } +} + /** * Some utils for easy acceptance testing. */ class AcceptanceTestUtils { + XmlParser parser + + AcceptanceTestUtils() { + parser = new XmlParser() + parser.setFeature('http://apache.org/xml/features/disallow-doctype-decl', false) + parser.setFeature('http://apache.org/xml/features/nonvalidating/load-external-dtd', false) + } protected BuildLauncher setupBuild(File projectRoot, boolean useAnt) { return GradleConnector. @@ -28,4 +49,9 @@ class AcceptanceTestUtils { return new File(baseDir, 'build/reports/scoverage') } + protected Double coverage(File reportDir, CoverageType coverageType) { + File reportFile = new File(reportDir, coverageType.fileName) + def xml = parser.parse(reportFile) + xml.attribute(coverageType.paramName).toDouble() + } } diff --git a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy new file mode 100644 index 0000000..79ffcf7 --- /dev/null +++ b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy @@ -0,0 +1,41 @@ +package org.scoverage + +import org.hamcrest.core.Is +import org.junit.Assert +import org.junit.Test + +class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { + + private void testSeparate(boolean useAnt) throws Exception { + File projectDir = new File('src/test/separate-tests') + File subprojectDir = new File(projectDir, 'a') + File testsSubprojectDir = new File(projectDir, 'a-tests') + + def build = setupBuild(projectDir, useAnt) + build.forTasks('clean', 'reportScoverage').run() + + // ensure report is generated in base project ... + def indexHtml = new File(reportDir(subprojectDir), 'index.html') + checkFile('an index HTML file', indexHtml, true) + + // ... but not in test project ... + def testsIndexHtml = new File(reportDir(testsSubprojectDir), 'index.html') + checkFile('an index HTML file', testsIndexHtml, false) + + // ... and both statement and branch coverage is 100% + def branchCoverage = coverage(reportDir(subprojectDir), CoverageType.Branch) + def statementCoverage = coverage(reportDir(subprojectDir), CoverageType.Statement) + Assert.assertThat('Branch coverage should be 100%, was ' + branchCoverage, branchCoverage, Is.is(100.0)) + Assert.assertThat('Statement coverage should be 100%, was ' + statementCoverage, statementCoverage, Is.is(100.0)) + } + + @Test + public void testSeparateTestsWithAnt() throws Exception { + testSeparate(true) + } + + @Test + public void testSeparateTestsWithZinc() throws Exception { + testSeparate(false) + } +} diff --git a/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala b/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala new file mode 100644 index 0000000..4e9c871 --- /dev/null +++ b/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala @@ -0,0 +1,12 @@ +package hello + +import org.junit.Test +import org.junit.Assert.assertEquals + +class HelloTest { + + @Test def testText() { + assertEquals("Hello World", new Hello().text) + } + +} diff --git a/src/test/separate-tests/a/src/main/scala/hello/Hello.scala b/src/test/separate-tests/a/src/main/scala/hello/Hello.scala new file mode 100644 index 0000000..eabfc20 --- /dev/null +++ b/src/test/separate-tests/a/src/main/scala/hello/Hello.scala @@ -0,0 +1,5 @@ +package hello + +class Hello { + def text = "Hello World" +} diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle new file mode 100644 index 0000000..1fe7706 --- /dev/null +++ b/src/test/separate-tests/build.gradle @@ -0,0 +1,53 @@ +description = 'a multi-project with separate tests setup for gradle-scoverage' + +buildscript { + repositories { + // need to get up to the working directory of gradle-plugins build + flatDir dir: "${project.projectDir}/../../../build/libs" + } + dependencies { + classpath name: 'gradle-scoverage', version: '+' + } +} + +subprojects { + + repositories { + mavenCentral() + } + + apply plugin: 'scala' + apply plugin: 'scoverage' + + dependencies { + compile 'org.scala-lang:scala-library:2.11.4' + scoverage 'org.scoverage:scalac-scoverage-plugin_2.11:1.0.4', + 'org.scoverage:scalac-scoverage-runtime_2.11:1.0.4' + + testCompile 'junit:junit:4.11' + } + + testScoverage { + onlyIf { project.name.endsWith('-tests') } + } + + reportScoverage { + onlyIf { project.name.endsWith('-tests') } + } + +} + +configure(subprojects.findAll { it.name.endsWith('-tests') }) { + def mainProject = project(":${project.name.minus('-tests')}") + dependencies { + testCompile mainProject + } + scoverage { + sources = mainProject.extensions.scoverage.sources + dataDir = mainProject.extensions.scoverage.dataDir + reportDir = mainProject.extensions.scoverage.reportDir + } + testScoverage { + classpath = mainProject.sourceSets.scoverage.runtimeClasspath + sourceSets.test.runtimeClasspath + } +} \ No newline at end of file diff --git a/src/test/separate-tests/settings.gradle b/src/test/separate-tests/settings.gradle new file mode 100644 index 0000000..85eeb66 --- /dev/null +++ b/src/test/separate-tests/settings.gradle @@ -0,0 +1 @@ +include ':a', ':a-tests' From ca7abc8f527d405e441d54cb16d7dd6e1a53e095 Mon Sep 17 00:00:00 2001 From: Stu Date: Sun, 15 Feb 2015 23:23:31 +0000 Subject: [PATCH 4/6] Fix tests in separate projects Provide an instrumented jar for dependent projects. Use closeTo to verify coverage. Let 'a-test' address the published scoverage artifacts directly. checkScoverage should also only be run on the a-test project. --- build.gradle | 1 + .../groovy/org/scoverage/ScoverageExtension.groovy | 10 ++++++++++ .../org/scoverage/SeparateTestsAcceptanceTest.groovy | 9 +++++---- src/test/separate-tests/build.gradle | 8 +++++--- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 677b2d5..39ecdba 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ dependencies { compile gradleApi() compile localGroovy() scoverage 'org.scoverage:scalac-scoverage-plugin_2.11:1.0.2' + testCompile 'org.hamcrest:hamcrest-library:1.3' } task groovydocJar(type: Jar, dependsOn: groovydoc) { diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 4d4c943..de65a3f 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -9,6 +9,7 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.scala.ScalaPlugin import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.testing.Test import org.gradle.util.GFileUtils @@ -88,6 +89,15 @@ class ScoverageExtension { runtimeClasspath = it.output + mainSourceSet.output + project.configurations.scoverage + project.configurations.testRuntime } + def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { + dependsOn('scoverageClasses') + classifier = ScoveragePlugin.CONFIGURATION_NAME + from mainSourceSet.output + } + project.artifacts { + scoverage project.tasks.jarScoverage + } + project.tasks.create(ScoveragePlugin.TEST_NAME, Test.class) { conventionMapping.map("testClassesDir", new Callable() { public Object call() throws Exception { diff --git a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy index 79ffcf7..d900809 100644 --- a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy @@ -1,9 +1,10 @@ package org.scoverage -import org.hamcrest.core.Is -import org.junit.Assert +import static org.hamcrest.number.IsCloseTo.closeTo import org.junit.Test +import static org.junit.Assert.assertThat + class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { private void testSeparate(boolean useAnt) throws Exception { @@ -25,8 +26,8 @@ class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { // ... and both statement and branch coverage is 100% def branchCoverage = coverage(reportDir(subprojectDir), CoverageType.Branch) def statementCoverage = coverage(reportDir(subprojectDir), CoverageType.Statement) - Assert.assertThat('Branch coverage should be 100%, was ' + branchCoverage, branchCoverage, Is.is(100.0)) - Assert.assertThat('Statement coverage should be 100%, was ' + statementCoverage, statementCoverage, Is.is(100.0)) + assertThat('Branch coverage should be 100%, was ' + branchCoverage, branchCoverage, closeTo(100.0, 1.0)) + assertThat('Statement coverage should be 100%, was ' + statementCoverage, statementCoverage, closeTo(100.0, 1.0)) } @Test diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle index 1fe7706..bff2e31 100644 --- a/src/test/separate-tests/build.gradle +++ b/src/test/separate-tests/build.gradle @@ -35,19 +35,21 @@ subprojects { onlyIf { project.name.endsWith('-tests') } } + checkScoverage { + onlyIf { project.name.endsWith('-tests') } + } + } configure(subprojects.findAll { it.name.endsWith('-tests') }) { def mainProject = project(":${project.name.minus('-tests')}") dependencies { testCompile mainProject + scoverage mainProject.configurations.scoverage.artifacts.files } scoverage { sources = mainProject.extensions.scoverage.sources dataDir = mainProject.extensions.scoverage.dataDir reportDir = mainProject.extensions.scoverage.reportDir } - testScoverage { - classpath = mainProject.sourceSets.scoverage.runtimeClasspath + sourceSets.test.runtimeClasspath - } } \ No newline at end of file From 0eeeb59b6169f4569fa86f2a2b253955ab59e1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jerzy=20M=C3=BCller?= Date: Tue, 17 Feb 2015 16:42:56 +0100 Subject: [PATCH 5/6] Adding class to main in 'a-tests' breaks reports --- .../org/scoverage/SeparateTestsAcceptanceTest.groovy | 4 ++++ .../a-tests/src/main/scala/hello/BaseTest.scala | 7 +++++++ .../a-tests/src/test/scala/hello/HelloTest.scala | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala diff --git a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy index d900809..f9b7158 100644 --- a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy @@ -23,6 +23,10 @@ class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { def testsIndexHtml = new File(reportDir(testsSubprojectDir), 'index.html') checkFile('an index HTML file', testsIndexHtml, false) + // ... and 'Hello.scala' is present there ... + def helloHtml = new File(reportDir(subprojectDir), 'src/main/scala/hello/Hello.scala.html') + checkFile('Hello.scala html file', helloHtml, true) + // ... and both statement and branch coverage is 100% def branchCoverage = coverage(reportDir(subprojectDir), CoverageType.Branch) def statementCoverage = coverage(reportDir(subprojectDir), CoverageType.Statement) diff --git a/src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala b/src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala new file mode 100644 index 0000000..12d82ae --- /dev/null +++ b/src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala @@ -0,0 +1,7 @@ +package hello + +class BaseTest { + def beforeTest() = { + println("Running test!") + } +} \ No newline at end of file diff --git a/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala b/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala index 4e9c871..1c7df23 100644 --- a/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala +++ b/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala @@ -3,9 +3,10 @@ package hello import org.junit.Test import org.junit.Assert.assertEquals -class HelloTest { +class HelloTest extends BaseTest { @Test def testText() { + beforeTest() assertEquals("Hello World", new Hello().text) } From 9cdf7d6fceea3ed2572dcabb9f278f2472e16c24 Mon Sep 17 00:00:00 2001 From: Stu Date: Thu, 5 Mar 2015 23:32:58 +0000 Subject: [PATCH 6/6] fix separate-tests project: add dependency on a-tests main --- src/test/separate-tests/build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle index bff2e31..76ce060 100644 --- a/src/test/separate-tests/build.gradle +++ b/src/test/separate-tests/build.gradle @@ -52,4 +52,13 @@ configure(subprojects.findAll { it.name.endsWith('-tests') }) { dataDir = mainProject.extensions.scoverage.dataDir reportDir = mainProject.extensions.scoverage.reportDir } + sourceSets { + testScoverage { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + } + } + compileScoverageScala { + onlyIf { false } + } } \ No newline at end of file