From 9c2c0961cad67b6cec1a18b346c15248719138ca Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Tue, 29 Jan 2019 10:34:50 +0200 Subject: [PATCH 01/17] Update the project according to the current (gradle 5.0) plugin development configurations --- .gitignore | 1 + build.gradle | 103 ++++++++++-------- init-scoverage.gradle | 7 -- .../org/scoverage/AcceptanceTestUtils.groovy | 22 ++-- .../AggregationAcceptanceTest.groovy | 3 +- .../SeparateTestsAcceptanceTest.groovy | 3 +- .../SimpleReportAcceptanceTest.groovy | 6 +- src/test/happy day/build.gradle | 19 +--- src/test/runtime/build.gradle | 19 +--- src/test/separate-tests/build.gradle | 22 ++-- src/test/water/build.gradle | 19 ++-- 11 files changed, 103 insertions(+), 121 deletions(-) delete mode 100644 init-scoverage.gradle diff --git a/.gitignore b/.gitignore index 39e1fda..143f20e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ classes build +out .gradle *.iml diff --git a/build.gradle b/build.gradle index 3a87c1e..cf31903 100644 --- a/build.gradle +++ b/build.gradle @@ -1,25 +1,17 @@ -// First, apply the publishing plugin -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "com.gradle.publish:plugin-publish-plugin:0.9.10" - } -} - -apply plugin: "com.gradle.plugin-publish" -description = 'gradle-scoverage is a Gradle plugin for calculating code coverage using Scoverage' -if (project.version == 'unspecified') { - version = '2.0.0-SNAPSHOT' +plugins { + id 'java-gradle-plugin' + id "com.gradle.plugin-publish" version "0.10.0" + id "org.jetbrains.gradle.plugin.idea-ext" version "0.4.2" } repositories { - mavenCentral() + jcenter() } +group 'org.scoverage' +description = 'gradle-scoverage is a Gradle plugin for calculating code coverage using Scoverage' +version = '2.0.0-SNAPSHOT' + ext { website = 'http://scoverage.org' vcsUrl = 'https://github.com/scoverage/gradle-scoverage.git' @@ -28,24 +20,37 @@ ext { sonatypePass = System.env.SONATYPE_PASS } -apply plugin: 'idea' +gradlePlugin { + plugins { + gradleScoverage { + id = 'org.scoverage' + implementationClass = 'org.scoverage.ScoveragePlugin' + } + } +} + +pluginBundle { + website = project.website + vcsUrl = ext.vcsUrl + description = project.description + tags = ['coverage', 'scala', 'scoverage'] + plugins { + scoveragePlugin { + displayName = 'Gradle Scoverage plugin' + } + } +} + apply plugin: 'maven' apply plugin: 'groovy' -group 'org.scoverage' sourceCompatibility = '1.6' targetCompatibility = '1.6' -configurations { - scoverage - compile.extendsFrom scoverage -} - dependencies { - compile gradleApi() - compile localGroovy() - scoverage "org.scoverage:scalac-scoverage-plugin_2.11:1.1.1" - testCompile 'junit:junit:4.12', 'org.hamcrest:hamcrest-library:1.3' + compile "org.scoverage:scalac-scoverage-plugin_2.12:1.3.1" + testCompile 'junit:junit:4.12' + testCompile 'org.hamcrest:hamcrest-library:1.3' } task groovydocJar(type: Jar, dependsOn: groovydoc) { @@ -58,10 +63,6 @@ task sourcesJar(type: Jar) { classifier = 'sources' } -test { - dependsOn jar -} - artifacts { archives jar archives groovydocJar @@ -75,25 +76,11 @@ if (project.properties.containsKey('signing.keyId')) { } } -pluginBundle { - website = project.website - vcsUrl = project.vcsUrl - description = project.description - tags = ['coverage', 'scala', 'scoverage'] - withDependencies { it.clear() } - plugins { - scoveragePlugin { - id = 'org.scoverage' - displayName = 'Gradle Scoverage plugin' - } - } -} - uploadArchives { repositories { mavenDeployer { if (project.properties.containsKey('signing.keyId')) { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } } snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots') { @@ -132,8 +119,30 @@ uploadArchives { developer { id 'D-Roch' } + developer { + id 'eyalroth' + } } } } } } + +// see https://stackoverflow.com/questions/44679007 +task fixIdeaPluginClasspath { + doFirst { + configure(tasks.pluginUnderTestMetadata) { + def ideaClassesPath = project.buildDir.toPath().resolveSibling("out").resolve("production") + def newClasspath = pluginClasspath as List + newClasspath.add(0, ideaClassesPath) + pluginClasspath.setFrom(newClasspath) + } + } +} +pluginUnderTestMetadata.mustRunAfter(fixIdeaPluginClasspath) + +idea.project.settings { + taskTriggers { + beforeBuild fixIdeaPluginClasspath, pluginUnderTestMetadata + } +} \ No newline at end of file diff --git a/init-scoverage.gradle b/init-scoverage.gradle deleted file mode 100644 index 0d7837a..0000000 --- a/init-scoverage.gradle +++ /dev/null @@ -1,7 +0,0 @@ -gradle.beforeProject { p -> - p.ext { - scoverageVersion = '1.1.1' - scoverageLib = ["org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}"] - } -} diff --git a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy index 803da60..00498c5 100644 --- a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy +++ b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy @@ -1,7 +1,6 @@ package org.scoverage -import org.gradle.tooling.BuildLauncher -import org.gradle.tooling.GradleConnector +import org.gradle.testkit.runner.GradleRunner import org.hamcrest.core.Is import org.junit.Assert @@ -20,15 +19,16 @@ class AcceptanceTestUtils { parser.setFeature('http://apache.org/xml/features/nonvalidating/load-external-dtd', false) } - protected BuildLauncher setupBuild(File projectRoot) { - return GradleConnector. - newConnector(). - forProjectDirectory(projectRoot). - connect(). - newBuild(). - withArguments( - '--init-script', - new File(System.properties.getProperty('user.dir'), 'init-scoverage.gradle').toString()) + protected void runBuild(File projectRoot, String... tasks) { + def runner = GradleRunner + .create() + .withProjectDir(projectRoot) + .withPluginClasspath() + .forwardOutput() + + def arguments = tasks + "-PscoverageVersion=1.3.1" + + runner.withArguments(arguments as List).build() } protected void checkFile(String description, File file, boolean shouldExist) throws Exception { diff --git a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy index d5883f2..90bdb51 100644 --- a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy @@ -7,8 +7,7 @@ class AggregationAcceptanceTest extends AcceptanceTestUtils { @Test public void testMultiProjectAggregation() throws Exception { File projectDir = new File('src/test/water') - def build = setupBuild(projectDir) - build.forTasks('clean', 'aggregateScoverage').run() + runBuild(projectDir, 'clean', 'aggregateScoverage') def indexHtml = new File(aggregateReportDir(projectDir), 'index.html') checkFile('an aggregated index HTML file', indexHtml, true) def cobertura = new File(aggregateReportDir(projectDir), 'cobertura.xml') diff --git a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy index 54c7ec2..4308937 100644 --- a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy @@ -12,8 +12,7 @@ class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { 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) - build.forTasks('clean', 'reportScoverage').run() + runBuild(projectDir, 'clean', 'reportScoverage') def indexHtml = new File(reportDir(subprojectDir), 'index.html') checkFile('an index HTML file', indexHtml, true) def testsIndexHtml = new File(reportDir(testsSubprojectDir), 'index.html') diff --git a/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy index 8be41e5..0c2ca6c 100644 --- a/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy @@ -8,8 +8,7 @@ class SimpleReportAcceptanceTest extends AcceptanceTestUtils { @Test public void testProjectWithCompleteCoverage() throws Exception { File projectRoot = new File('src/test/happy day') - def build = setupBuild(projectRoot) - build.forTasks('clean', 'checkScoverage').run() + runBuild(projectRoot, 'clean', 'checkScoverage') def html = new File(reportDir(projectRoot), 'index.html') checkFile('an index HTML file', html, true) def cobertura = new File(reportDir(projectRoot), 'cobertura.xml') @@ -21,7 +20,6 @@ class SimpleReportAcceptanceTest extends AcceptanceTestUtils { @Test public void testRun() throws Exception { File projectRoot = new File('src/test/runtime') - def build = setupBuild(projectRoot) - build.forTasks('clean', 'run').run() + runBuild(projectRoot, 'clean', 'run') } } diff --git a/src/test/happy day/build.gradle b/src/test/happy day/build.gradle index a4a19ef..e47e145 100644 --- a/src/test/happy day/build.gradle +++ b/src/test/happy day/build.gradle @@ -1,23 +1,16 @@ -description = 'a project that builds successfully with 100% coverage' - -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: '+' - } +plugins { + id 'org.scoverage' } -apply plugin: 'org.scoverage' +description = 'a project that builds successfully with 100% coverage' repositories { - mavenCentral() + jcenter() } dependencies { - scoverage scoverageLib + scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", + "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.0' testCompile 'junit:junit:4.11' } diff --git a/src/test/runtime/build.gradle b/src/test/runtime/build.gradle index 0ed527f..e8e1171 100644 --- a/src/test/runtime/build.gradle +++ b/src/test/runtime/build.gradle @@ -1,23 +1,16 @@ -description = 'a project that runs an application and captures 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: '+' - } +plugins { + id 'org.scoverage' } -apply plugin: 'org.scoverage' +description = 'a project that runs an application and captures scoverage' repositories { - mavenCentral() + jcenter() } dependencies { - scoverage scoverageLib + scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", + "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.0' } diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle index 8a258dd..83522c7 100644 --- a/src/test/separate-tests/build.gradle +++ b/src/test/separate-tests/build.gradle @@ -1,27 +1,29 @@ +plugins { + id 'org.scoverage' +} + description = 'a multi-project with separate tests setup for gradle-scoverage' -buildscript { +allprojects { + repositories { - // need to get up to the working directory of gradle-plugins build - flatDir dir: "${project.projectDir}/../../../build/libs" + jcenter() } + + apply plugin: 'org.scoverage' + dependencies { - classpath name: 'gradle-scoverage', version: '+' + scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", + "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" } } subprojects { - repositories { - mavenCentral() - } - apply plugin: 'scala' - apply plugin: 'org.scoverage' dependencies { compile 'org.scala-lang:scala-library:2.11.4' - scoverage scoverageLib testCompile 'junit:junit:4.11' } diff --git a/src/test/water/build.gradle b/src/test/water/build.gradle index a878fe5..f4d5a6e 100644 --- a/src/test/water/build.gradle +++ b/src/test/water/build.gradle @@ -1,24 +1,19 @@ -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: '+' - } +plugins { + id 'org.scoverage' } +description = 'a multi-project setup for gradle-scoverage' + allprojects { repositories { - mavenCentral() + jcenter() } apply plugin: 'org.scoverage' dependencies { - scoverage scoverageLib + scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", + "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.5' testCompile 'junit:junit:4.11' From de988f122a2e005044b876fe9ab15c4205c581e8 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Tue, 29 Jan 2019 11:10:37 +0200 Subject: [PATCH 02/17] Add new functional tests --- .travis.yml | 2 +- build.gradle | 25 ++++ .../org.scoverage/ScalaSingleModuleTest.java | 92 ++++++++++++ .../ScoverageFunctionalTest.java | 138 ++++++++++++++++++ .../projects/scala-single-module/build.gradle | 34 +++++ .../scala-single-module/settings.gradle | 0 .../src/main/scala/org/hello/World.scala | 9 ++ .../scala/org/hello/TestNothingSuite.scala | 12 ++ .../src/test/scala/org/hello/WorldSuite.scala | 13 ++ .../org/scoverage/ScoveragePlugin.groovy | 1 + 10 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java create mode 100644 src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java create mode 100644 src/functionalTest/resources/projects/scala-single-module/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-single-module/settings.gradle create mode 100644 src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala create mode 100644 src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala diff --git a/.travis.yml b/.travis.yml index 3b53e86..1e5da93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: groovy script: - - "./gradlew --info --stacktrace test" \ No newline at end of file + - "./gradlew --info --stacktrace check" \ No newline at end of file diff --git a/build.gradle b/build.gradle index cf31903..d82bb5d 100644 --- a/build.gradle +++ b/build.gradle @@ -53,6 +53,31 @@ dependencies { testCompile 'org.hamcrest:hamcrest-library:1.3' } +sourceSets { + functionalTest { + java { + srcDir file('src/functionalTest/java') + } + resources { + srcDir file('src/functionalTest/resources') + } + compileClasspath += sourceSets.main.output + configurations.testRuntime + runtimeClasspath += output + compileClasspath + } +} + +task functionalTest(type: Test) { + testClassesDirs = sourceSets.functionalTest.output.classesDirs + classpath = sourceSets.functionalTest.runtimeClasspath +} + +functionalTest.mustRunAfter(test) +check.dependsOn functionalTest + +gradlePlugin { + testSourceSets sourceSets.test, sourceSets.functionalTest +} + task groovydocJar(type: Jar, dependsOn: groovydoc) { classifier = 'groovydoc' from "$buildDir/docs/groovydoc" diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java new file mode 100644 index 0000000..4a6e594 --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -0,0 +1,92 @@ +package org.scoverage; + +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Test; + +public class ScalaSingleModuleTest extends ScoverageFunctionalTest { + + public ScalaSingleModuleTest() { + super("scala-single-module"); + } + + @Test + public void test() { + + AssertableBuildResult result = dryRun("clean", "test"); + + result.assertTaskDoesntExist(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getTEST_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void build() { + + AssertableBuildResult result = dryRun("clean", "build"); + + result.assertTaskDoesntExist(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getTEST_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void testScoverage() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getTEST_NAME()); + + result.assertTaskExists(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskExists(ScoveragePlugin.getTEST_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void reportScoverage() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME()); + + result.assertTaskExists(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskExists(ScoveragePlugin.getTEST_NAME()); + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void aggregateScoverage() { + + AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertNoTasks(); + } + + @Test + public void checkScoverage() { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.SUCCESS); + result.assertTaskSkipped(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkScoverageFails() { + + AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getTEST_NAME(), "--tests", "org.hello.TestNothingSuite"); + + result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); + result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.FAILED); + result.assertTaskSkipped(ScoveragePlugin.getAGGREGATE_NAME()); + } +} \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java new file mode 100644 index 0000000..bba1e8a --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -0,0 +1,138 @@ +package org.scoverage; + +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.BuildTask; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Assert; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public abstract class ScoverageFunctionalTest { + + private final String projectName; + private final GradleRunner runner; + + protected ScoverageFunctionalTest(String projectName) { + + this.projectName = projectName; + this.runner = GradleRunner.create() + .withProjectDir(projectDir()) + .withPluginClasspath() + .forwardOutput(); + + + List filteredPluginClassPath = new ArrayList(); + + for (File file : runner.getPluginClasspath()) { + if (!file.getName().contains("scalac-scoverage-plugin")) { + filteredPluginClassPath.add(file); + } + } + + runner.withPluginClasspath(filteredPluginClassPath); + } + + protected File projectDir() { + + return new File("src/functionalTest/resources/projects/" + projectName); + } + + protected AssertableBuildResult run(String... arguments) { + + configureArguments(arguments); + return new AssertableBuildResult(runner.build()); + } + + protected AssertableBuildResult runAndFail(String... arguments) { + + configureArguments(arguments); + return new AssertableBuildResult(runner.buildAndFail()); + } + + protected AssertableBuildResult dryRun(String... arguments) { + + List withDryArgument = new ArrayList(Arrays.asList(arguments)); + withDryArgument.add("--dry-run"); + return run(withDryArgument.toArray(new String[]{})); + } + + private void configureArguments(String... arguments) { + + List fullArguments = new ArrayList(); + + fullArguments.add("-PscalaVersionMajor=2"); + fullArguments.add("-PscalaVersionMinor=11"); + fullArguments.add("-PscalaVersionBuild=5"); + fullArguments.add("-PjunitVersion=5.3.2"); + fullArguments.add("-PjunitPlatformVersion=1.3.2"); + fullArguments.add("-PscalatestVersion=3.0.5"); + fullArguments.add("-PscoverageVersion=1.3.1"); + fullArguments.addAll(Arrays.asList(arguments)); + + runner.withArguments(fullArguments); + } + + protected static class AssertableBuildResult { + + private final BuildResult result; + + private AssertableBuildResult(BuildResult result) { + + this.result = result; + } + + public BuildResult getResult() { + + return result; + } + + public void assertNoTasks() { + + Assert.assertEquals(0, result.getTasks().size()); + } + + public void assertTaskExists(String taskName) { + + Assert.assertTrue(taskExists(taskName)); + } + + public void assertTaskDoesntExist(String taskName) { + + Assert.assertFalse(taskExists(taskName)); + } + + public void assertTaskSkipped(String taskName) { + + BuildTask task = getTask(taskName); + Assert.assertTrue(task == null || task.getOutcome() == TaskOutcome.SKIPPED); + } + + public void assertTaskOutcome(String taskName, TaskOutcome outcome) { + + BuildTask task = getTask(taskName); + Assert.assertNotNull(task); + Assert.assertEquals(outcome, task.getOutcome()); + + } + + private BuildTask getTask(String taskName) { + + return result.task(fullTaskName(taskName)); + } + + private String fullTaskName(String taskName) { + + return ":" + taskName; + } + + private boolean taskExists(String taskName) { + + return result.getOutput().contains(fullTaskName(taskName) + " "); + } + } +} + diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle new file mode 100644 index 0000000..cad5985 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'org.scoverage' +} + +repositories { + jcenter() +} + +description = 'a single-module Scala project that builds successfully with 100% coverage' + +apply plugin: 'java' +apply plugin: 'scala' + +dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + + testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion + + def fullScoverageVersion = "${scalaVersionMajor}.${scalaVersionMinor}:${scoverageVersion}" + + scoverage "org.scoverage:scalac-scoverage-plugin_${fullScoverageVersion}", + "org.scoverage:scalac-scoverage-runtime_${fullScoverageVersion}" +} + +test { + useJUnitPlatform() +} + +checkScoverage { + minimumRate = 0.5 +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/settings.gradle b/src/functionalTest/resources/projects/scala-single-module/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala b/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala new file mode 100644 index 0000000..27dbe28 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala @@ -0,0 +1,9 @@ +package org.hello + +class World { + + def foo(): String = { + val s = "a" + "b" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala new file mode 100644 index 0000000..1ac25b5 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/TestNothingSuite.scala @@ -0,0 +1,12 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class TestNothingSuite extends FunSuite { + + test("nothing") { + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala new file mode 100644 index 0000000..7281a12 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module/src/test/scala/org/hello/WorldSuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldSuite extends FunSuite { + + test("foo") { + new World().foo() + } +} \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 16a87be..cc4b25b 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -10,6 +10,7 @@ class ScoveragePlugin implements Plugin { static String REPORT_NAME = 'reportScoverage' static String CHECK_NAME = 'checkScoverage' static String COMPILE_NAME = 'compileScoverageScala' + static String AGGREGATE_NAME = 'aggregateScoverage' @Override void apply(Project t) { From 78dc56f9a451fe227c76446aa712f7b23cccff5b Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Tue, 29 Jan 2019 13:37:15 +0200 Subject: [PATCH 03/17] Make the plugin configure the scalac-plugin classpath and the aggregation task automatically --- README.md | 16 +- build.gradle | 2 +- .../org.scoverage/ScalaSingleModuleTest.java | 4 +- .../ScoverageFunctionalTest.java | 16 +- .../projects/scala-single-module/build.gradle | 5 - .../org/scoverage/AggregateReportApp.java | 33 --- .../org/scoverage/OverallCheckTask.groovy | 8 +- .../org/scoverage/ScoverageAggregate.groovy | 55 +++-- .../org/scoverage/ScoverageExtension.groovy | 184 +++++----------- .../org/scoverage/ScoveragePlugin.groovy | 201 ++++++++++++++++-- .../org/scoverage/ScoverageReport.groovy | 74 +++++-- .../org/scoverage/ScoverageRunner.groovy | 32 +++ .../groovy/org/scoverage/SingleReportApp.java | 53 ----- .../org/scoverage/AcceptanceTestUtils.groovy | 4 +- src/test/happy day/build.gradle | 2 - src/test/runtime/build.gradle | 2 - src/test/separate-tests/build.gradle | 16 +- src/test/water/build.gradle | 11 +- 18 files changed, 376 insertions(+), 342 deletions(-) delete mode 100644 src/main/groovy/org/scoverage/AggregateReportApp.java create mode 100644 src/main/groovy/org/scoverage/ScoverageRunner.groovy delete mode 100644 src/main/groovy/org/scoverage/SingleReportApp.java diff --git a/README.md b/README.md index 77de109..bbbb87c 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,12 @@ This creates an additional task `testScoverage` which will run tests against ins A further task `reportScoverage` produces XML and HTML reports for analysing test code coverage. -You need to configure the version of Scoverage that will be used. This plugin should be compatible with all 1+ versions. +You can configure the version of Scoverage that will be used. This plugin should be compatible with all 1+ versions. ```groovy -dependencies { - scoverage 'org.scoverage:scalac-scoverage-plugin_2.11:1.1.0', 'org.scoverage:scalac-scoverage-runtime_2.11:1.1.0' +scoverage { + scoverageVersion = "1.3.1" + scoverageScalaVersion = "2.12" // will be overridden by the 'scala-library' version (if configured) } ``` @@ -49,14 +50,7 @@ Aggregating Reports 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) -``` +When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`. This will produce a report into `build/scoverage-aggregate` directory. diff --git a/build.gradle b/build.gradle index d82bb5d..4cbd2b9 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ sourceCompatibility = '1.6' targetCompatibility = '1.6' dependencies { - compile "org.scoverage:scalac-scoverage-plugin_2.12:1.3.1" + compileOnly "org.scoverage:scalac-scoverage-plugin_2.12:1.3.1" testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3' } diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index 4a6e594..d4c3706 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -74,7 +74,7 @@ public void checkScoverage() { result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.SUCCESS); - result.assertTaskSkipped(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } @Test @@ -87,6 +87,6 @@ public void checkScoverageFails() { result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.FAILED); - result.assertTaskSkipped(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } } \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java index bba1e8a..06311c6 100644 --- a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -23,17 +23,6 @@ protected ScoverageFunctionalTest(String projectName) { .withProjectDir(projectDir()) .withPluginClasspath() .forwardOutput(); - - - List filteredPluginClassPath = new ArrayList(); - - for (File file : runner.getPluginClasspath()) { - if (!file.getName().contains("scalac-scoverage-plugin")) { - filteredPluginClassPath.add(file); - } - } - - runner.withPluginClasspath(filteredPluginClassPath); } protected File projectDir() { @@ -65,12 +54,11 @@ private void configureArguments(String... arguments) { List fullArguments = new ArrayList(); fullArguments.add("-PscalaVersionMajor=2"); - fullArguments.add("-PscalaVersionMinor=11"); - fullArguments.add("-PscalaVersionBuild=5"); + fullArguments.add("-PscalaVersionMinor=12"); + fullArguments.add("-PscalaVersionBuild=8"); fullArguments.add("-PjunitVersion=5.3.2"); fullArguments.add("-PjunitPlatformVersion=1.3.2"); fullArguments.add("-PscalatestVersion=3.0.5"); - fullArguments.add("-PscoverageVersion=1.3.1"); fullArguments.addAll(Arrays.asList(arguments)); runner.withArguments(fullArguments); diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index cad5985..b0a4ec6 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -18,11 +18,6 @@ dependencies { testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion - - def fullScoverageVersion = "${scalaVersionMajor}.${scalaVersionMinor}:${scoverageVersion}" - - scoverage "org.scoverage:scalac-scoverage-plugin_${fullScoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_${fullScoverageVersion}" } test { diff --git a/src/main/groovy/org/scoverage/AggregateReportApp.java b/src/main/groovy/org/scoverage/AggregateReportApp.java deleted file mode 100644 index 2bf2f14..0000000 --- a/src/main/groovy/org/scoverage/AggregateReportApp.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.scoverage; - -import scoverage.Coverage; -import scoverage.report.CoverageAggregator; - -import java.io.File; - -public class AggregateReportApp { - - public static void main(String... args) { - File rootDir = new File(args[0]); - File reportDir = new File(args[1]); - Boolean clean = Boolean.parseBoolean(args[2]); - - 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(); - - ScoverageWriter.write( - rootDir, - reportDir, - coverage, - coverageOutputCobertura, - coverageOutputXML, - coverageOutputHTML, - coverageDebug - ); - } - -} \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/OverallCheckTask.groovy b/src/main/groovy/org/scoverage/OverallCheckTask.groovy index 356ca6e..810c0ad 100644 --- a/src/main/groovy/org/scoverage/OverallCheckTask.groovy +++ b/src/main/groovy/org/scoverage/OverallCheckTask.groovy @@ -2,6 +2,7 @@ package org.scoverage import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.TaskAction import org.gradle.internal.impldep.com.google.common.annotations.VisibleForTesting @@ -46,19 +47,16 @@ class OverallCheckTask extends DefaultTask { CoverageType coverageType = CoverageType.Statement double minimumRate = 0.75 - /** Set if want to change default from 'reportDir' in scoverage extension. */ - File reportDir + final Property reportDir = project.objects.property(File) /** Overwrite to test for a specific locale. */ Locale locale @TaskAction void requireLineCoverage() { - def extension = ScoveragePlugin.extensionIn(project) - NumberFormat nf = NumberFormat.getInstance(locale == null ? Locale.getDefault() : locale) - Exception failure = checkLineCoverage(nf, reportDir == null ? extension.reportDir : reportDir, coverageType, minimumRate) + Exception failure = checkLineCoverage(nf, reportDir.get(), coverageType, minimumRate) if (failure) throw failure } diff --git a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy index 8703d09..9e96781 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -1,32 +1,43 @@ package org.scoverage -import org.gradle.api.tasks.JavaExec +import org.gradle.api.DefaultTask +import org.gradle.api.provider.Property +import org.gradle.api.tasks.TaskAction +import scoverage.report.CoverageAggregator -class ScoverageAggregate extends JavaExec { +class ScoverageAggregate extends DefaultTask { + ScoverageRunner runner + + // TODO - consider separate options for `report` and `aggregate` tasks + final Property coverageOutputCobertura = project.objects.property(Boolean) + final Property coverageOutputXML = project.objects.property(Boolean) + final Property coverageOutputHTML = project.objects.property(Boolean) + final Property coverageDebug = project.objects.property(Boolean) + + // TODO get these from extension boolean clean = false File reportDir - @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, - // TODO - consider separate options for `report` and `aggregate` tasks - extension.coverageOutputCobertura, - extension.coverageOutputXML, - extension.coverageOutputHTML, - extension.coverageDebug - ]) - super.exec() - } + @TaskAction + def aggregate() { + runner.run { + def rootDir = project.projectDir + def reportPath = reportDir ? reportDir : new File(project.buildDir, 'scoverage-aggregate') + + def coverage = CoverageAggregator.aggregate(rootDir, clean) - def reportDirOrDefault() { - return reportDir ? reportDir : new File(project.buildDir, 'scoverage-aggregate') + if (coverage.nonEmpty()) { + ScoverageWriter.write( + rootDir, + reportPath, + coverage.get(), + coverageOutputCobertura.get(), + coverageOutputXML.get(), + coverageOutputHTML.get(), + coverageDebug.get() + ) + } + } } } diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 7c47d1e..997b467 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -1,18 +1,10 @@ package org.scoverage -import org.gradle.api.Action -import org.gradle.api.GradleException import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.file.FileCollection import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.scala.ScalaPlugin -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 - -import java.util.concurrent.Callable +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property /** * Defines a new SourceSet for the code to be instrumented. @@ -21,142 +13,70 @@ import java.util.concurrent.Callable */ class ScoverageExtension { + /** Version of scoverage to use for the scalac plugin */ + final Property scoverageVersion + + /** Version of scala to use for the scalac plugin */ + final Property scoverageScalaVersion + /** a directory to write working files to */ - File dataDir + final Property dataDir /** a directory to write final output to */ - File reportDir + final Property reportDir /** sources to highlight */ - File sources + final Property sources /** range positioning for highlighting */ - boolean highlighting = true + final Property highlighting /** regex for each excluded package */ - List excludedPackages = [] + final ListProperty excludedPackages /** regex for each excluded file */ - List excludedFiles = [] - - FileCollection pluginClasspath + final ListProperty excludedFiles /** Options for enabling and disabling output */ - boolean coverageOutputCobertura = true - boolean coverageOutputXML = true - boolean coverageOutputHTML = true - boolean coverageDebug = false + final Property coverageOutputCobertura + final Property coverageOutputXML + final Property coverageOutputHTML + final Property coverageDebug ScoverageExtension(Project project) { project.plugins.apply(JavaPlugin.class) project.plugins.apply(ScalaPlugin.class) - project.afterEvaluate(configureRuntimeOptions) - - project.configurations.create(ScoveragePlugin.CONFIGURATION_NAME) { - visible = false - transitive = true - description = 'Scoverage dependencies' - } - - def instrumentedSourceSet = project.sourceSets.create('scoverage') { - def original = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - - resources.source(original.resources) - scala.source(original.java) - scala.source(original.scala) - - compileClasspath += original.compileClasspath + project.configurations.scoverage - runtimeClasspath = it.output + project.configurations.scoverage + original.runtimeClasspath - } - - def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { - dependsOn('scoverageClasses') - classifier = ScoveragePlugin.CONFIGURATION_NAME - from instrumentedSourceSet.output - } - project.artifacts { - scoverage scoverageJar - } - - project.tasks.create(ScoveragePlugin.TEST_NAME, Test.class) { - conventionMapping.map("classpath", new Callable() { - Object call() throws Exception { - def testSourceSet = project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) - return testSourceSet.output + - instrumentedSourceSet.output + - project.configurations.scoverage + - testSourceSet.runtimeClasspath - } - }) - group = 'verification' - } - - project.tasks.create(ScoveragePlugin.REPORT_NAME, ScoverageReport.class) { - dependsOn(project.tasks[ScoveragePlugin.TEST_NAME]) - onlyIf { ScoveragePlugin.extensionIn(project).dataDir.list() } - group = 'verification' - } - - project.tasks.create(ScoveragePlugin.CHECK_NAME, OverallCheckTask.class) { - dependsOn(project.tasks[ScoveragePlugin.REPORT_NAME]) - group = 'verification' - } - - sources = project.projectDir - dataDir = new File(project.buildDir, 'scoverage') - reportDir = new File(project.buildDir, 'reports' + File.separatorChar + 'scoverage') - def classLocation = ScoverageExtension.class.getProtectionDomain().getCodeSource().getLocation() - pluginClasspath = project.files(classLocation.file) + project.configurations.scoverage - } - private Action configureRuntimeOptions = new Action() { - - @Override - void execute(Project t) { - - def extension = ScoveragePlugin.extensionIn(t) - extension.dataDir.mkdirs() - - Configuration configuration = t.configurations[ScoveragePlugin.CONFIGURATION_NAME] - File pluginFile - try { - pluginFile = configuration.filter { it.name.contains('plugin') }.iterator().next() - } catch(NoSuchElementException ignored) { - throw new GradleException("Could not find a plugin jar in configuration '${ScoveragePlugin.CONFIGURATION_NAME}'") - } - - t.tasks[ScoveragePlugin.COMPILE_NAME].configure { - List parameters = ['-Xplugin:' + pluginFile.absolutePath] - List existingParameters = scalaCompileOptions.additionalParameters - if (existingParameters) { - parameters.addAll(existingParameters) - } - parameters.add("-P:scoverage:dataDir:${extension.dataDir.absolutePath}".toString()) - if (extension.excludedPackages) { - parameters.add("-P:scoverage:excludedPackages:${extension.excludedPackages.join(';')}".toString()) - } - if (extension.excludedFiles) { - parameters.add("-P:scoverage:excludedFiles:${extension.excludedFiles.join(';')}".toString()) - } - if (extension.highlighting) { - parameters.add('-Yrangepos') - } - doFirst { - GFileUtils.deleteDirectory(destinationDir) - } - scalaCompileOptions.additionalParameters = parameters - // the compile task creates a store of measured statements - outputs.file(new File(extension.dataDir, 'scoverage.coverage.xml')) - } - t.tasks[ScoveragePlugin.TEST_NAME].outputs.upToDateWhen { extension.dataDir.listFiles(measurementFile) } - t.tasks[ScoveragePlugin.REPORT_NAME].configure { - inputs.dir(extension.dataDir) - outputs.dir(extension.reportDir) - } - } - - FilenameFilter measurementFile = new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - return name.startsWith("scoverage.measurements.") - } - } + scoverageVersion = project.objects.property(String) + scoverageVersion.set('1.3.1') + + scoverageScalaVersion = project.objects.property(String) + scoverageScalaVersion.set('2.12') + + sources = project.objects.property(File) + sources.set(project.projectDir) + + dataDir = project.objects.property(File) + dataDir.set(new File(project.buildDir, 'scoverage')) + + reportDir = project.objects.property(File) + reportDir.set(new File(project.buildDir, 'reports' + File.separatorChar + 'scoverage')) + + highlighting = project.objects.property(Boolean) + highlighting.set(true) + + excludedPackages = project.objects.listProperty(String) + excludedPackages.set([]) + + excludedFiles = project.objects.listProperty(String) + excludedFiles.set([]) + + coverageOutputCobertura = project.objects.property(Boolean) + coverageOutputCobertura.set(true) + + coverageOutputXML = project.objects.property(Boolean) + coverageOutputXML.set(true) + + coverageOutputHTML = project.objects.property(Boolean) + coverageOutputHTML.set(true) + coverageDebug = project.objects.property(Boolean) + coverageDebug.set(false) } } diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index cc4b25b..9609619 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -1,25 +1,200 @@ package org.scoverage +import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.invocation.Gradle +import org.gradle.api.plugins.PluginAware +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 -class ScoveragePlugin implements Plugin { - static String CONFIGURATION_NAME = 'scoverage' +import java.util.concurrent.Callable - static String TEST_NAME = 'testScoverage' - static String REPORT_NAME = 'reportScoverage' - static String CHECK_NAME = 'checkScoverage' - static String COMPILE_NAME = 'compileScoverageScala' - static String AGGREGATE_NAME = 'aggregateScoverage' +class ScoveragePlugin implements Plugin { + + static final String CONFIGURATION_NAME = 'scoverage' + static final String TEST_NAME = 'testScoverage' + static final String REPORT_NAME = 'reportScoverage' + static final String CHECK_NAME = 'checkScoverage' + static final String COMPILE_NAME = 'compileScoverageScala' + static final String AGGREGATE_NAME = 'aggregateScoverage' @Override - void apply(Project t) { - if (t.extensions.findByName(CONFIGURATION_NAME) == null) { - t.extensions.create(CONFIGURATION_NAME, ScoverageExtension, t) + void apply(PluginAware pluginAware) { + if (pluginAware instanceof Project) { + applyProject(pluginAware) + if (pluginAware == pluginAware.rootProject) { + pluginAware.subprojects { p -> + p.plugins.apply(ScoveragePlugin) + } + } + } else if (pluginAware instanceof Gradle) { + pluginAware.allprojects { p -> + p.plugins.apply(ScoveragePlugin) + } + } else { + throw new IllegalArgumentException("${pluginAware.getClass()} is currently not supported as an apply target, please report if you need it") + } + } + + void applyProject(Project project) { + + if (project.plugins.hasPlugin(ScoveragePlugin)) { + project.logger.info("Project ${project.name} already has the scoverage plugin") + return + } + project.logger.info("Applying scoverage plugin to $project.name") + + def extension = project.extensions.create('scoverage', ScoverageExtension, project) + if (!project.configurations.asMap[CONFIGURATION_NAME]) { + project.configurations.create(CONFIGURATION_NAME) { + visible = false + transitive = true + description = 'Scoverage dependencies' + } + + project.afterEvaluate { + def scoverageVersion = project.extensions.scoverage.scoverageVersion.get() + def scalaVersion = project.extensions.scoverage.scoverageScalaVersion.get() + + def scalaLibrary = project.configurations.compile.dependencies.find { + it.group == "org.scala-lang" && it.name == "scala-library" + } + + if (scalaLibrary != null) { + scalaVersion = scalaLibrary.version.substring(0, scalaLibrary.version.lastIndexOf(".")) + } + + def fullScoverageVersion = "$scalaVersion:$scoverageVersion" + + project.logger.info("Using scoverage scalac plugin version '$fullScoverageVersion'") + + project.dependencies { + scoverage("org.scoverage:scalac-scoverage-plugin_$fullScoverageVersion") + scoverage("org.scoverage:scalac-scoverage-runtime_$fullScoverageVersion") + } + } + } + + ScoverageRunner scoverageRunner = new ScoverageRunner(project.configurations.scoverage) + + createTasks(project, extension, scoverageRunner) + + project.afterEvaluate { + configureAfterEvaluation(project, extension, scoverageRunner) } } - protected static ScoverageExtension extensionIn(Project project) { - project.extensions[CONFIGURATION_NAME] as ScoverageExtension + private void createTasks(Project project, ScoverageExtension extension, ScoverageRunner scoverageRunner) { + + + def instrumentedSourceSet = project.sourceSets.create('scoverage') { + def original = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + + resources.source(original.resources) + scala.source(original.java) + scala.source(original.scala) + + compileClasspath += original.compileClasspath + project.configurations.scoverage + runtimeClasspath = it.output + project.configurations.scoverage + original.runtimeClasspath + } + + def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { + dependsOn('scoverageClasses') + classifier = CONFIGURATION_NAME + from instrumentedSourceSet.output + } + project.artifacts { + scoverage scoverageJar + } + + project.tasks.create(TEST_NAME, Test.class) { + conventionMapping.map("classpath", new Callable() { + Object call() throws Exception { + def testSourceSet = project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) + return testSourceSet.output + + instrumentedSourceSet.output + + project.configurations.scoverage + + testSourceSet.runtimeClasspath + } + }) + group = 'verification' + + FilenameFilter measurementFile = new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + return name.startsWith("scoverage.measurements.") + } + } + outputs.upToDateWhen { extension.dataDir.get().listFiles(measurementFile) } + } + + project.tasks.create(REPORT_NAME, ScoverageReport.class) { + dependsOn(project.tasks[TEST_NAME]) + onlyIf { extension.dataDir.get().list() } + group = 'verification' + runner = scoverageRunner + reportDir = extension.reportDir + sources = extension.sources + dataDir = extension.dataDir + coverageOutputCobertura = extension.coverageOutputCobertura + coverageOutputXML = extension.coverageOutputXML + coverageOutputHTML = extension.coverageOutputHTML + coverageDebug = extension.coverageDebug + } + + project.tasks.create(CHECK_NAME, OverallCheckTask.class) { + dependsOn(project.tasks[REPORT_NAME]) + group = 'verification' + reportDir = extension.reportDir + } + + } + + private void configureAfterEvaluation(Project project, ScoverageExtension extension, ScoverageRunner scoverageRunner) { + + if (project.childProjects.size() > 0) { + def reportTasks = project.getSubprojects().collect { it.tasks.withType(ScoverageReport) } + project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { + dependsOn(reportTasks) + group = 'verification' + runner = scoverageRunner + coverageOutputCobertura = extension.coverageOutputCobertura + coverageOutputXML = extension.coverageOutputXML + coverageOutputHTML = extension.coverageOutputHTML + coverageDebug = extension.coverageDebug + } + } + + project.tasks[COMPILE_NAME].configure { + File pluginFile = project.configurations[CONFIGURATION_NAME].find { + it.name.startsWith("scalac-scoverage-plugin") + } + List parameters = ['-Xplugin:' + pluginFile.absolutePath] + List existingParameters = scalaCompileOptions.additionalParameters + if (existingParameters) { + parameters.addAll(existingParameters) + } + parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) + if (extension.excludedPackages.get()) { + def packages = extension.excludedPackages.get().join(';') + parameters.add("-P:scoverage:excludedPackages:$packages".toString()) + } + if (extension.excludedFiles.get()) { + def packages = extension.excludedFiles.get().join(';') + parameters.add("-P:scoverage:excludedFiles:$packages".toString()) + } + if (extension.highlighting.get()) { + parameters.add('-Yrangepos') + } + doFirst { + GFileUtils.deleteDirectory(destinationDir) + } + scalaCompileOptions.additionalParameters = parameters + // the compile task creates a store of measured statements + outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) + } } -} +} \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index ddf81ac..3e53858 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -1,26 +1,62 @@ package org.scoverage +import org.gradle.api.DefaultTask +import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import scala.collection.Seq +import scala.collection.Set +import scoverage.Coverage +import scoverage.IOUtils +import scoverage.Serializer @CacheableTask -class ScoverageReport extends JavaExec { - - @Override - void exec() { - def extension = ScoveragePlugin.extensionIn(project) - extension.reportDir.mkdirs() - setClasspath(extension.pluginClasspath) - setMain('org.scoverage.SingleReportApp') - setArgs([ - /* sourceDir = */ extension.sources.absolutePath, - /* dataDir = */ extension.dataDir.absolutePath, - /* reportDir = */ extension.reportDir.absolutePath, - extension.coverageOutputCobertura, - extension.coverageOutputXML, - extension.coverageOutputHTML, - extension.coverageDebug - ]) - super.exec() +class ScoverageReport extends DefaultTask { + + ScoverageRunner runner + + @Input + final Property dataDir = project.objects.property(File) + + @OutputFile + final Property reportDir = project.objects.property(File) + + final Property sources = project.objects.property(File) + final Property coverageOutputCobertura = project.objects.property(Boolean) + final Property coverageOutputXML = project.objects.property(Boolean) + final Property coverageOutputHTML = project.objects.property(Boolean) + final Property coverageDebug = project.objects.property(Boolean) + + @TaskAction + def report() { + runner.run { + reportDir.get().mkdirs() + + File coverageFile = Serializer.coverageFile(dataDir.get()) + + if (!coverageFile.exists()) { + project.logger.info("[scoverage] Could not find coverage file, skipping...") + } else { + File[] array = IOUtils.findMeasurementFiles(dataDir.get()) + // 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( + sources.get(), + reportDir.get(), + coverage, + coverageOutputCobertura.get(), + coverageOutputXML.get(), + coverageOutputHTML.get(), + coverageDebug.get()) + } + } } } diff --git a/src/main/groovy/org/scoverage/ScoverageRunner.groovy b/src/main/groovy/org/scoverage/ScoverageRunner.groovy new file mode 100644 index 0000000..48889e7 --- /dev/null +++ b/src/main/groovy/org/scoverage/ScoverageRunner.groovy @@ -0,0 +1,32 @@ +package org.scoverage + +import org.gradle.api.file.FileCollection + +import java.lang.reflect.Method + +class ScoverageRunner { + + private FileCollection runtimeClasspath + + ScoverageRunner(FileCollection runtimeClasspath) { + + this.runtimeClasspath = runtimeClasspath + } + + def run(Closure action) { + + URLClassLoader cloader = (URLClassLoader) Thread.currentThread().getContextClassLoader() + + Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class) + method.setAccessible(true) + + runtimeClasspath.files.each { f -> + def url = f.toURL() + if (!cloader.getURLs().contains(url)) { + method.invoke(cloader, url) + } + } + + action.call() + } +} diff --git a/src/main/groovy/org/scoverage/SingleReportApp.java b/src/main/groovy/org/scoverage/SingleReportApp.java deleted file mode 100644 index 998cb83..0000000 --- a/src/main/groovy/org/scoverage/SingleReportApp.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.scoverage; - -import scala.collection.Seq; -import scala.collection.Set; -import scoverage.Coverage; -import scoverage.IOUtils; -import scoverage.Serializer; - -import java.io.File; -import java.util.Arrays; - -/** - * late binding of scoverage core libraries (without a dependency on groovy) - */ -public class SingleReportApp { - - public static void main(String... args) { - File sourceDir = new File(args[0]); - File dataDir = new File(args[1]); - File reportDir = new File(args[2]); - - 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); - - 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); - } - } - - -} \ No newline at end of file diff --git a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy index 00498c5..14e22c2 100644 --- a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy +++ b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy @@ -26,9 +26,7 @@ class AcceptanceTestUtils { .withPluginClasspath() .forwardOutput() - def arguments = tasks + "-PscoverageVersion=1.3.1" - - runner.withArguments(arguments as List).build() + runner.withArguments(tasks).build() } protected void checkFile(String description, File file, boolean shouldExist) throws Exception { diff --git a/src/test/happy day/build.gradle b/src/test/happy day/build.gradle index e47e145..17a1522 100644 --- a/src/test/happy day/build.gradle +++ b/src/test/happy day/build.gradle @@ -9,8 +9,6 @@ repositories { } dependencies { - scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.0' testCompile 'junit:junit:4.11' } diff --git a/src/test/runtime/build.gradle b/src/test/runtime/build.gradle index e8e1171..ec21818 100644 --- a/src/test/runtime/build.gradle +++ b/src/test/runtime/build.gradle @@ -9,8 +9,6 @@ repositories { } dependencies { - scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.0' } diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle index 83522c7..1aeb9b8 100644 --- a/src/test/separate-tests/build.gradle +++ b/src/test/separate-tests/build.gradle @@ -9,13 +9,6 @@ allprojects { repositories { jcenter() } - - apply plugin: 'org.scoverage' - - dependencies { - scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" - } } subprojects { @@ -45,20 +38,13 @@ subprojects { configure(subprojects.findAll { it.name.endsWith('-tests') }) { def mainProject = project(":${project.name.minus('-tests')}") dependencies { - testCompile mainProject - scoverage mainProject.configurations.scoverage.artifacts.files + testCompile mainProject.sourceSets.scoverage.output } scoverage { sources = mainProject.extensions.scoverage.sources dataDir = mainProject.extensions.scoverage.dataDir reportDir = mainProject.extensions.scoverage.reportDir } - sourceSets { - testScoverage { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } - } compileScoverageScala { onlyIf { false } } diff --git a/src/test/water/build.gradle b/src/test/water/build.gradle index f4d5a6e..b9bb87c 100644 --- a/src/test/water/build.gradle +++ b/src/test/water/build.gradle @@ -9,20 +9,11 @@ allprojects { jcenter() } - apply plugin: 'org.scoverage' + apply plugin: 'scala' dependencies { - scoverage "org.scoverage:scalac-scoverage-plugin_2.11:${scoverageVersion}", - "org.scoverage:scalac-scoverage-runtime_2.11:${scoverageVersion}" compile 'org.scala-lang:scala-library:2.11.5' - testCompile 'junit:junit:4.11' } } -task aggregateScoverage(type: org.scoverage.ScoverageAggregate) - -subprojects { - aggregateScoverage.dependsOn(it.tasks.reportScoverage) -} - From b2fbb84ba5dbaf8ef7af4426a1a44483dfacafaf Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Tue, 29 Jan 2019 17:05:23 +0200 Subject: [PATCH 04/17] Properly handle report, check and aggregation tasks in multi-module projects --- README.md | 22 +-- .../org.scoverage/ScalaMultiModuleTest.java | 167 ++++++++++++++++++ .../org.scoverage/ScalaSingleModuleTest.java | 16 +- .../ScoverageFunctionalTest.java | 14 +- .../a/src/main/scala/org/hello/a/WorldA.scala | 9 + .../scala/org/hello/a/TestNothingASuite.scala | 12 ++ .../test/scala/org/hello/a/WorldASuite.scala | 13 ++ .../b/src/main/scala/org/hello/b/WorldB.scala | 9 + .../scala/org/hello/b/TestNothingBSuite.scala | 12 ++ .../test/scala/org/hello/b/WorldBSuite.scala | 13 ++ .../projects/scala-multi-module/build.gradle | 38 ++++ .../scala-multi-module/settings.gradle | 1 + .../src/main/scala/org/hello/World.scala | 9 + .../scala/org/hello/TestNothingSuite.scala | 12 ++ .../src/test/scala/org/hello/WorldSuite.scala | 13 ++ .../org/scoverage/ScoverageAggregate.groovy | 17 +- .../org/scoverage/ScoverageExtension.groovy | 5 + .../org/scoverage/ScoveragePlugin.groovy | 9 +- .../org/scoverage/ScoverageReport.groovy | 8 +- .../AggregationAcceptanceTest.groovy | 2 +- 20 files changed, 368 insertions(+), 33 deletions(-) create mode 100644 src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java create mode 100644 src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/TestNothingASuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/WorldASuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/TestNothingBSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/WorldBSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module/settings.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module/src/main/scala/org/hello/World.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/TestNothingSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/WorldSuite.scala diff --git a/README.md b/README.md index bbbb87c..696adb5 100644 --- a/README.md +++ b/README.md @@ -48,25 +48,27 @@ You can configure output generated by `gradle reportScoverage` using flags: Aggregating Reports ------------------- -There is now experimental support for aggregating coverage statistics across sub-projects. -When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`. +There is now experimental support for aggregating coverage statistics in composite builds. -This will produce a report into `build/scoverage-aggregate` directory. +When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`, which +will first generate reports for each project individually (including the parent project), and will then generate an +aggregated result based on these reports. -Aggregation uses same flags as reporting for enabling/disabling different output types. +The aggregated report will override the parent-project specific report (`parent-project/build/reports/scoverage`). -For checking coverage of the aggregated result, configure the checkScoverage task: +One can still use `reportScoverage` in order to generate a report without aggregation. -```groovy -checkScoverage { - reportDir = file("$buildDir/scoverage-aggregate") -} -``` +Aggregation uses same flags as reporting for enabling/disabling different output types. CheckScoverage -------------- +The `checkScoverage` task validates coverage status according the generated reports. + +`gradle checkScoverage` will automatically generate reports via `reportScoverage` but it won't generate aggregated reports. +In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`. + By default, when you launch `gradle checkScoverage` build fail if only 75% of statements in project is covered by tests. To configure it as you want, add this configuration : diff --git a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java new file mode 100644 index 0000000..92b427c --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java @@ -0,0 +1,167 @@ +package org.scoverage; + +import org.junit.Test; + +public class ScalaMultiModuleTest extends ScoverageFunctionalTest { + + public ScalaMultiModuleTest() { + super("scala-multi-module"); + } + + @Test + public void reportScoverage() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME()); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + } + + @Test + public void reportScoverageOnlyRoot() { + + AssertableBuildResult result = dryRun("clean", ":" + ScoveragePlugin.getREPORT_NAME()); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + } + + @Test + public void reportScoverageOnlyA() { + + AssertableBuildResult result = dryRun("clean", ":a:" + ScoveragePlugin.getREPORT_NAME()); + + result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + } + + @Test + public void aggregateScoverage() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkScoverage() { + + AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskExists("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkScoverageOnlyRoot() { + + AssertableBuildResult result = dryRun("clean", ":" + ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist("a:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkScoverageOnlyA() { + + AssertableBuildResult result = dryRun("clean", ":a:" + ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkAndAggregateScoverage() { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkScoverageWithoutCoverageInRoot() { + + AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getTEST_NAME(), + "--tests", "org.hello.TestNothingSuite", + "--tests", "org.hello.a.WorldASuite", + "--tests", "org.hello.b.WorldBSuite"); + + result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void checkScoverageWithoutCoverageInA() { + + AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getTEST_NAME(), + "--tests", "org.hello.a.TestNothingASuite", + "--tests", "org.hello.WorldSuite", + "--tests", "org.hello.b.WorldBSuite"); + + result.assertTaskFailed("a:" + ScoveragePlugin.getCHECK_NAME()); + } + + @Test + public void checkAndAggregateScoverageWithoutCoverageInRoot() { + + // should pass as the check on the root is for the aggregation (which covers > 50%) + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(), + "--tests", "org.hello.TestNothingSuite", + "--tests", "org.hello.a.WorldASuite", + "--tests", "org.hello.b.WorldBSuite"); + + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + } + + @Test + public void checkAndAggregateScoverageWithoutCoverageInAll() { + + AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(), + "--tests", "org.hello.TestNothingSuite", + "--tests", "org.hello.a.TestNothingASuite", + "--tests", "org.hello.b.TestNothingBSuite"); + + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); + } +} \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index d4c3706..ea4823a 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -70,10 +70,10 @@ public void checkScoverage() { AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); - result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.SUCCESS); + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } @@ -83,10 +83,10 @@ public void checkScoverageFails() { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getTEST_NAME(), "--tests", "org.hello.TestNothingSuite"); - result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS); - result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.FAILED); + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } } \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java index 06311c6..eb64c61 100644 --- a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Pattern; public abstract class ScoverageFunctionalTest { @@ -99,6 +100,16 @@ public void assertTaskSkipped(String taskName) { Assert.assertTrue(task == null || task.getOutcome() == TaskOutcome.SKIPPED); } + public void assertTaskSucceeded(String taskName) { + + assertTaskOutcome(taskName, TaskOutcome.SUCCESS); + } + + public void assertTaskFailed(String taskName) { + + assertTaskOutcome(taskName, TaskOutcome.FAILED); + } + public void assertTaskOutcome(String taskName, TaskOutcome outcome) { BuildTask task = getTask(taskName); @@ -119,7 +130,8 @@ private String fullTaskName(String taskName) { private boolean taskExists(String taskName) { - return result.getOutput().contains(fullTaskName(taskName) + " "); + Pattern regex = Pattern.compile("^" + fullTaskName(taskName), Pattern.MULTILINE); + return regex.matcher(result.getOutput()).find(); } } } diff --git a/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala b/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala new file mode 100644 index 0000000..301f445 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala @@ -0,0 +1,9 @@ +package org.hello.a + +class WorldA { + + def fooA(): String = { + val s = "a" + "a" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/TestNothingASuite.scala b/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/TestNothingASuite.scala new file mode 100644 index 0000000..996857c --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/TestNothingASuite.scala @@ -0,0 +1,12 @@ +package org.hello.a + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class TestNothingASuite extends FunSuite { + + test("nothing") { + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/WorldASuite.scala b/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/WorldASuite.scala new file mode 100644 index 0000000..840bdcc --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/a/src/test/scala/org/hello/a/WorldASuite.scala @@ -0,0 +1,13 @@ +package org.hello.a + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldASuite extends FunSuite { + + test("fooA") { + new WorldA().fooA() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala b/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala new file mode 100644 index 0000000..bb7a579 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala @@ -0,0 +1,9 @@ +package org.hello.b + +class WorldB { + + def fooB(): String = { + val s = "b" + "b" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/TestNothingBSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/TestNothingBSuite.scala new file mode 100644 index 0000000..bebfee8 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/TestNothingBSuite.scala @@ -0,0 +1,12 @@ +package org.hello.b + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class TestNothingBSuite extends FunSuite { + + test("nothing") { + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/WorldBSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/WorldBSuite.scala new file mode 100644 index 0000000..fdb8d68 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/b/src/test/scala/org/hello/b/WorldBSuite.scala @@ -0,0 +1,13 @@ +package org.hello.b + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldBSuite extends FunSuite { + + test("fooB") { + new WorldB().fooB() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/build.gradle new file mode 100644 index 0000000..805ce48 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'org.scoverage' +} + +allprojects { + repositories { + jcenter() + } +} + +description = 'a multi-module Scala project that builds successfully with 100% coverage' + +allprojects { + + apply plugin: 'java' + apply plugin: 'scala' + + dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + + testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion + } + + test { + useJUnitPlatform() + } + + checkScoverage { + minimumRate = 0.5 + } +} + +checkScoverage { + minimumRate = 0.5 +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/settings.gradle b/src/functionalTest/resources/projects/scala-multi-module/settings.gradle new file mode 100644 index 0000000..dd79722 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/settings.gradle @@ -0,0 +1 @@ +include 'a', 'b' \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/src/main/scala/org/hello/World.scala b/src/functionalTest/resources/projects/scala-multi-module/src/main/scala/org/hello/World.scala new file mode 100644 index 0000000..27dbe28 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/src/main/scala/org/hello/World.scala @@ -0,0 +1,9 @@ +package org.hello + +class World { + + def foo(): String = { + val s = "a" + "b" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/TestNothingSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/TestNothingSuite.scala new file mode 100644 index 0000000..1ac25b5 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/TestNothingSuite.scala @@ -0,0 +1,12 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class TestNothingSuite extends FunSuite { + + test("nothing") { + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/WorldSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/WorldSuite.scala new file mode 100644 index 0000000..7281a12 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/src/test/scala/org/hello/WorldSuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldSuite extends FunSuite { + + test("foo") { + new World().foo() + } +} \ 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 9e96781..1e40907 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -2,6 +2,7 @@ package org.scoverage import org.gradle.api.DefaultTask import org.gradle.api.provider.Property +import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import scoverage.report.CoverageAggregator @@ -9,28 +10,30 @@ class ScoverageAggregate extends DefaultTask { ScoverageRunner runner + @OutputDirectory + final Property reportDir = project.objects.property(File) + + final Property deleteReportsOnAggregation = project.objects.property(Boolean) + // TODO - consider separate options for `report` and `aggregate` tasks final Property coverageOutputCobertura = project.objects.property(Boolean) final Property coverageOutputXML = project.objects.property(Boolean) final Property coverageOutputHTML = project.objects.property(Boolean) final Property coverageDebug = project.objects.property(Boolean) - // TODO get these from extension - boolean clean = false - File reportDir - @TaskAction def aggregate() { runner.run { def rootDir = project.projectDir - def reportPath = reportDir ? reportDir : new File(project.buildDir, 'scoverage-aggregate') - def coverage = CoverageAggregator.aggregate(rootDir, clean) + def coverage = CoverageAggregator.aggregate(rootDir, deleteReportsOnAggregation.get()) + + reportDir.get().deleteDir() if (coverage.nonEmpty()) { ScoverageWriter.write( rootDir, - reportPath, + reportDir.get(), coverage.get(), coverageOutputCobertura.get(), coverageOutputXML.get(), diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 997b467..419224f 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -38,6 +38,8 @@ class ScoverageExtension { final Property coverageOutputHTML final Property coverageDebug + final Property deleteReportsOnAggregation + ScoverageExtension(Project project) { project.plugins.apply(JavaPlugin.class) @@ -78,5 +80,8 @@ class ScoverageExtension { coverageDebug = project.objects.property(Boolean) coverageDebug.set(false) + + deleteReportsOnAggregation = project.objects.property(Boolean) + deleteReportsOnAggregation.set(false) } } diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 9609619..418ffe3 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -1,6 +1,6 @@ package org.scoverage -import org.gradle.api.GradleException + import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.invocation.Gradle @@ -156,16 +156,19 @@ class ScoveragePlugin implements Plugin { private void configureAfterEvaluation(Project project, ScoverageExtension extension, ScoverageRunner scoverageRunner) { if (project.childProjects.size() > 0) { - def reportTasks = project.getSubprojects().collect { it.tasks.withType(ScoverageReport) } - project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { + def reportTasks = project.getAllprojects().collect { it.tasks.withType(ScoverageReport) } + def aggregationTask = project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { dependsOn(reportTasks) group = 'verification' runner = scoverageRunner + reportDir = extension.reportDir + deleteReportsOnAggregation = extension.deleteReportsOnAggregation coverageOutputCobertura = extension.coverageOutputCobertura coverageOutputXML = extension.coverageOutputXML coverageOutputHTML = extension.coverageOutputHTML coverageDebug = extension.coverageDebug } + project.tasks[CHECK_NAME].mustRunAfter(aggregationTask) } project.tasks[COMPILE_NAME].configure { diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index 3e53858..cb9c5d2 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -4,7 +4,7 @@ import org.gradle.api.DefaultTask import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import scala.collection.Seq import scala.collection.Set @@ -20,10 +20,12 @@ class ScoverageReport extends DefaultTask { @Input final Property dataDir = project.objects.property(File) - @OutputFile + @Input + final Property sources = project.objects.property(File) + + @OutputDirectory final Property reportDir = project.objects.property(File) - final Property sources = project.objects.property(File) final Property coverageOutputCobertura = project.objects.property(Boolean) final Property coverageOutputXML = project.objects.property(Boolean) final Property coverageOutputHTML = project.objects.property(Boolean) diff --git a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy index 90bdb51..2b425ff 100644 --- a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy +++ b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy @@ -21,6 +21,6 @@ class AggregationAcceptanceTest extends AcceptanceTestUtils { } private static File aggregateReportDir(File baseDir) { - return new File(baseDir, 'build/scoverage-aggregate') + return new File(baseDir, 'build/reports/scoverage') } } From 22be0bd56ac2bf9a886d62b4965a243694d38a9e Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Wed, 30 Jan 2019 17:45:18 +0200 Subject: [PATCH 05/17] Move the coverage XML validation to the functional tests and remove the "separate tests" use-case --- build.gradle | 14 ++-- .../org.scoverage/ScalaMultiModuleTest.java | 69 +++++++++++++++++-- .../org.scoverage/ScalaSingleModuleTest.java | 18 ++++- .../ScoverageFunctionalTest.java | 53 ++++++++++++++ .../org/scoverage/ScoverageExtension.groovy | 2 +- .../org/scoverage/ScoveragePlugin.groovy | 2 + .../org/scoverage/AcceptanceTestUtils.groovy | 47 ------------- .../AggregationAcceptanceTest.groovy | 26 ------- .../org/scoverage/OverallCheckTaskTest.groovy | 11 ++- .../SeparateTestsAcceptanceTest.groovy | 27 -------- .../SimpleReportAcceptanceTest.groovy | 25 ------- src/test/happy day/build.gradle | 19 ----- src/test/happy day/settings.gradle | 0 .../happy day/src/main/java/old/World.java | 7 -- .../happy day/src/main/resources/main.txt | 0 .../src/main/scala/hello/World.scala | 8 --- .../happy day/src/test/resources/test.txt | 0 .../src/test/scala/hello/ResourcesTest.scala | 14 ---- .../src/test/scala/hello/WorldTest.scala | 10 --- .../resources/{ => checkTask}/cobertura.xml | 0 .../resources/{ => checkTask}/scoverage.xml | 0 src/test/runtime/build.gradle | 20 ------ src/test/runtime/settings.gradle | 0 .../runtime/src/main/scala/hello/World.scala | 5 -- .../src/main/scala/hello/BaseTest.scala | 7 -- .../src/test/scala/hello/HelloTest.scala | 13 ---- .../a/src/main/scala/hello/Hello.scala | 5 -- src/test/separate-tests/build.gradle | 51 -------------- src/test/separate-tests/settings.gradle | 1 - .../src/main/scala/whales/BlueWhale.scala | 5 -- .../src/test/scala/whales/BlueWhaleTest.scala | 11 --- src/test/water/build.gradle | 19 ----- .../src/main/scala/krills/NorthernKrill.scala | 5 -- .../test/scala/krills/NorthernKrillTest.scala | 11 --- src/test/water/settings.gradle | 1 - 35 files changed, 147 insertions(+), 359 deletions(-) delete mode 100644 src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy delete mode 100644 src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy delete mode 100644 src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy delete mode 100644 src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy delete mode 100644 src/test/happy day/build.gradle delete mode 100644 src/test/happy day/settings.gradle delete mode 100644 src/test/happy day/src/main/java/old/World.java delete mode 100644 src/test/happy day/src/main/resources/main.txt delete mode 100644 src/test/happy day/src/main/scala/hello/World.scala delete mode 100644 src/test/happy day/src/test/resources/test.txt delete mode 100644 src/test/happy day/src/test/scala/hello/ResourcesTest.scala delete mode 100644 src/test/happy day/src/test/scala/hello/WorldTest.scala rename src/test/resources/{ => checkTask}/cobertura.xml (100%) rename src/test/resources/{ => checkTask}/scoverage.xml (100%) delete mode 100644 src/test/runtime/build.gradle delete mode 100644 src/test/runtime/settings.gradle delete mode 100644 src/test/runtime/src/main/scala/hello/World.scala delete mode 100644 src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala delete mode 100644 src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala delete mode 100644 src/test/separate-tests/a/src/main/scala/hello/Hello.scala delete mode 100644 src/test/separate-tests/build.gradle delete mode 100644 src/test/separate-tests/settings.gradle delete mode 100644 src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala delete mode 100644 src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala delete mode 100644 src/test/water/build.gradle delete mode 100644 src/test/water/krill/src/main/scala/krills/NorthernKrill.scala delete mode 100644 src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala delete mode 100644 src/test/water/settings.gradle diff --git a/build.gradle b/build.gradle index 4cbd2b9..606cc15 100644 --- a/build.gradle +++ b/build.gradle @@ -55,27 +55,25 @@ dependencies { sourceSets { functionalTest { - java { - srcDir file('src/functionalTest/java') - } - resources { - srcDir file('src/functionalTest/resources') - } + java.srcDir file('src/functionalTest/java') + resources.srcDir file('src/functionalTest/resources') compileClasspath += sourceSets.main.output + configurations.testRuntime runtimeClasspath += output + compileClasspath } } task functionalTest(type: Test) { + description = 'Runs the functional tests.' + group = 'verification' testClassesDirs = sourceSets.functionalTest.output.classesDirs classpath = sourceSets.functionalTest.runtimeClasspath + mustRunAfter test } -functionalTest.mustRunAfter(test) check.dependsOn functionalTest gradlePlugin { - testSourceSets sourceSets.test, sourceSets.functionalTest + testSourceSets sourceSets.functionalTest } task groovydocJar(type: Jar, dependsOn: groovydoc) { diff --git a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java index 92b427c..68d7a92 100644 --- a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java @@ -1,7 +1,10 @@ package org.scoverage; +import org.junit.Assert; import org.junit.Test; +import java.io.File; + public class ScalaMultiModuleTest extends ScoverageFunctionalTest { public ScalaMultiModuleTest() { @@ -92,9 +95,10 @@ public void checkScoverageOnlyA() { } @Test - public void checkAndAggregateScoverage() { + public void checkAndAggregateScoverage() throws Exception { - AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getAGGREGATE_NAME()); + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); @@ -103,10 +107,13 @@ public void checkAndAggregateScoverage() { result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(100.0); } @Test - public void checkScoverageWithoutCoverageInRoot() { + public void checkScoverageWithoutCoverageInRoot() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getTEST_NAME(), @@ -115,10 +122,13 @@ public void checkScoverageWithoutCoverageInRoot() { "--tests", "org.hello.b.WorldBSuite"); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); + + assertRootReportFilesExist(); + assertCoverage(0.0); } @Test - public void checkScoverageWithoutCoverageInA() { + public void checkScoverageWithoutCoverageInA() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getTEST_NAME(), @@ -127,10 +137,13 @@ public void checkScoverageWithoutCoverageInA() { "--tests", "org.hello.b.WorldBSuite"); result.assertTaskFailed("a:" + ScoveragePlugin.getCHECK_NAME()); + + assertAReportFilesExist(); + assertCoverage(0.0, reportDir(projectDir().toPath().resolve("a").toFile())); } @Test - public void checkAndAggregateScoverageWithoutCoverageInRoot() { + public void checkAndAggregateScoverageWithoutCoverageInRoot() throws Exception { // should pass as the check on the root is for the aggregation (which covers > 50%) @@ -147,10 +160,13 @@ public void checkAndAggregateScoverageWithoutCoverageInRoot() { result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(66.6); } @Test - public void checkAndAggregateScoverageWithoutCoverageInAll() { + public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(), @@ -163,5 +179,44 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() { result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); + + assertAllReportFilesExist(); + assertCoverage(0.0); + } + + private void assertAllReportFilesExist() { + + assertRootReportFilesExist(); + assertAReportFilesExist(); + assertBReportFilesExist(); + assertAggregationFilesExist(); + } + + private void assertAggregationFilesExist() { + + assertRootReportFilesExist(); + Assert.assertTrue(resolve(reportDir(), "a/src/main/scala/org/hello/a/WorldA.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "b/src/main/scala/org/hello/b/WorldB.scala.html").exists()); + } + + private void assertRootReportFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + } + + private void assertAReportFilesExist() { + + File aReportDir = reportDir(projectDir().toPath().resolve("a").toFile()); + Assert.assertTrue(resolve(aReportDir, "index.html").exists()); + Assert.assertTrue(resolve(aReportDir, "src/main/scala/org/hello/a/WorldA.scala.html").exists()); + } + + private void assertBReportFilesExist() { + + File bReportDir = reportDir(projectDir().toPath().resolve("b").toFile()); + Assert.assertTrue(resolve(bReportDir, "index.html").exists()); + Assert.assertTrue(resolve(bReportDir, "src/main/scala/org/hello/b/WorldB.scala.html").exists()); + } -} \ No newline at end of file +} diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index ea4823a..3734bc8 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -1,6 +1,6 @@ package org.scoverage; -import org.gradle.testkit.runner.TaskOutcome; +import org.junit.Assert; import org.junit.Test; public class ScalaSingleModuleTest extends ScoverageFunctionalTest { @@ -66,7 +66,7 @@ public void aggregateScoverage() { } @Test - public void checkScoverage() { + public void checkScoverage() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); @@ -75,10 +75,13 @@ public void checkScoverage() { result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + assertReportFilesExist(); + assertCoverage(100.0); } @Test - public void checkScoverageFails() { + public void checkScoverageFails() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getTEST_NAME(), "--tests", "org.hello.TestNothingSuite"); @@ -88,5 +91,14 @@ public void checkScoverageFails() { result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + assertReportFilesExist(); + assertCoverage(0.0); + } + + private void assertReportFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); } } \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java index eb64c61..a9481fe 100644 --- a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -1,21 +1,31 @@ package org.scoverage; +import groovy.util.Node; +import groovy.util.XmlParser; import org.gradle.testkit.runner.BuildResult; import org.gradle.testkit.runner.BuildTask; import org.gradle.testkit.runner.GradleRunner; import org.gradle.testkit.runner.TaskOutcome; import org.junit.Assert; +import org.xml.sax.SAXException; import java.io.File; +import java.io.IOException; +import java.text.NumberFormat; +import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; +import static org.hamcrest.number.IsCloseTo.closeTo; +import static org.junit.Assert.assertThat; + public abstract class ScoverageFunctionalTest { private final String projectName; private final GradleRunner runner; + private final XmlParser parser; protected ScoverageFunctionalTest(String projectName) { @@ -24,6 +34,14 @@ protected ScoverageFunctionalTest(String projectName) { .withProjectDir(projectDir()) .withPluginClasspath() .forwardOutput(); + + try { + this.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); + } catch (Exception e) { + throw new RuntimeException(e); + } } protected File projectDir() { @@ -31,6 +49,16 @@ protected File projectDir() { return new File("src/functionalTest/resources/projects/" + projectName); } + protected File reportDir() { + + return reportDir(projectDir()); + } + + protected File reportDir(File projectDir) { + + return projectDir.toPath().resolve("build").resolve(ScoveragePlugin.getDEFAULT_REPORT_DIR()).toFile(); + } + protected AssertableBuildResult run(String... arguments) { configureArguments(arguments); @@ -50,6 +78,31 @@ protected AssertableBuildResult dryRun(String... arguments) { return run(withDryArgument.toArray(new String[]{})); } + protected void assertCoverage(Double expected) throws Exception { + + assertCoverage(expected, reportDir()); + } + + protected void assertCoverage(Double expected, File reportDir) throws Exception { + + assertThat(coverage(reportDir, CoverageType.Statement), closeTo(expected, 1.0)); + assertThat(coverage(reportDir, CoverageType.Line), closeTo(expected, 1.0)); + } + + protected File resolve(File file, String relativePath) { + + return file.toPath().resolve(relativePath).toFile(); + } + + private Double coverage(File reportDir, CoverageType coverageType) throws IOException, SAXException, ParseException { + + File reportFile = reportDir.toPath().resolve(coverageType.getFileName()).toFile(); + Node xml = parser.parse(reportFile); + Object attribute = xml.attribute(coverageType.getParamName()); + double rawValue = NumberFormat.getInstance().parse(attribute.toString()).doubleValue(); + return coverageType.normalize(rawValue) * 100.0; + } + private void configureArguments(String... arguments) { List fullArguments = new ArrayList(); diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 419224f..ae99167 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -58,7 +58,7 @@ class ScoverageExtension { dataDir.set(new File(project.buildDir, 'scoverage')) reportDir = project.objects.property(File) - reportDir.set(new File(project.buildDir, 'reports' + File.separatorChar + 'scoverage')) + reportDir.set(new File(project.buildDir, ScoveragePlugin.DEFAULT_REPORT_DIR)) highlighting = project.objects.property(Boolean) highlighting.set(true) diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 418ffe3..9afe93c 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -21,6 +21,8 @@ class ScoveragePlugin implements Plugin { static final String COMPILE_NAME = 'compileScoverageScala' static final String AGGREGATE_NAME = 'aggregateScoverage' + static final String DEFAULT_REPORT_DIR = 'reports' + File.separatorChar + 'scoverage' + @Override void apply(PluginAware pluginAware) { if (pluginAware instanceof Project) { diff --git a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy b/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy deleted file mode 100644 index 14e22c2..0000000 --- a/src/test/groovy/org/scoverage/AcceptanceTestUtils.groovy +++ /dev/null @@ -1,47 +0,0 @@ -package org.scoverage - -import org.gradle.testkit.runner.GradleRunner -import org.hamcrest.core.Is -import org.junit.Assert - -import java.text.NumberFormat - -/** - * 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 void runBuild(File projectRoot, String... tasks) { - def runner = GradleRunner - .create() - .withProjectDir(projectRoot) - .withPluginClasspath() - .forwardOutput() - - runner.withArguments(tasks).build() - } - - 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') - } - - protected Double coverage(File reportDir, CoverageType coverageType) { - File reportFile = new File(reportDir, coverageType.fileName) - def xml = parser.parse(reportFile) - println("reportfile path: ${reportFile.absolutePath}") - NumberFormat nf = NumberFormat.getInstance(); - nf.parse(xml.attribute(coverageType.paramName) as String).doubleValue(); - } -} diff --git a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy b/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy deleted file mode 100644 index 2b425ff..0000000 --- a/src/test/groovy/org/scoverage/AggregationAcceptanceTest.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package org.scoverage - -import org.junit.Test - -class AggregationAcceptanceTest extends AcceptanceTestUtils { - - @Test - public void testMultiProjectAggregation() throws Exception { - File projectDir = new File('src/test/water') - runBuild(projectDir, 'clean', 'aggregateScoverage') - 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) - } - - private static File aggregateReportDir(File baseDir) { - return new File(baseDir, 'build/reports/scoverage') - } -} diff --git a/src/test/groovy/org/scoverage/OverallCheckTaskTest.groovy b/src/test/groovy/org/scoverage/OverallCheckTaskTest.groovy index fcc26ab..27c68dc 100644 --- a/src/test/groovy/org/scoverage/OverallCheckTaskTest.groovy +++ b/src/test/groovy/org/scoverage/OverallCheckTaskTest.groovy @@ -4,8 +4,11 @@ import org.gradle.api.GradleException import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.TypeSafeMatcher +import org.junit.Rule import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.nio.file.Paths import java.text.NumberFormat import static org.junit.Assert.assertNull @@ -43,7 +46,10 @@ class OverallCheckTaskTest { private NumberFormat numberFormat = NumberFormat.getInstance(Locale.US) - private static File reportDir = new File('src/test/resources') + private File reportDir = Paths.get(getClass().getClassLoader().getResource("checkTask").toURI()).toFile() + + @Rule + public TemporaryFolder tempDir = new TemporaryFolder() private static Matcher failsWith(String message) { return new CauseMatcher( @@ -57,7 +63,7 @@ class OverallCheckTaskTest { @Test void failsWhenReportFileIsNotFound() { assertThat( - checkLineCoverage(numberFormat, new File('src/test/nothingthere'), CoverageType.Line, 0.0), + checkLineCoverage(numberFormat, tempDir.getRoot(), CoverageType.Line, 0.0), failsWith(OverallCheckTask.fileNotFoundErrorMsg(CoverageType.Line))) } @@ -117,5 +123,4 @@ class OverallCheckTaskTest { void doesNotFailWhenBranchRateIsAboveTarget() { assertNull(checkLineCoverage(numberFormat, reportDir, CoverageType.Branch, 0.45)) } - } diff --git a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy deleted file mode 100644 index 4308937..0000000 --- a/src/test/groovy/org/scoverage/SeparateTestsAcceptanceTest.groovy +++ /dev/null @@ -1,27 +0,0 @@ -package org.scoverage - -import static org.hamcrest.number.IsCloseTo.closeTo -import org.junit.Test - -import static org.junit.Assert.assertThat - -class SeparateTestsAcceptanceTest extends AcceptanceTestUtils { - - @Test - public void testSeparateTestsWithZinc() throws Exception { - File projectDir = new File('src/test/separate-tests') - File subprojectDir = new File(projectDir, 'a') - File testsSubprojectDir = new File(projectDir, 'a-tests') - runBuild(projectDir, 'clean', 'reportScoverage') - def indexHtml = new File(reportDir(subprojectDir), 'index.html') - checkFile('an index HTML file', indexHtml, true) - def testsIndexHtml = new File(reportDir(testsSubprojectDir), 'index.html') - checkFile('an index HTML file', testsIndexHtml, false) - def helloHtml = new File(reportDir(subprojectDir), 'src/main/scala/hello/Hello.scala.html') - checkFile('Hello.scala html file', helloHtml, true) - def branchCoverage = coverage(reportDir(subprojectDir), CoverageType.Branch) - def statementCoverage = coverage(reportDir(subprojectDir), CoverageType.Statement) - 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)) - } -} diff --git a/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy b/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy deleted file mode 100644 index 0c2ca6c..0000000 --- a/src/test/groovy/org/scoverage/SimpleReportAcceptanceTest.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package org.scoverage - -import org.junit.Test - -class SimpleReportAcceptanceTest extends AcceptanceTestUtils { - - - @Test - public void testProjectWithCompleteCoverage() throws Exception { - File projectRoot = new File('src/test/happy day') - runBuild(projectRoot, 'clean', 'checkScoverage') - 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 testRun() throws Exception { - File projectRoot = new File('src/test/runtime') - runBuild(projectRoot, 'clean', 'run') - } -} diff --git a/src/test/happy day/build.gradle b/src/test/happy day/build.gradle deleted file mode 100644 index 17a1522..0000000 --- a/src/test/happy day/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'org.scoverage' -} - -description = 'a project that builds successfully with 100% coverage' - -repositories { - jcenter() -} - -dependencies { - compile 'org.scala-lang:scala-library:2.11.0' - testCompile 'junit:junit:4.11' -} - -checkScoverage { - minimumRate = 1.0 - coverageType = 'Line' -} \ No newline at end of file diff --git a/src/test/happy day/settings.gradle b/src/test/happy day/settings.gradle deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/happy day/src/main/java/old/World.java b/src/test/happy day/src/main/java/old/World.java deleted file mode 100644 index 97f710f..0000000 --- a/src/test/happy day/src/main/java/old/World.java +++ /dev/null @@ -1,7 +0,0 @@ -package old; - -public class World { - public String getMessage() { - return "Hello old boy"; - } -} \ No newline at end of file diff --git a/src/test/happy day/src/main/resources/main.txt b/src/test/happy day/src/main/resources/main.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/happy day/src/main/scala/hello/World.scala b/src/test/happy day/src/main/scala/hello/World.scala deleted file mode 100644 index cd1117d..0000000 --- a/src/test/happy day/src/main/scala/hello/World.scala +++ /dev/null @@ -1,8 +0,0 @@ -package hello - -object World { - def say() = { - println(new old.World().getMessage()) - println("ahoy") - } -} \ No newline at end of file diff --git a/src/test/happy day/src/test/resources/test.txt b/src/test/happy day/src/test/resources/test.txt deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/happy day/src/test/scala/hello/ResourcesTest.scala b/src/test/happy day/src/test/scala/hello/ResourcesTest.scala deleted file mode 100644 index fd5573b..0000000 --- a/src/test/happy day/src/test/scala/hello/ResourcesTest.scala +++ /dev/null @@ -1,14 +0,0 @@ -package hello - -import org.junit.Test - -class ResourcesTest { - @Test - def mainResourcesAreBuilt() { - assert(getClass.getResource("/main.txt") != null) - } - @Test - def testResourcesAreBuilt() { - assert(getClass.getResource("/test.txt") != null) - } -} \ No newline at end of file diff --git a/src/test/happy day/src/test/scala/hello/WorldTest.scala b/src/test/happy day/src/test/scala/hello/WorldTest.scala deleted file mode 100644 index a087efa..0000000 --- a/src/test/happy day/src/test/scala/hello/WorldTest.scala +++ /dev/null @@ -1,10 +0,0 @@ -package hello - -import org.junit.Test - -class WorldTest { - @Test - def bob() { - World.say() - } -} \ No newline at end of file diff --git a/src/test/resources/cobertura.xml b/src/test/resources/checkTask/cobertura.xml similarity index 100% rename from src/test/resources/cobertura.xml rename to src/test/resources/checkTask/cobertura.xml diff --git a/src/test/resources/scoverage.xml b/src/test/resources/checkTask/scoverage.xml similarity index 100% rename from src/test/resources/scoverage.xml rename to src/test/resources/checkTask/scoverage.xml diff --git a/src/test/runtime/build.gradle b/src/test/runtime/build.gradle deleted file mode 100644 index ec21818..0000000 --- a/src/test/runtime/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id 'org.scoverage' -} - -description = 'a project that runs an application and captures scoverage' - -repositories { - jcenter() -} - -dependencies { - compile 'org.scala-lang:scala-library:2.11.0' -} - -task run(type: JavaExec) { - classpath = sourceSets.scoverage.runtimeClasspath - main = 'hello.World' -} - -checkScoverage.dependsOn(run) \ No newline at end of file diff --git a/src/test/runtime/settings.gradle b/src/test/runtime/settings.gradle deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/runtime/src/main/scala/hello/World.scala b/src/test/runtime/src/main/scala/hello/World.scala deleted file mode 100644 index d90e2c9..0000000 --- a/src/test/runtime/src/main/scala/hello/World.scala +++ /dev/null @@ -1,5 +0,0 @@ -package hello - -object World extends App { - println("ahoy") -} \ No newline at end of file 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 deleted file mode 100644 index 12d82ae..0000000 --- a/src/test/separate-tests/a-tests/src/main/scala/hello/BaseTest.scala +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 1c7df23..0000000 --- a/src/test/separate-tests/a-tests/src/test/scala/hello/HelloTest.scala +++ /dev/null @@ -1,13 +0,0 @@ -package hello - -import org.junit.Test -import org.junit.Assert.assertEquals - -class HelloTest extends BaseTest { - - @Test def testText() { - beforeTest() - 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 deleted file mode 100644 index eabfc20..0000000 --- a/src/test/separate-tests/a/src/main/scala/hello/Hello.scala +++ /dev/null @@ -1,5 +0,0 @@ -package hello - -class Hello { - def text = "Hello World" -} diff --git a/src/test/separate-tests/build.gradle b/src/test/separate-tests/build.gradle deleted file mode 100644 index 1aeb9b8..0000000 --- a/src/test/separate-tests/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'org.scoverage' -} - -description = 'a multi-project with separate tests setup for gradle-scoverage' - -allprojects { - - repositories { - jcenter() - } -} - -subprojects { - - apply plugin: 'scala' - - dependencies { - compile 'org.scala-lang:scala-library:2.11.4' - - testCompile 'junit:junit:4.11' - } - - testScoverage { - onlyIf { project.name.endsWith('-tests') } - } - - reportScoverage { - 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.sourceSets.scoverage.output - } - scoverage { - sources = mainProject.extensions.scoverage.sources - dataDir = mainProject.extensions.scoverage.dataDir - reportDir = mainProject.extensions.scoverage.reportDir - } - compileScoverageScala { - onlyIf { false } - } -} \ No newline at end of file diff --git a/src/test/separate-tests/settings.gradle b/src/test/separate-tests/settings.gradle deleted file mode 100644 index 85eeb66..0000000 --- a/src/test/separate-tests/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':a', ':a-tests' diff --git a/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala b/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala deleted file mode 100644 index 2953e26..0000000 --- a/src/test/water/bluewhale/src/main/scala/whales/BlueWhale.scala +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 2e3d0be..0000000 --- a/src/test/water/bluewhale/src/test/scala/whales/BlueWhaleTest.scala +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index b9bb87c..0000000 --- a/src/test/water/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'org.scoverage' -} - -description = 'a multi-project setup for gradle-scoverage' - -allprojects { - repositories { - jcenter() - } - - apply plugin: 'scala' - - dependencies { - 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 deleted file mode 100644 index 16fdf22..0000000 --- a/src/test/water/krill/src/main/scala/krills/NorthernKrill.scala +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index bcb4dd7..0000000 --- a/src/test/water/krill/src/test/scala/krills/NorthernKrillTest.scala +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index a978eb9..0000000 --- a/src/test/water/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include 'bluewhale', 'krill' \ No newline at end of file From e11b33181f43d5912b0931d414df210a84ec912d Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Wed, 30 Jan 2019 18:46:26 +0200 Subject: [PATCH 06/17] Remove the "testScoverage" task and make "test" run the coverage (by adding the instrumented classes to its classpath) --- .../org.scoverage/ScalaMultiModuleTest.java | 8 +- .../org.scoverage/ScalaSingleModuleTest.java | 19 +-- .../org/scoverage/ScoveragePlugin.groovy | 149 ++++++++---------- 3 files changed, 75 insertions(+), 101 deletions(-) diff --git a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java index 68d7a92..18b5e0a 100644 --- a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java @@ -116,7 +116,7 @@ public void checkAndAggregateScoverage() throws Exception { public void checkScoverageWithoutCoverageInRoot() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), - ScoveragePlugin.getTEST_NAME(), + "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.WorldASuite", "--tests", "org.hello.b.WorldBSuite"); @@ -131,7 +131,7 @@ public void checkScoverageWithoutCoverageInRoot() throws Exception { public void checkScoverageWithoutCoverageInA() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), - ScoveragePlugin.getTEST_NAME(), + "test", "--tests", "org.hello.a.TestNothingASuite", "--tests", "org.hello.WorldSuite", "--tests", "org.hello.b.WorldBSuite"); @@ -148,7 +148,7 @@ public void checkAndAggregateScoverageWithoutCoverageInRoot() throws Exception { // should pass as the check on the root is for the aggregation (which covers > 50%) AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), - ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(), + ScoveragePlugin.getAGGREGATE_NAME(), "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.WorldASuite", "--tests", "org.hello.b.WorldBSuite"); @@ -169,7 +169,7 @@ public void checkAndAggregateScoverageWithoutCoverageInRoot() throws Exception { public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), - ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(), + ScoveragePlugin.getAGGREGATE_NAME(), "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.TestNothingASuite", "--tests", "org.hello.b.TestNothingBSuite"); diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index 3734bc8..e69603c 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -15,7 +15,6 @@ public void test() { AssertableBuildResult result = dryRun("clean", "test"); result.assertTaskDoesntExist(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskDoesntExist(ScoveragePlugin.getTEST_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); @@ -27,19 +26,6 @@ public void build() { AssertableBuildResult result = dryRun("clean", "build"); result.assertTaskDoesntExist(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskDoesntExist(ScoveragePlugin.getTEST_NAME()); - result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); - result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); - result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); - } - - @Test - public void testScoverage() { - - AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getTEST_NAME()); - - result.assertTaskExists(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskExists(ScoveragePlugin.getTEST_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); @@ -51,7 +37,6 @@ public void reportScoverage() { AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskExists(ScoveragePlugin.getTEST_NAME()); result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); @@ -71,7 +56,6 @@ public void checkScoverage() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); @@ -84,10 +68,9 @@ public void checkScoverage() throws Exception { public void checkScoverageFails() throws Exception { AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(), - ScoveragePlugin.getTEST_NAME(), "--tests", "org.hello.TestNothingSuite"); + "test", "--tests", "org.hello.TestNothingSuite"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); - result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 9afe93c..cce285e 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -1,21 +1,16 @@ package org.scoverage - import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.invocation.Gradle import org.gradle.api.plugins.PluginAware 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 -import java.util.concurrent.Callable - class ScoveragePlugin implements Plugin { static final String CONFIGURATION_NAME = 'scoverage' - static final String TEST_NAME = 'testScoverage' static final String REPORT_NAME = 'reportScoverage' static final String CHECK_NAME = 'checkScoverage' static final String COMPILE_NAME = 'compileScoverageScala' @@ -80,17 +75,12 @@ class ScoveragePlugin implements Plugin { } } - ScoverageRunner scoverageRunner = new ScoverageRunner(project.configurations.scoverage) - - createTasks(project, extension, scoverageRunner) - - project.afterEvaluate { - configureAfterEvaluation(project, extension, scoverageRunner) - } + createTasks(project, extension) } - private void createTasks(Project project, ScoverageExtension extension, ScoverageRunner scoverageRunner) { + private void createTasks(Project project, ScoverageExtension extension) { + ScoverageRunner scoverageRunner = new ScoverageRunner(project.configurations.scoverage) def instrumentedSourceSet = project.sourceSets.create('scoverage') { def original = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) @@ -103,8 +93,11 @@ class ScoveragePlugin implements Plugin { runtimeClasspath = it.output + project.configurations.scoverage + original.runtimeClasspath } + def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] + project.test.mustRunAfter(compileTask) + def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { - dependsOn('scoverageClasses') + dependsOn(instrumentedSourceSet.classesTaskName) classifier = CONFIGURATION_NAME from instrumentedSourceSet.output } @@ -112,29 +105,8 @@ class ScoveragePlugin implements Plugin { scoverage scoverageJar } - project.tasks.create(TEST_NAME, Test.class) { - conventionMapping.map("classpath", new Callable() { - Object call() throws Exception { - def testSourceSet = project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) - return testSourceSet.output + - instrumentedSourceSet.output + - project.configurations.scoverage + - testSourceSet.runtimeClasspath - } - }) - group = 'verification' - - FilenameFilter measurementFile = new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - return name.startsWith("scoverage.measurements.") - } - } - outputs.upToDateWhen { extension.dataDir.get().listFiles(measurementFile) } - } - - project.tasks.create(REPORT_NAME, ScoverageReport.class) { - dependsOn(project.tasks[TEST_NAME]) + def reportTask = project.tasks.create(REPORT_NAME, ScoverageReport.class) { + dependsOn compileTask, project.test onlyIf { extension.dataDir.get().list() } group = 'verification' runner = scoverageRunner @@ -148,58 +120,77 @@ class ScoveragePlugin implements Plugin { } project.tasks.create(CHECK_NAME, OverallCheckTask.class) { - dependsOn(project.tasks[REPORT_NAME]) + dependsOn(reportTask) group = 'verification' reportDir = extension.reportDir } - } + project.gradle.taskGraph.whenReady { graph -> + if (graph.hasTask(reportTask)) { - private void configureAfterEvaluation(Project project, ScoverageExtension extension, ScoverageRunner scoverageRunner) { - - if (project.childProjects.size() > 0) { - def reportTasks = project.getAllprojects().collect { it.tasks.withType(ScoverageReport) } - def aggregationTask = project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { - dependsOn(reportTasks) - group = 'verification' - runner = scoverageRunner - reportDir = extension.reportDir - deleteReportsOnAggregation = extension.deleteReportsOnAggregation - coverageOutputCobertura = extension.coverageOutputCobertura - coverageOutputXML = extension.coverageOutputXML - coverageOutputHTML = extension.coverageOutputHTML - coverageDebug = extension.coverageDebug + project.test.configure { + project.logger.debug("Adding instrumented classes to '${path}' classpath") + + classpath = project.configurations.scoverage + instrumentedSourceSet.output + classpath + + outputs.upToDateWhen { + extension.dataDir.get().listFiles(new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + return name.startsWith("scoverage.measurements.") + } + }) + } + } } - project.tasks[CHECK_NAME].mustRunAfter(aggregationTask) } - project.tasks[COMPILE_NAME].configure { - File pluginFile = project.configurations[CONFIGURATION_NAME].find { - it.name.startsWith("scalac-scoverage-plugin") - } - List parameters = ['-Xplugin:' + pluginFile.absolutePath] - List existingParameters = scalaCompileOptions.additionalParameters - if (existingParameters) { - parameters.addAll(existingParameters) - } - parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) - if (extension.excludedPackages.get()) { - def packages = extension.excludedPackages.get().join(';') - parameters.add("-P:scoverage:excludedPackages:$packages".toString()) - } - if (extension.excludedFiles.get()) { - def packages = extension.excludedFiles.get().join(';') - parameters.add("-P:scoverage:excludedFiles:$packages".toString()) - } - if (extension.highlighting.get()) { - parameters.add('-Yrangepos') + project.afterEvaluate { + + if (project.childProjects.size() > 0) { + def reportTasks = project.getAllprojects().collect { it.tasks.withType(ScoverageReport) } + def aggregationTask = project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { + dependsOn(reportTasks) + group = 'verification' + runner = scoverageRunner + reportDir = extension.reportDir + deleteReportsOnAggregation = extension.deleteReportsOnAggregation + coverageOutputCobertura = extension.coverageOutputCobertura + coverageOutputXML = extension.coverageOutputXML + coverageOutputHTML = extension.coverageOutputHTML + coverageDebug = extension.coverageDebug + } + project.tasks[CHECK_NAME].mustRunAfter(aggregationTask) } - doFirst { - GFileUtils.deleteDirectory(destinationDir) + + compileTask.configure { + File pluginFile = project.configurations[CONFIGURATION_NAME].find { + it.name.startsWith("scalac-scoverage-plugin") + } + List parameters = ['-Xplugin:' + pluginFile.absolutePath] + List existingParameters = scalaCompileOptions.additionalParameters + if (existingParameters) { + parameters.addAll(existingParameters) + } + parameters.add("-P:scoverage:dataDir:${extension.dataDir.get().absolutePath}".toString()) + if (extension.excludedPackages.get()) { + def packages = extension.excludedPackages.get().join(';') + parameters.add("-P:scoverage:excludedPackages:$packages".toString()) + } + if (extension.excludedFiles.get()) { + def packages = extension.excludedFiles.get().join(';') + parameters.add("-P:scoverage:excludedFiles:$packages".toString()) + } + if (extension.highlighting.get()) { + parameters.add('-Yrangepos') + } + doFirst { + GFileUtils.deleteDirectory(destinationDir) + } + scalaCompileOptions.additionalParameters = parameters + // the compile task creates a store of measured statements + outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) } - scalaCompileOptions.additionalParameters = parameters - // the compile task creates a store of measured statements - outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) } } } \ No newline at end of file From 0a6ec0b977872bc82d224b2983fa4fcc5bbebc29 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Thu, 31 Jan 2019 09:31:18 +0200 Subject: [PATCH 07/17] Make the plugin delete non-instrumented classes from the output directory --- build.gradle | 1 + .../org.scoverage/ScalaSingleModuleTest.java | 18 +++++ .../ScoverageFunctionalTest.java | 12 +++- .../projects/scala-single-module/build.gradle | 6 ++ .../org/scoverage/ScoveragePlugin.groovy | 66 +++++++++++++++---- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 606cc15..b9fe523 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,7 @@ targetCompatibility = '1.6' dependencies { compileOnly "org.scoverage:scalac-scoverage-plugin_2.12:1.3.1" + compile group: 'commons-io', name: 'commons-io', version: '2.6' testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3' } diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index e69603c..c7f9823 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -79,6 +79,24 @@ public void checkScoverageFails() throws Exception { assertCoverage(0.0); } + @Test + public void reportScoverageWithExcludedClasses() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), + "-PexcludedFile=.*"); + + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + assertCoverage(100.0); // coverage is 100 since no classes are covered + + Assert.assertFalse(resolve(buildDir(), "classes/scala/scoverage/org/hello/World.class").exists()); + } + private void assertReportFilesExist() { Assert.assertTrue(resolve(reportDir(), "index.html").exists()); diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java index a9481fe..27aab49 100644 --- a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -49,6 +49,16 @@ protected File projectDir() { return new File("src/functionalTest/resources/projects/" + projectName); } + protected File buildDir() { + + return buildDir(projectDir()); + } + + protected File buildDir(File projectDir) { + + return projectDir.toPath().resolve("build").toFile(); + } + protected File reportDir() { return reportDir(projectDir()); @@ -56,7 +66,7 @@ protected File reportDir() { protected File reportDir(File projectDir) { - return projectDir.toPath().resolve("build").resolve(ScoveragePlugin.getDEFAULT_REPORT_DIR()).toFile(); + return buildDir(projectDir).toPath().resolve(ScoveragePlugin.getDEFAULT_REPORT_DIR()).toFile(); } protected AssertableBuildResult run(String... arguments) { diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index b0a4ec6..5ef9ffe 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -26,4 +26,10 @@ test { checkScoverage { minimumRate = 0.5 +} + +if (hasProperty("excludedFile")) { + scoverage { + excludedFiles = [excludedFile] + } } \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index cce285e..cb4eef3 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -1,12 +1,16 @@ package org.scoverage +import org.apache.commons.io.FileUtils import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.invocation.Gradle import org.gradle.api.plugins.PluginAware import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.bundling.Jar -import org.gradle.util.GFileUtils + +import java.nio.file.Files + +import static groovy.io.FileType.FILES class ScoveragePlugin implements Plugin { @@ -82,18 +86,19 @@ class ScoveragePlugin implements Plugin { ScoverageRunner scoverageRunner = new ScoverageRunner(project.configurations.scoverage) + def originalSourceSet = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) def instrumentedSourceSet = project.sourceSets.create('scoverage') { - def original = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - resources.source(original.resources) - scala.source(original.java) - scala.source(original.scala) + resources.source(originalSourceSet.resources) + scala.source(originalSourceSet.java) + scala.source(originalSourceSet.scala) - compileClasspath += original.compileClasspath + project.configurations.scoverage - runtimeClasspath = it.output + project.configurations.scoverage + original.runtimeClasspath + compileClasspath += originalSourceSet.compileClasspath + project.configurations.scoverage + runtimeClasspath = it.output + project.configurations.scoverage + originalSourceSet.runtimeClasspath } def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] + compileTask.mustRunAfter(originalSourceSet.getCompileTaskName("scala")) project.test.mustRunAfter(compileTask) def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { @@ -127,7 +132,6 @@ class ScoveragePlugin implements Plugin { project.gradle.taskGraph.whenReady { graph -> if (graph.hasTask(reportTask)) { - project.test.configure { project.logger.debug("Adding instrumented classes to '${path}' classpath") @@ -164,6 +168,10 @@ class ScoveragePlugin implements Plugin { } compileTask.configure { + doFirst { + destinationDir.deleteDir() + } + File pluginFile = project.configurations[CONFIGURATION_NAME].find { it.name.startsWith("scalac-scoverage-plugin") } @@ -184,13 +192,49 @@ class ScoveragePlugin implements Plugin { if (extension.highlighting.get()) { parameters.add('-Yrangepos') } - doFirst { - GFileUtils.deleteDirectory(destinationDir) - } scalaCompileOptions.additionalParameters = parameters // the compile task creates a store of measured statements outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) + + doLast { + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath + } + } + } + + return files + } + + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + return FileUtils.contentEquals(fileA, fileB) + } + + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + return originalClasses.contains(relativePath) && isSameFile(relativePath) + }) + + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) + } + } } } } + + } \ No newline at end of file From ad39e96701b13e822ec9bcc848c28e0c8e02b739 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Thu, 31 Jan 2019 10:58:59 +0200 Subject: [PATCH 08/17] Allow running scoverage without running the normal compilation (only scoverage compilation) --- .../org.scoverage/ScalaSingleModuleTest.java | 38 +++++++- .../projects/scala-single-module/build.gradle | 12 +-- .../src/main/scala/org/hello/World.scala | 3 + .../org/scoverage/ScoverageExtension.groovy | 5 ++ .../org/scoverage/ScoveragePlugin.groovy | 88 ++++++++++--------- 5 files changed, 99 insertions(+), 47 deletions(-) diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index c7f9823..081fa9a 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -61,7 +61,7 @@ public void checkScoverage() throws Exception { result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); assertReportFilesExist(); - assertCoverage(100.0); + assertCoverage(50.0); } @Test @@ -94,6 +94,42 @@ public void reportScoverageWithExcludedClasses() throws Exception { Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); assertCoverage(100.0); // coverage is 100 since no classes are covered + // compiled class should exist in the default classes directory, but not in scoverage + Assert.assertTrue(resolve(buildDir(), "classes/scala/main/org/hello/World.class").exists()); + Assert.assertFalse(resolve(buildDir(), "classes/scala/scoverage/org/hello/World.class").exists()); + } + + @Test + public void reportScoverageWithoutNormalCompilation() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), + "-PrunNormalCompilation=false"); + + result.assertTaskSkipped("compileScala"); + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + assertReportFilesExist(); + assertCoverage(50.0); + + Assert.assertTrue(resolve(buildDir(), "classes/scala/main/org/hello/World.class").exists()); + Assert.assertFalse(resolve(buildDir(), "classes/scala/scoverage/org/hello/World.class").exists()); + } + + @Test + public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), + "-PrunNormalCompilation=false", "-PexcludedFile=.*"); + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + assertCoverage(100.0); // coverage is 100 since no classes are covered + + // compiled class should exist in the default classes directory, but not in scoverage + Assert.assertTrue(resolve(buildDir(), "classes/scala/main/org/hello/World.class").exists()); Assert.assertFalse(resolve(buildDir(), "classes/scala/scoverage/org/hello/World.class").exists()); } diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index 5ef9ffe..180988a 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -25,11 +25,13 @@ test { } checkScoverage { - minimumRate = 0.5 + minimumRate = 0.3 } if (hasProperty("excludedFile")) { - scoverage { - excludedFiles = [excludedFile] - } -} \ No newline at end of file + scoverage.excludedFiles = [excludedFile] +} +if (hasProperty("runNormalCompilation")) { + scoverage.runNormalCompilation = Boolean.valueOf(runNormalCompilation) +} + diff --git a/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala b/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala index 27dbe28..270482f 100644 --- a/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala +++ b/src/functionalTest/resources/projects/scala-single-module/src/main/scala/org/hello/World.scala @@ -6,4 +6,7 @@ class World { val s = "a" + "b" s } + + // not covered by tests + def bar(): String = "y" } \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index ae99167..f47232d 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -40,6 +40,8 @@ class ScoverageExtension { final Property deleteReportsOnAggregation + final Property runNormalCompilation + ScoverageExtension(Project project) { project.plugins.apply(JavaPlugin.class) @@ -83,5 +85,8 @@ class ScoverageExtension { deleteReportsOnAggregation = project.objects.property(Boolean) deleteReportsOnAggregation.set(false) + + runNormalCompilation = project.objects.property(Boolean) + runNormalCompilation.set(true) } } diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index cb4eef3..51f9b06 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -6,7 +6,6 @@ import org.gradle.api.Project import org.gradle.api.invocation.Gradle import org.gradle.api.plugins.PluginAware import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.bundling.Jar import java.nio.file.Files @@ -97,19 +96,13 @@ class ScoveragePlugin implements Plugin { runtimeClasspath = it.output + project.configurations.scoverage + originalSourceSet.runtimeClasspath } + def originalCompileTask = project.tasks[originalSourceSet.getCompileTaskName("scala")] + originalCompileTask.onlyIf { extension.runNormalCompilation.get() } + def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] - compileTask.mustRunAfter(originalSourceSet.getCompileTaskName("scala")) + compileTask.mustRunAfter(originalCompileTask) project.test.mustRunAfter(compileTask) - def scoverageJar = project.tasks.create('jarScoverage', Jar.class) { - dependsOn(instrumentedSourceSet.classesTaskName) - classifier = CONFIGURATION_NAME - from instrumentedSourceSet.output - } - project.artifacts { - scoverage scoverageJar - } - def reportTask = project.tasks.create(REPORT_NAME, ScoverageReport.class) { dependsOn compileTask, project.test onlyIf { extension.dataDir.get().list() } @@ -146,6 +139,12 @@ class ScoveragePlugin implements Plugin { }) } } + + if (!extension.runNormalCompilation.get()) { + project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) { + compileClasspath = instrumentedSourceSet.output + compileClasspath + } + } } } @@ -168,8 +167,12 @@ class ScoveragePlugin implements Plugin { } compileTask.configure { - doFirst { - destinationDir.deleteDir() + if (extension.runNormalCompilation.get()) { + doFirst { + destinationDir.deleteDir() + } + } else { + destinationDir = originalCompileTask.destinationDir } File pluginFile = project.configurations[CONFIGURATION_NAME].find { @@ -196,40 +199,43 @@ class ScoveragePlugin implements Plugin { // the compile task creates a store of measured statements outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) - doLast { - def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getCompileTaskName("scala") - def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir - - def findFiles = { File dir, Closure condition = null -> - def files = [] - - if (dir.exists()) { - dir.eachFileRecurse(FILES) { f -> - if (condition == null || condition(f)) { - def relativePath = dir.relativePath(f) - files << relativePath + if (extension.runNormalCompilation.get()) { + // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage + doLast { + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath + } } } - } - return files - } + return files + } - def isSameFile = { String relativePath -> - def fileA = new File(originalDestinationDir, relativePath) - def fileB = new File(destinationDir, relativePath) - return FileUtils.contentEquals(fileA, fileB) - } + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + return FileUtils.contentEquals(fileA, fileB) + } - def originalClasses = findFiles(originalDestinationDir) - def identicalInstrumentedClasses = findFiles(destinationDir, { f -> - def relativePath = destinationDir.relativePath(f) - return originalClasses.contains(relativePath) && isSameFile(relativePath) - }) + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + return originalClasses.contains(relativePath) && isSameFile(relativePath) + }) - identicalInstrumentedClasses.each { f -> - Files.deleteIfExists(destinationDir.toPath().resolve(f)) + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) + } } } } From 7e17f5362ef113292c74d14d8f036577880d6a2f Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Thu, 31 Jan 2019 11:44:24 +0200 Subject: [PATCH 09/17] Make the check-task flags configurable via the extension and not via the task itself --- .../resources/projects/scala-multi-module/build.gradle | 4 ++-- .../resources/projects/scala-single-module/build.gradle | 2 +- src/main/groovy/org/scoverage/OverallCheckTask.groovy | 6 +++--- src/main/groovy/org/scoverage/ScoverageExtension.groovy | 9 +++++++++ src/main/groovy/org/scoverage/ScoveragePlugin.groovy | 2 ++ 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/functionalTest/resources/projects/scala-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/build.gradle index 805ce48..7c751a7 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module/build.gradle @@ -28,11 +28,11 @@ allprojects { useJUnitPlatform() } - checkScoverage { + scoverage { minimumRate = 0.5 } } -checkScoverage { +scoverage { minimumRate = 0.5 } \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index 180988a..25a9a4e 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -24,7 +24,7 @@ test { useJUnitPlatform() } -checkScoverage { +scoverage { minimumRate = 0.3 } diff --git a/src/main/groovy/org/scoverage/OverallCheckTask.groovy b/src/main/groovy/org/scoverage/OverallCheckTask.groovy index 810c0ad..e56822a 100644 --- a/src/main/groovy/org/scoverage/OverallCheckTask.groovy +++ b/src/main/groovy/org/scoverage/OverallCheckTask.groovy @@ -44,8 +44,8 @@ enum CoverageType { class OverallCheckTask extends DefaultTask { /** Type of coverage to check. Available options: Line, Statement and Branch */ - CoverageType coverageType = CoverageType.Statement - double minimumRate = 0.75 + final Property coverageType = project.objects.property(CoverageType) + final Property minimumRate = project.objects.property(BigDecimal) final Property reportDir = project.objects.property(File) @@ -56,7 +56,7 @@ class OverallCheckTask extends DefaultTask { void requireLineCoverage() { NumberFormat nf = NumberFormat.getInstance(locale == null ? Locale.getDefault() : locale) - Exception failure = checkLineCoverage(nf, reportDir.get(), coverageType, minimumRate) + Exception failure = checkLineCoverage(nf, reportDir.get(), coverageType.get(), minimumRate.get().doubleValue()) if (failure) throw failure } diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index f47232d..148ea77 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -42,6 +42,9 @@ class ScoverageExtension { final Property runNormalCompilation + final Property coverageType + final Property minimumRate + ScoverageExtension(Project project) { project.plugins.apply(JavaPlugin.class) @@ -88,5 +91,11 @@ class ScoverageExtension { runNormalCompilation = project.objects.property(Boolean) runNormalCompilation.set(true) + + coverageType = project.objects.property(CoverageType) + coverageType.set(CoverageType.Statement) + + minimumRate = project.objects.property(BigDecimal) + minimumRate.set(0.75) } } diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 51f9b06..90afe16 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -120,6 +120,8 @@ class ScoveragePlugin implements Plugin { project.tasks.create(CHECK_NAME, OverallCheckTask.class) { dependsOn(reportTask) group = 'verification' + coverageType = extension.coverageType + minimumRate = extension.minimumRate reportDir = extension.reportDir } From 096d286c8217f5e9fc4c95293d7e55053e6b4cfd Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Thu, 31 Jan 2019 11:54:01 +0200 Subject: [PATCH 10/17] Update README.md with all of the latest changes --- README.md | 99 ++++++++++++++++++++----------------------------------- 1 file changed, 35 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 696adb5..d6a8c61 100644 --- a/README.md +++ b/README.md @@ -6,90 +6,61 @@ A plugin to enable the use of Scoverage in a gradle Scala project. Getting started --------------- -http://plugins.gradle.org/plugin/org.scoverage - -This creates an additional task `testScoverage` which will run tests against instrumented code. - -A further task `reportScoverage` produces XML and HTML reports for analysing test code coverage. - -You can configure the version of Scoverage that will be used. This plugin should be compatible with all 1+ versions. -```groovy -scoverage { - scoverageVersion = "1.3.1" - scoverageScalaVersion = "2.12" // will be overridden by the 'scala-library' version (if configured) -} -``` - -Then launch command : -`gradle reportScoverage` or `gradle checkScoverage` +http://plugins.gradle.org/plugin/org.scoverage 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. +1. `reportScoverage`: Produces XML and HTML reports for analysing test code coverage. -ReportScoverage ---------------- +2. `aggregateScoverage`: An experimental support for aggregating coverage statistics in composite builds. -You can configure output generated by `gradle reportScoverage` using flags: + When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`, which + will first generate reports for each project individually (including the parent project), and will then generate an + aggregated result based on these reports. -| 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. | + The aggregated report will override the parent-project specific report (`parent-project/build/reports/scoverage`). -Aggregating Reports -------------------- + One can still use `reportScoverage` in order to generate a report without aggregation. +3. `checkScoverage`: Validates coverage according status according the generated reports (aggregated or not). -There is now experimental support for aggregating coverage statistics in composite builds. + `gradle checkScoverage` will automatically invoke `reportScoverage` but it won't generate aggregated reports. + In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`. + +Configuration +--------------- -When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`, which -will first generate reports for each project individually (including the parent project), and will then generate an -aggregated result based on these reports. +The plugin exposes multiple options that can be configured by setting them in an `scoverage` block within the project's +build script. These options are as follows: -The aggregated report will override the parent-project specific report (`parent-project/build/reports/scoverage`). +You can configure the version of Scoverage that will be used. This plugin should -One can still use `reportScoverage` in order to generate a report without aggregation. +* `scoverageVersion = ` (default `"1.3.1"`): The version of the scoverage scalac plugin. This (gradle) plugin +should be compatible with all 1+ versions. -Aggregation uses same flags as reporting for enabling/disabling different output types. +* `scoverageScalaVersion = ` (default `"2.12"`): The scala version of the scoverage scalac plugin. This will +be overridden by the version of the `scala-library` compile dependency (if the dependency is configured). + +* `coverageOutputCobertura = ` (default `true`): Enables/disables cobertura.xml file generation (for both aggregated and non-aggregated reports). -CheckScoverage --------------- +* `coverageOutputXML = ` (default `true`): Enables/disables scoverage XML output (for both aggregated and non-aggregated reports). -The `checkScoverage` task validates coverage status according the generated reports. +* `coverageOutputHTML = ` (default `true`): Enables/disables scoverage HTML output (for both aggregated and non-aggregated reports). -`gradle checkScoverage` will automatically generate reports via `reportScoverage` but it won't generate aggregated reports. -In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`. +* `coverageDebug = ` (default `false`): Enables/disables scoverage debug output (for both aggregated and non-aggregated reports). -By default, when you launch `gradle checkScoverage` build fail if only 75% of statements in project is covered by tests. +* `minimumRate = ` (default `0.75`): The minimum amount of coverage in decimal proportion (`1.0` == 100%) +required for the validation to pass (otherwise `checkScoverage` will fail the build). -To configure it as you want, add this configuration : -``` -checkScoverage { - minimumRate = 0.5 -} -``` +* `coverageType = <"Statement" | "Branch" | "Line">` (default `"Statement"`): The type of coverage validated by the +`checkScoverage` task. For more information on the different types, please refer to the documentation of the scalac +plugin (https://github.com/scoverage/scalac-scoverage-plugin). -You can also modify type of value to check from `Statement`s to `Line`s or `Branch`es: +* `runNormalCompilation = ` (default `true`): Determines whether both normal scalac compilation (`compileScala`) +and compilation with scoverage (`compileScoverageScala`) should be executed, or if only the scoverage compilation should. +It may be helpful to turn this off so that only the scoverage instrumented classes -- which are not intended for release +-- will be created, thus reducing the build time. -``` -checkScoverage { - coverageType = 'Line' - minimumRate = 0.5 -} -``` -``` -checkScoverage { - coverageType = 'Branch' - minimumRate = 0.5 -} -``` From 1044b8a0dcb828f0749d946cdc09f2615d13f03d Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Fri, 1 Feb 2019 09:42:19 +0200 Subject: [PATCH 11/17] Move all system prints to logger --- .../org/scoverage/ScoverageAggregate.groovy | 2 +- .../org/scoverage/ScoverageReport.groovy | 2 +- .../groovy/org/scoverage/ScoverageWriter.java | 22 +++++++++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy index 1e40907..4094476 100644 --- a/src/main/groovy/org/scoverage/ScoverageAggregate.groovy +++ b/src/main/groovy/org/scoverage/ScoverageAggregate.groovy @@ -31,7 +31,7 @@ class ScoverageAggregate extends DefaultTask { reportDir.get().deleteDir() if (coverage.nonEmpty()) { - ScoverageWriter.write( + new ScoverageWriter(project.logger).write( rootDir, reportDir.get(), coverage.get(), diff --git a/src/main/groovy/org/scoverage/ScoverageReport.groovy b/src/main/groovy/org/scoverage/ScoverageReport.groovy index cb9c5d2..ed2d6db 100644 --- a/src/main/groovy/org/scoverage/ScoverageReport.groovy +++ b/src/main/groovy/org/scoverage/ScoverageReport.groovy @@ -50,7 +50,7 @@ class ScoverageReport extends DefaultTask { Set measurements = IOUtils.invoked(measurementFiles) coverage.apply(measurements) - ScoverageWriter.write( + new ScoverageWriter(project.logger).write( sources.get(), reportDir.get(), coverage, diff --git a/src/main/groovy/org/scoverage/ScoverageWriter.java b/src/main/groovy/org/scoverage/ScoverageWriter.java index 0d8ab2f..cf68851 100644 --- a/src/main/groovy/org/scoverage/ScoverageWriter.java +++ b/src/main/groovy/org/scoverage/ScoverageWriter.java @@ -1,5 +1,6 @@ package org.scoverage; +import org.gradle.api.logging.Logger; import scoverage.Constants; import scoverage.Coverage; import scoverage.report.CoberturaXmlWriter; @@ -15,6 +16,13 @@ */ public class ScoverageWriter { + private final Logger logger; + + public ScoverageWriter(Logger logger) { + + this.logger = logger; + } + /** * Generates all reports from given data. * @@ -26,7 +34,7 @@ public class ScoverageWriter { * @param coverageOutputHTML switch for Scoverage HTML output * @param coverageDebug switch for Scoverage Debug output */ - public static void write(File sourceDir, + public void write(File sourceDir, File reportDir, Coverage coverage, Boolean coverageOutputCobertura, @@ -34,13 +42,13 @@ public static void write(File sourceDir, Boolean coverageOutputHTML, Boolean coverageDebug) { - System.out.println("[scoverage] Generating scoverage reports..."); + logger.info("[scoverage] Generating scoverage reports..."); reportDir.mkdirs(); if (coverageOutputCobertura) { new CoberturaXmlWriter(sourceDir, reportDir).write(coverage); - System.out.println("[scoverage] Written Cobertura XML report to " + + logger.info("[scoverage] Written Cobertura XML report to " + reportDir.getAbsolutePath() + File.separator + "cobertura.xml"); @@ -48,13 +56,13 @@ public static void write(File sourceDir, if (coverageOutputXML) { new ScoverageXmlWriter(sourceDir, reportDir, /* debug = */ false).write(coverage); - System.out.println("[scoverage] Written XML report to " + + logger.info("[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 " + + logger.info("[scoverage] Written XML report with debug information to " + reportDir.getAbsolutePath() + File.separator + Constants.XMLReportFilenameWithDebug()); @@ -63,12 +71,12 @@ public static void write(File sourceDir, if (coverageOutputHTML) { new ScoverageHtmlWriter(sourceDir, reportDir).write(coverage); - System.out.println("[scoverage] Written HTML report to " + + logger.info("[scoverage] Written HTML report to " + reportDir.getAbsolutePath() + File.separator + "index.html"); } - System.out.println("[scoverage] Coverage reports completed"); + logger.info("[scoverage] Coverage reports completed"); } } From 67e510629a432df81f441942f37dcac764eaf729 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Fri, 1 Feb 2019 10:42:50 +0200 Subject: [PATCH 12/17] Fix checking without aggregation on multi-module with no source code in the root project --- src/main/groovy/org/scoverage/ScoveragePlugin.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 90afe16..923b65a 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -119,6 +119,7 @@ class ScoveragePlugin implements Plugin { project.tasks.create(CHECK_NAME, OverallCheckTask.class) { dependsOn(reportTask) + onlyIf { extension.reportDir.get().list() } group = 'verification' coverageType = extension.coverageType minimumRate = extension.minimumRate From 269b17c71c3e4c935105b2606272bab878366673 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Fri, 1 Feb 2019 10:45:53 +0200 Subject: [PATCH 13/17] Add a functional test for a multi-module project with multiple scala versions --- .../ScalaMultiModuleCrossVersionTest.java | 59 +++++++++++++++++++ .../2_11/build.gradle | 4 ++ .../src/main/scala/org/hello/World211.scala | 9 +++ .../test/scala/org/hello/World211Suite.scala | 13 ++++ .../2_12/build.gradle | 4 ++ .../src/main/scala/org/hello/World212.scala | 9 +++ .../test/scala/org/hello/World212Suite.scala | 13 ++++ .../build.gradle | 36 +++++++++++ .../settings.gradle | 1 + .../projects/scala-single-module/build.gradle | 2 +- 10 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/functionalTest/java/org.scoverage/ScalaMultiModuleCrossVersionTest.java create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/main/scala/org/hello/World211.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/test/scala/org/hello/World211Suite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/main/scala/org/hello/World212.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/test/scala/org/hello/World212Suite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module-cross-version/settings.gradle diff --git a/src/functionalTest/java/org.scoverage/ScalaMultiModuleCrossVersionTest.java b/src/functionalTest/java/org.scoverage/ScalaMultiModuleCrossVersionTest.java new file mode 100644 index 0000000..224ef34 --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScalaMultiModuleCrossVersionTest.java @@ -0,0 +1,59 @@ +package org.scoverage; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +public class ScalaMultiModuleCrossVersionTest extends ScoverageFunctionalTest { + + public ScalaMultiModuleCrossVersionTest() { + super("scala-multi-module-cross-version"); + } + + @Test + public void checkAndAggregateScoverage() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertTaskSkipped(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("2_11:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("2_12:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("2_11:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("2_12:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(100.0); + } + + private void assertAllReportFilesExist() { + + assert211ReportFilesExist(); + assert212ReportFilesExist(); + assertAggregationFilesExist(); + } + + private void assertAggregationFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertTrue(resolve(reportDir(), "2_11/src/main/scala/org/hello/World211.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "2_12/src/main/scala/org/hello/World212.scala.html").exists()); + } + + private void assert211ReportFilesExist() { + + File reportDir = reportDir(projectDir().toPath().resolve("2_11").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/World211.scala.html").exists()); + } + + private void assert212ReportFilesExist() { + + File reportDir = reportDir(projectDir().toPath().resolve("2_12").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/World212.scala.html").exists()); + } +} diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/build.gradle new file mode 100644 index 0000000..5421d5a --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/build.gradle @@ -0,0 +1,4 @@ +dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "2.11.12" + testCompile group: 'org.scalatest', name: "scalatest_2.11", version: scalatestVersion +} diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/main/scala/org/hello/World211.scala b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/main/scala/org/hello/World211.scala new file mode 100644 index 0000000..7c7f57b --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/main/scala/org/hello/World211.scala @@ -0,0 +1,9 @@ +package org.hello + +class World211 { + + def foo(): String = { + val s = "2" + "11" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/test/scala/org/hello/World211Suite.scala b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/test/scala/org/hello/World211Suite.scala new file mode 100644 index 0000000..f872840 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_11/src/test/scala/org/hello/World211Suite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class World211Suite extends FunSuite { + + test("foo") { + new World211().foo() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle new file mode 100644 index 0000000..0a8112a --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/build.gradle @@ -0,0 +1,4 @@ +dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "2.12.8" + testCompile group: 'org.scalatest', name: "scalatest_2.12", version: scalatestVersion +} diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/main/scala/org/hello/World212.scala b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/main/scala/org/hello/World212.scala new file mode 100644 index 0000000..0968efa --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/main/scala/org/hello/World212.scala @@ -0,0 +1,9 @@ +package org.hello + +class World212 { + + def foo(): String = { + val s = "2" + "12" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/test/scala/org/hello/World212Suite.scala b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/test/scala/org/hello/World212Suite.scala new file mode 100644 index 0000000..cd26ad3 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/2_12/src/test/scala/org/hello/World212Suite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class World212Suite extends FunSuite { + + test("foo") { + new World212().foo() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/build.gradle b/src/functionalTest/resources/projects/scala-multi-module-cross-version/build.gradle new file mode 100644 index 0000000..4a024a0 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'org.scoverage' +} + +allprojects { + repositories { + jcenter() + } +} + +description = 'a multi-module Scala project with multiple Scala versions that builds successfully with 100% coverage' + +allprojects { + + apply plugin: 'java' + apply plugin: 'scala' + + dependencies { + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + } + + test { + useJUnitPlatform() + } + + scoverage { + minimumRate = 0.5 + } +} + +scoverage { + minimumRate = 0.5 +} + +project(":2_11").tasks.reportScoverage.mustRunAfter(project(":2_12").tasks.reportScoverage) \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module-cross-version/settings.gradle b/src/functionalTest/resources/projects/scala-multi-module-cross-version/settings.gradle new file mode 100644 index 0000000..6d138e6 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module-cross-version/settings.gradle @@ -0,0 +1 @@ +include '2_11', '2_12' \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index 25a9a4e..fbe8e1e 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -6,7 +6,7 @@ repositories { jcenter() } -description = 'a single-module Scala project that builds successfully with 100% coverage' +description = 'a single-module Scala project that builds successfully with 50% coverage' apply plugin: 'java' apply plugin: 'scala' From 213239b5740c1ec6fac3d25b8cf9ec233f75f27e Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Fri, 1 Feb 2019 12:11:43 +0200 Subject: [PATCH 14/17] Add a functional test for a multi-module project mixed with both scala and java --- .../ScalaJavaMultiModuleTest.java | 62 +++++++++++++++++++ .../scala-java-multi-module/build.gradle | 32 ++++++++++ .../java_only/build.gradle | 6 ++ .../main/java/org/hello/WorldJavaOnly.java | 9 +++ .../java/org/hello/WorldJavaOnlyTest.java | 11 ++++ .../mixed_scala_java/build.gradle | 11 ++++ .../src/main/java/org/hello/WorldJava.java | 9 +++ .../src/main/scala/org/hello/WorldScala.scala | 9 +++ .../test/java/org/hello/WorldJavaTest.java | 11 ++++ .../scala/org/hello/WorldScalaSuite.scala | 13 ++++ .../scala_only/build.gradle | 10 +++ .../main/scala/org/hello/WorldScalaOnly.scala | 9 +++ .../scala/org/hello/WorldScalaOnlySuite.scala | 13 ++++ .../scala-java-multi-module/settings.gradle | 1 + 14 files changed, 206 insertions(+) create mode 100644 src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/java_only/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/main/java/org/hello/WorldJavaOnly.java create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/java/org/hello/WorldJava.java create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/scala_only/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/main/scala/org/hello/WorldScalaOnly.scala create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/test/scala/org/hello/WorldScalaOnlySuite.scala create mode 100644 src/functionalTest/resources/projects/scala-java-multi-module/settings.gradle diff --git a/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java new file mode 100644 index 0000000..ccbe91e --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java @@ -0,0 +1,62 @@ +package org.scoverage; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +public class ScalaJavaMultiModuleTest extends ScoverageFunctionalTest { + + public ScalaJavaMultiModuleTest() { + super("scala-java-multi-module"); + } + + @Test + public void checkAndAggregateScoverage() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), + ScoveragePlugin.getAGGREGATE_NAME()); + + result.assertTaskSkipped(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("scala_only:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("java_only:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("scala_only:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist("java_only:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(100.0); + } + + private void assertAllReportFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + + assertScalaOnlyReportFilesExist(); + assertMixedScalaJavaReportFilesExist(); + assertAggregationFilesExist(); + } + + private void assertAggregationFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "scala_only/src/main/scala/org/hello/WorldScalaOnly.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "mixed_scala_java/src/main/scala/org/hello/WorldScala.scala.html").exists()); + } + + private void assertScalaOnlyReportFilesExist() { + + File reportDir = reportDir(projectDir().toPath().resolve("scala_only").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/WorldScalaOnly.scala.html").exists()); + } + + private void assertMixedScalaJavaReportFilesExist() { + + File reportDir = reportDir(projectDir().toPath().resolve("mixed_scala_java").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/WorldScala.scala.html").exists()); + } +} diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle new file mode 100644 index 0000000..b486ee1 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'org.scoverage' apply false +} + +allprojects { + repositories { + jcenter() + } +} + +description = 'a multi-module Scala and Java project that builds successfully with 100% coverage' + +apply plugin: 'org.scoverage' + +allprojects { + + dependencies { + testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + } + + test { + useJUnitPlatform() + } + + scoverage { + minimumRate = 0.5 + } +} + +scoverage { + minimumRate = 0.5 +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/java_only/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/build.gradle new file mode 100644 index 0000000..1cb42eb --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'java' + +dependencies { + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion +} diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/main/java/org/hello/WorldJavaOnly.java b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/main/java/org/hello/WorldJavaOnly.java new file mode 100644 index 0000000..95c85de --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/main/java/org/hello/WorldJavaOnly.java @@ -0,0 +1,9 @@ +package org.hello; + +public class WorldJavaOnly { + + public String foo() { + String s = "java_only" + "a"; + return s; + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java new file mode 100644 index 0000000..68fd33d --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/java_only/src/test/java/org/hello/WorldJavaOnlyTest.java @@ -0,0 +1,11 @@ +package org.hello; + +import org.junit.Test; + +public class WorldJavaOnlyTest { + + @Test + public void foo() { + new WorldJavaOnly().foo(); + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle new file mode 100644 index 0000000..3aa23b7 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'java' +apply plugin: 'scala' +apply plugin: 'org.scoverage' + +dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + + testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion +} diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/java/org/hello/WorldJava.java b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/java/org/hello/WorldJava.java new file mode 100644 index 0000000..bcfd8cf --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/java/org/hello/WorldJava.java @@ -0,0 +1,9 @@ +package org.hello; + +public class WorldJava { + + public String foo() { + String s = "java" + "a"; + return s; + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala new file mode 100644 index 0000000..8cdded1 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/main/scala/org/hello/WorldScala.scala @@ -0,0 +1,9 @@ +package org.hello + +class WorldScala { + + def foo(): String = { + val s = "scala" + "a" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java new file mode 100644 index 0000000..a88e452 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/java/org/hello/WorldJavaTest.java @@ -0,0 +1,11 @@ +package org.hello; + +import org.junit.Test; + +public class WorldJavaTest { + + @Test + public void foo() { + new WorldJava().foo(); + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala new file mode 100644 index 0000000..4244420 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/src/test/scala/org/hello/WorldScalaSuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldScalaSuite extends FunSuite { + + test("foo") { + new WorldScala().foo() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/build.gradle new file mode 100644 index 0000000..97f6ee6 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'scala' +apply plugin: 'org.scoverage' + +dependencies { + compile group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + + testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion +} diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/main/scala/org/hello/WorldScalaOnly.scala b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/main/scala/org/hello/WorldScalaOnly.scala new file mode 100644 index 0000000..268c5a5 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/main/scala/org/hello/WorldScalaOnly.scala @@ -0,0 +1,9 @@ +package org.hello + +class WorldScalaOnly { + + def foo(): String = { + val s = "scala_only" + "a" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/test/scala/org/hello/WorldScalaOnlySuite.scala b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/test/scala/org/hello/WorldScalaOnlySuite.scala new file mode 100644 index 0000000..7601021 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/scala_only/src/test/scala/org/hello/WorldScalaOnlySuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldScalaOnlySuite extends FunSuite { + + test("foo") { + new WorldScalaOnly().foo() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/settings.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/settings.gradle new file mode 100644 index 0000000..d8268de --- /dev/null +++ b/src/functionalTest/resources/projects/scala-java-multi-module/settings.gradle @@ -0,0 +1 @@ +include 'java_only', 'scala_only', 'mixed_scala_java' \ No newline at end of file From 57e6d2771f3d6ad0d3648e4fc8366804509f05a9 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Fri, 1 Feb 2019 13:44:53 +0200 Subject: [PATCH 15/17] Add compatibility with spring's dependency management plugin --- ...laSingleModuleWithDepdencyManagerTest.java | 31 ++++++++++++++ .../build.gradle | 42 +++++++++++++++++++ .../settings.gradle | 0 .../src/main/scala/org/hello/World.scala | 9 ++++ .../src/test/scala/org/hello/WorldSuite.scala | 13 ++++++ .../org/scoverage/ScoveragePlugin.groovy | 14 ++++++- 6 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src/functionalTest/java/org.scoverage/ScalaSingleModuleWithDepdencyManagerTest.java create mode 100644 src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-single-module-dependency-manager/settings.gradle create mode 100644 src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/main/scala/org/hello/World.scala create mode 100644 src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/test/scala/org/hello/WorldSuite.scala diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleWithDepdencyManagerTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleWithDepdencyManagerTest.java new file mode 100644 index 0000000..f8548fe --- /dev/null +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleWithDepdencyManagerTest.java @@ -0,0 +1,31 @@ +package org.scoverage; + +import org.junit.Assert; +import org.junit.Test; + +public class ScalaSingleModuleWithDepdencyManagerTest extends ScoverageFunctionalTest { + + public ScalaSingleModuleWithDepdencyManagerTest() { + super("scala-single-module-dependency-manager"); + } + + @Test + public void checkScoverage() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); + + assertReportFilesExist(); + assertCoverage(100.0); + } + + private void assertReportFilesExist() { + + Assert.assertTrue(resolve(reportDir(), "index.html").exists()); + Assert.assertTrue(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle new file mode 100644 index 0000000..135d248 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/build.gradle @@ -0,0 +1,42 @@ +plugins { + id 'io.spring.dependency-management' version "1.0.4.RELEASE" + id 'org.scoverage' +} + +repositories { + jcenter() +} + +description = 'a single-module Scala project with dependency manager that builds successfully with 100% coverage' + +apply plugin: 'java' +apply plugin: 'scala' + + +dependencyManagement { + dependencies { + dependency group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}" + } +} + +dependencies { + compile group: 'org.scala-lang', name: 'scala-library' + + // scala compilation with the dependency management plugin needs this (otherwise compilation will fail) + zinc group: 'com.typesafe.zinc', name: 'zinc', version: '0.3.15' + zinc group: 'org.scala-lang', name: 'scala-library', version: '2.10.5' + + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion + testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion + + testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion +} + +test { + useJUnitPlatform() +} + +scoverage { + minimumRate = 0.3 +} + diff --git a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/settings.gradle b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/settings.gradle new file mode 100644 index 0000000..e69de29 diff --git a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/main/scala/org/hello/World.scala b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/main/scala/org/hello/World.scala new file mode 100644 index 0000000..27dbe28 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/main/scala/org/hello/World.scala @@ -0,0 +1,9 @@ +package org.hello + +class World { + + def foo(): String = { + val s = "a" + "b" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/test/scala/org/hello/WorldSuite.scala b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/test/scala/org/hello/WorldSuite.scala new file mode 100644 index 0000000..7281a12 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-single-module-dependency-manager/src/test/scala/org/hello/WorldSuite.scala @@ -0,0 +1,13 @@ +package org.hello + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldSuite extends FunSuite { + + test("foo") { + new World().foo() + } +} \ No newline at end of file diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 923b65a..6bd42d6 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -57,14 +57,24 @@ class ScoveragePlugin implements Plugin { project.afterEvaluate { def scoverageVersion = project.extensions.scoverage.scoverageVersion.get() - def scalaVersion = project.extensions.scoverage.scoverageScalaVersion.get() + def scalaVersion = null def scalaLibrary = project.configurations.compile.dependencies.find { it.group == "org.scala-lang" && it.name == "scala-library" } if (scalaLibrary != null) { - scalaVersion = scalaLibrary.version.substring(0, scalaLibrary.version.lastIndexOf(".")) + scalaVersion = scalaLibrary.version + } + + if (scalaVersion == null && project.pluginManager.hasPlugin("io.spring.dependency-management")) { + scalaVersion = project.dependencyManagement.compile.managedVersions["org.scala-lang:scala-library"] + } + + if (scalaVersion == null) { + scalaVersion = project.extensions.scoverage.scoverageScalaVersion.get() + } else { + scalaVersion = scalaVersion.substring(0, scalaVersion.lastIndexOf(".")) } def fullScoverageVersion = "$scalaVersion:$scoverageVersion" From ea988ae2dc9432978da0a1df4486af9d563545a6 Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Sun, 3 Feb 2019 15:47:54 +0200 Subject: [PATCH 16/17] Change the usage of running without normal compilation and fix it in multi-module projects with inner dependencies --- README.md | 14 +- .../ScalaJavaMultiModuleTest.java | 7 +- .../org.scoverage/ScalaMultiModuleTest.java | 96 ++++++++++-- .../org.scoverage/ScalaSingleModuleTest.java | 4 +- .../ScoverageFunctionalTest.java | 2 +- .../mixed_scala_java/build.gradle | 8 + .../scala-multi-module/a/build.gradle | 3 + .../a/src/main/scala/org/hello/a/WorldA.scala | 4 +- .../scala-multi-module/b/build.gradle | 3 + .../b/src/main/scala/org/hello/b/WorldB.scala | 4 +- .../projects/scala-multi-module/build.gradle | 4 - .../scala/org/hello/common/WorldCommon.scala | 9 ++ .../hello/common/TestNothingCommonSuite.scala | 12 ++ .../org/hello/common/WorldCommonSuite.scala | 13 ++ .../scala-multi-module/settings.gradle | 2 +- .../projects/scala-single-module/build.gradle | 4 - .../org/scoverage/ScoverageExtension.groovy | 5 - .../org/scoverage/ScoveragePlugin.groovy | 146 ++++++++++-------- 18 files changed, 238 insertions(+), 102 deletions(-) create mode 100644 src/functionalTest/resources/projects/scala-multi-module/a/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module/b/build.gradle create mode 100644 src/functionalTest/resources/projects/scala-multi-module/common/src/main/scala/org/hello/common/WorldCommon.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/TestNothingCommonSuite.scala create mode 100644 src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/WorldCommonSuite.scala diff --git a/README.md b/README.md index d6a8c61..c03c22c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Available tasks In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`. Configuration ---------------- +------------- The plugin exposes multiple options that can be configured by setting them in an `scoverage` block within the project's build script. These options are as follows: @@ -58,9 +58,13 @@ required for the validation to pass (otherwise `checkScoverage` will fail the bu `checkScoverage` task. For more information on the different types, please refer to the documentation of the scalac plugin (https://github.com/scoverage/scalac-scoverage-plugin). -* `runNormalCompilation = ` (default `true`): Determines whether both normal scalac compilation (`compileScala`) -and compilation with scoverage (`compileScoverageScala`) should be executed, or if only the scoverage compilation should. -It may be helpful to turn this off so that only the scoverage instrumented classes -- which are not intended for release --- will be created, thus reducing the build time. +Run without normal compilation +------------------------------ +By default, running any of the plugin tasks will compile the code both using "normal" compilation (`compileScala`) +and using the scoverage scalac plugin (`compileScoverageScala`). +In cases where you only wish to generate reports / validate coverage, but are not interested in publishing the code, +it is possible to only compile the code with the scoverage scalac plugin, thus reducing build times significantly. +In order to do so, simply add the arguments `-x compileScala` to the gradle execution. +For example: `gradle reportScoverage -x compileScala`. \ No newline at end of file diff --git a/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java index ccbe91e..0c6156e 100644 --- a/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaJavaMultiModuleTest.java @@ -1,5 +1,6 @@ package org.scoverage; +import org.gradle.testkit.runner.TaskOutcome; import org.junit.Assert; import org.junit.Test; @@ -17,14 +18,16 @@ public void checkAndAggregateScoverage() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskOutcome("java_only:" + ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.NO_SOURCE); + result.assertTaskSkipped(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("scala_only:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getREPORT_NAME()); - result.assertTaskDoesntExist("java_only:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSkipped("java_only:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("scala_only:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("mixed_scala_java:" + ScoveragePlugin.getCHECK_NAME()); - result.assertTaskDoesntExist("java_only:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSkipped("java_only:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); assertAllReportFilesExist(); diff --git a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java index 18b5e0a..5818ca5 100644 --- a/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaMultiModuleTest.java @@ -19,6 +19,7 @@ public void reportScoverage() { result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); } @Test @@ -29,6 +30,7 @@ public void reportScoverageOnlyRoot() { result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("common:" + ScoveragePlugin.getREPORT_NAME()); } @Test @@ -39,6 +41,7 @@ public void reportScoverageOnlyA() { result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("common:" + ScoveragePlugin.getREPORT_NAME()); } @Test @@ -50,6 +53,7 @@ public void aggregateScoverage() { result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists(ScoveragePlugin.getAGGREGATE_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); } @Test @@ -60,9 +64,11 @@ public void checkScoverage() { result.assertTaskExists(ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists(ScoveragePlugin.getCHECK_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskExists("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskExists("common:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } @@ -88,9 +94,11 @@ public void checkScoverageOnlyA() { result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskDoesntExist("common:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME()); result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskDoesntExist("common:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME()); } @@ -103,9 +111,11 @@ public void checkAndAggregateScoverage() throws Exception { result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); assertAllReportFilesExist(); @@ -119,7 +129,8 @@ public void checkScoverageWithoutCoverageInRoot() throws Exception { "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.WorldASuite", - "--tests", "org.hello.b.WorldBSuite"); + "--tests", "org.hello.b.WorldBSuite", + "--tests", "org.hello.common.WorldCommonSuite"); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); @@ -134,7 +145,8 @@ public void checkScoverageWithoutCoverageInA() throws Exception { "test", "--tests", "org.hello.a.TestNothingASuite", "--tests", "org.hello.WorldSuite", - "--tests", "org.hello.b.WorldBSuite"); + "--tests", "org.hello.b.WorldBSuite", + "--tests", "org.hello.common.WorldCommonSuite"); result.assertTaskFailed("a:" + ScoveragePlugin.getCHECK_NAME()); @@ -142,6 +154,21 @@ public void checkScoverageWithoutCoverageInA() throws Exception { assertCoverage(0.0, reportDir(projectDir().toPath().resolve("a").toFile())); } + @Test + public void checkScoverageWithoutNormalCompilationAndWithoutCoverageInCommon() throws Exception { + + AssertableBuildResult result = runAndFail("clean", + ":a:test", + ":common:test", "--tests", "org.hello.common.TestNothingCommonSuite", + "-x", "compileScala", + ScoveragePlugin.getCHECK_NAME()); + + result.assertTaskFailed("common:" + ScoveragePlugin.getCHECK_NAME()); + + assertCommonReportFilesExist(); + assertCoverage(0.0, reportDir(projectDir().toPath().resolve("common").toFile())); + } + @Test public void checkAndAggregateScoverageWithoutCoverageInRoot() throws Exception { @@ -151,18 +178,21 @@ public void checkAndAggregateScoverageWithoutCoverageInRoot() throws Exception { ScoveragePlugin.getAGGREGATE_NAME(), "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.WorldASuite", - "--tests", "org.hello.b.WorldBSuite"); + "--tests", "org.hello.b.WorldBSuite", + "--tests", "org.hello.common.WorldCommonSuite"); result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getCHECK_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); assertAllReportFilesExist(); - assertCoverage(66.6); + assertCoverage(87.5); } @Test @@ -172,11 +202,13 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { ScoveragePlugin.getAGGREGATE_NAME(), "test", "--tests", "org.hello.TestNothingSuite", "--tests", "org.hello.a.TestNothingASuite", - "--tests", "org.hello.b.TestNothingBSuite"); + "--tests", "org.hello.b.TestNothingBSuite", + "--tests", "org.hello.common.TestNothingCommonSuite"); result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getREPORT_NAME()); result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME()); @@ -184,19 +216,53 @@ public void checkAndAggregateScoverageWithoutCoverageInAll() throws Exception { assertCoverage(0.0); } + @Test + public void aggregateScoverageWithoutNormalCompilation() throws Exception { + + AssertableBuildResult result = run("clean", ScoveragePlugin.getAGGREGATE_NAME(), + "-x", "compileScala"); + + result.assertTaskSkipped("compileScala"); + result.assertTaskSkipped("a:compileScala"); + result.assertTaskSkipped("b:compileScala"); + result.assertTaskSkipped("common:compileScala"); + result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getCOMPILE_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded("common:" + ScoveragePlugin.getREPORT_NAME()); + result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME()); + + assertAllReportFilesExist(); + assertCoverage(100.0); + + Assert.assertTrue(resolve(buildDir(resolve(projectDir(), "a")), "classes/scala/main/org/hello/a/WorldA.class").exists()); + Assert.assertFalse(resolve(buildDir(resolve(projectDir(), "a")), "classes/scala/scoverage/org/hello/a/WorldA.class").exists()); + + Assert.assertTrue(resolve(buildDir(resolve(projectDir(), "b")), "classes/scala/main/org/hello/b/WorldB.class").exists()); + Assert.assertFalse(resolve(buildDir(resolve(projectDir(), "b")), "classes/scala/scoverage/org/hello/b/WorldB.class").exists()); + + Assert.assertTrue(resolve(buildDir(resolve(projectDir(), "common")), "classes/scala/main/org/hello/common/WorldCommon.class").exists()); + Assert.assertFalse(resolve(buildDir(resolve(projectDir(), "common")), "classes/scala/scoverage/org/hello/common/WorldCommon.class").exists()); + } + private void assertAllReportFilesExist() { assertRootReportFilesExist(); assertAReportFilesExist(); assertBReportFilesExist(); + assertCommonReportFilesExist(); assertAggregationFilesExist(); } private void assertAggregationFilesExist() { - assertRootReportFilesExist(); Assert.assertTrue(resolve(reportDir(), "a/src/main/scala/org/hello/a/WorldA.scala.html").exists()); Assert.assertTrue(resolve(reportDir(), "b/src/main/scala/org/hello/b/WorldB.scala.html").exists()); + Assert.assertTrue(resolve(reportDir(), "common/src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); } private void assertRootReportFilesExist() { @@ -207,16 +273,22 @@ private void assertRootReportFilesExist() { private void assertAReportFilesExist() { - File aReportDir = reportDir(projectDir().toPath().resolve("a").toFile()); - Assert.assertTrue(resolve(aReportDir, "index.html").exists()); - Assert.assertTrue(resolve(aReportDir, "src/main/scala/org/hello/a/WorldA.scala.html").exists()); + File reportDir = reportDir(projectDir().toPath().resolve("a").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/a/WorldA.scala.html").exists()); } private void assertBReportFilesExist() { - File bReportDir = reportDir(projectDir().toPath().resolve("b").toFile()); - Assert.assertTrue(resolve(bReportDir, "index.html").exists()); - Assert.assertTrue(resolve(bReportDir, "src/main/scala/org/hello/b/WorldB.scala.html").exists()); + File reportDir = reportDir(projectDir().toPath().resolve("b").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/b/WorldB.scala.html").exists()); + } + + private void assertCommonReportFilesExist() { + File reportDir = reportDir(projectDir().toPath().resolve("common").toFile()); + Assert.assertTrue(resolve(reportDir, "index.html").exists()); + Assert.assertTrue(resolve(reportDir, "src/main/scala/org/hello/common/WorldCommon.scala.html").exists()); } } diff --git a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java index 081fa9a..f93e184 100644 --- a/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java +++ b/src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java @@ -103,7 +103,7 @@ public void reportScoverageWithExcludedClasses() throws Exception { public void reportScoverageWithoutNormalCompilation() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PrunNormalCompilation=false"); + "-x", "compileScala"); result.assertTaskSkipped("compileScala"); result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME()); @@ -122,7 +122,7 @@ public void reportScoverageWithoutNormalCompilation() throws Exception { public void reportScoverageWithoutNormalCompilationAndWithExcludedClasses() throws Exception { AssertableBuildResult result = run("clean", ScoveragePlugin.getREPORT_NAME(), - "-PrunNormalCompilation=false", "-PexcludedFile=.*"); + "-PexcludedFile=.*", "-x", "compileScala"); Assert.assertTrue(resolve(reportDir(), "index.html").exists()); Assert.assertFalse(resolve(reportDir(), "src/main/scala/org/hello/World.scala.html").exists()); diff --git a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java index 27aab49..af14ce5 100644 --- a/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java +++ b/src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java @@ -193,7 +193,7 @@ private String fullTaskName(String taskName) { private boolean taskExists(String taskName) { - Pattern regex = Pattern.compile("^" + fullTaskName(taskName), Pattern.MULTILINE); + Pattern regex = Pattern.compile("^(> Task )?" + fullTaskName(taskName), Pattern.MULTILINE); return regex.matcher(result.getOutput()).find(); } } diff --git a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle index 3aa23b7..9d6a7ab 100644 --- a/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle +++ b/src/functionalTest/resources/projects/scala-java-multi-module/mixed_scala_java/build.gradle @@ -9,3 +9,11 @@ dependencies { testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion } + +// A common practice in mixed java/scala modules to make Java code able to import Scala code +ext.configureSources = { set, name -> + set.scala.srcDir("src/$name/java") + set.java.srcDirs = [] +} +configureSources(sourceSets.main, 'main') +configureSources(sourceSets.test, 'test') \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/a/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/a/build.gradle new file mode 100644 index 0000000..054045f --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/a/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":common") +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala b/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala index 301f445..1d7b2a1 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala +++ b/src/functionalTest/resources/projects/scala-multi-module/a/src/main/scala/org/hello/a/WorldA.scala @@ -1,9 +1,11 @@ package org.hello.a +import org.hello.common.WorldCommon + class WorldA { def fooA(): String = { - val s = "a" + "a" + val s = "a" + new WorldCommon().fooCommon() s } } \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/b/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/b/build.gradle new file mode 100644 index 0000000..054045f --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/b/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":common") +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala b/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala index bb7a579..5c8bb57 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala +++ b/src/functionalTest/resources/projects/scala-multi-module/b/src/main/scala/org/hello/b/WorldB.scala @@ -1,9 +1,11 @@ package org.hello.b +import org.hello.common.WorldCommon + class WorldB { def fooB(): String = { - val s = "b" + "b" + val s = "b" + new WorldCommon().fooCommon() s } } \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/build.gradle b/src/functionalTest/resources/projects/scala-multi-module/build.gradle index 7c751a7..dcfad1a 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module/build.gradle @@ -31,8 +31,4 @@ allprojects { scoverage { minimumRate = 0.5 } -} - -scoverage { - minimumRate = 0.5 } \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/common/src/main/scala/org/hello/common/WorldCommon.scala b/src/functionalTest/resources/projects/scala-multi-module/common/src/main/scala/org/hello/common/WorldCommon.scala new file mode 100644 index 0000000..494ab34 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/common/src/main/scala/org/hello/common/WorldCommon.scala @@ -0,0 +1,9 @@ +package org.hello.common + +class WorldCommon { + + def fooCommon(): String = { + val s = "common" + "a" + s + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/TestNothingCommonSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/TestNothingCommonSuite.scala new file mode 100644 index 0000000..970cd6c --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/TestNothingCommonSuite.scala @@ -0,0 +1,12 @@ +package org.hello.common + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class TestNothingCommonSuite extends FunSuite { + + test("nothing") { + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/WorldCommonSuite.scala b/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/WorldCommonSuite.scala new file mode 100644 index 0000000..94f8984 --- /dev/null +++ b/src/functionalTest/resources/projects/scala-multi-module/common/src/test/scala/org/hello/common/WorldCommonSuite.scala @@ -0,0 +1,13 @@ +package org.hello.common + +import org.junit.runner.RunWith +import org.scalatest.FunSuite +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class WorldCommonSuite extends FunSuite { + + test("fooCommon") { + new WorldCommon().fooCommon() + } +} \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-multi-module/settings.gradle b/src/functionalTest/resources/projects/scala-multi-module/settings.gradle index dd79722..6eabe3b 100644 --- a/src/functionalTest/resources/projects/scala-multi-module/settings.gradle +++ b/src/functionalTest/resources/projects/scala-multi-module/settings.gradle @@ -1 +1 @@ -include 'a', 'b' \ No newline at end of file +include 'a', 'b', 'common' \ No newline at end of file diff --git a/src/functionalTest/resources/projects/scala-single-module/build.gradle b/src/functionalTest/resources/projects/scala-single-module/build.gradle index fbe8e1e..6ae71be 100644 --- a/src/functionalTest/resources/projects/scala-single-module/build.gradle +++ b/src/functionalTest/resources/projects/scala-single-module/build.gradle @@ -31,7 +31,3 @@ scoverage { if (hasProperty("excludedFile")) { scoverage.excludedFiles = [excludedFile] } -if (hasProperty("runNormalCompilation")) { - scoverage.runNormalCompilation = Boolean.valueOf(runNormalCompilation) -} - diff --git a/src/main/groovy/org/scoverage/ScoverageExtension.groovy b/src/main/groovy/org/scoverage/ScoverageExtension.groovy index 148ea77..8ee680b 100644 --- a/src/main/groovy/org/scoverage/ScoverageExtension.groovy +++ b/src/main/groovy/org/scoverage/ScoverageExtension.groovy @@ -40,8 +40,6 @@ class ScoverageExtension { final Property deleteReportsOnAggregation - final Property runNormalCompilation - final Property coverageType final Property minimumRate @@ -89,9 +87,6 @@ class ScoverageExtension { deleteReportsOnAggregation = project.objects.property(Boolean) deleteReportsOnAggregation.set(false) - runNormalCompilation = project.objects.property(Boolean) - runNormalCompilation.set(true) - coverageType = project.objects.property(CoverageType) coverageType.set(CoverageType.Statement) diff --git a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy index 6bd42d6..7c6ac63 100644 --- a/src/main/groovy/org/scoverage/ScoveragePlugin.groovy +++ b/src/main/groovy/org/scoverage/ScoveragePlugin.groovy @@ -3,9 +3,11 @@ package org.scoverage import org.apache.commons.io.FileUtils import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.invocation.Gradle import org.gradle.api.plugins.PluginAware import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.scala.ScalaCompile import java.nio.file.Files @@ -99,7 +101,6 @@ class ScoveragePlugin implements Plugin { def instrumentedSourceSet = project.sourceSets.create('scoverage') { resources.source(originalSourceSet.resources) - scala.source(originalSourceSet.java) scala.source(originalSourceSet.scala) compileClasspath += originalSourceSet.compileClasspath + project.configurations.scoverage @@ -107,11 +108,12 @@ class ScoveragePlugin implements Plugin { } def originalCompileTask = project.tasks[originalSourceSet.getCompileTaskName("scala")] - originalCompileTask.onlyIf { extension.runNormalCompilation.get() } + def originalJarTask = project.tasks[originalSourceSet.getJarTaskName()] def compileTask = project.tasks[instrumentedSourceSet.getCompileTaskName("scala")] compileTask.mustRunAfter(originalCompileTask) project.test.mustRunAfter(compileTask) + originalJarTask.mustRunAfter(compileTask) def reportTask = project.tasks.create(REPORT_NAME, ScoverageReport.class) { dependsOn compileTask, project.test @@ -136,33 +138,9 @@ class ScoveragePlugin implements Plugin { reportDir = extension.reportDir } - project.gradle.taskGraph.whenReady { graph -> - if (graph.hasTask(reportTask)) { - project.test.configure { - project.logger.debug("Adding instrumented classes to '${path}' classpath") - - classpath = project.configurations.scoverage + instrumentedSourceSet.output + classpath - - outputs.upToDateWhen { - extension.dataDir.get().listFiles(new FilenameFilter() { - @Override - boolean accept(File dir, String name) { - return name.startsWith("scoverage.measurements.") - } - }) - } - } - - if (!extension.runNormalCompilation.get()) { - project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) { - compileClasspath = instrumentedSourceSet.output + compileClasspath - } - } - } - } - project.afterEvaluate { + // define aggregation task if (project.childProjects.size() > 0) { def reportTasks = project.getAllprojects().collect { it.tasks.withType(ScoverageReport) } def aggregationTask = project.tasks.create(AGGREGATE_NAME, ScoverageAggregate.class) { @@ -179,15 +157,23 @@ class ScoveragePlugin implements Plugin { project.tasks[CHECK_NAME].mustRunAfter(aggregationTask) } - compileTask.configure { - if (extension.runNormalCompilation.get()) { - doFirst { - destinationDir.deleteDir() - } - } else { - destinationDir = originalCompileTask.destinationDir + // make this project's scoverage compilation depend on scoverage compilation of any other project + // which this project depends on its normal compilation + // (essential when running without normal compilation on multi-module projects with inner dependencies) + def originalCompilationDependencies = recursiveDependenciesOf(compileTask).findAll { + it instanceof ScalaCompile + } + originalCompilationDependencies.each { + def dependencyProjectCompileTask = it.project.tasks[COMPILE_NAME] + def dependencyProjectReportTask = it.project.tasks[REPORT_NAME] + if (dependencyProjectCompileTask != null) { + compileTask.dependsOn(dependencyProjectCompileTask) + // we don't want this project's test to affect the other project's report + project.test.mustRunAfter(dependencyProjectReportTask) } + } + compileTask.configure { File pluginFile = project.configurations[CONFIGURATION_NAME].find { it.name.startsWith("scalac-scoverage-plugin") } @@ -211,43 +197,70 @@ class ScoveragePlugin implements Plugin { scalaCompileOptions.additionalParameters = parameters // the compile task creates a store of measured statements outputs.file(new File(extension.dataDir.get(), 'scoverage.coverage.xml')) + } + } - if (extension.runNormalCompilation.get()) { - // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage - doLast { - def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - .getCompileTaskName("scala") - def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir - - def findFiles = { File dir, Closure condition = null -> - def files = [] - - if (dir.exists()) { - dir.eachFileRecurse(FILES) { f -> - if (condition == null || condition(f)) { - def relativePath = dir.relativePath(f) - files << relativePath - } - } + project.gradle.taskGraph.whenReady { graph -> + if (graph.hasTask(reportTask)) { + project.test.configure { + project.logger.debug("Adding instrumented classes to '${path}' classpath") + + classpath = project.configurations.scoverage + instrumentedSourceSet.output + classpath + + outputs.upToDateWhen { + extension.dataDir.get().listFiles(new FilenameFilter() { + @Override + boolean accept(File dir, String name) { + return name.startsWith("scoverage.measurements.") } + }) + } + } - return files + compileTask.configure { + if (!graph.hasTask(originalCompileTask)) { + destinationDir = originalCompileTask.destinationDir + } else { + doFirst { + destinationDir.deleteDir() } - def isSameFile = { String relativePath -> - def fileA = new File(originalDestinationDir, relativePath) - def fileB = new File(destinationDir, relativePath) - return FileUtils.contentEquals(fileA, fileB) - } + // delete non-instrumented classes by comparing normally compiled classes to those compiled with scoverage + doLast { + def originalCompileTaskName = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getCompileTaskName("scala") + def originalDestinationDir = project.tasks[originalCompileTaskName].destinationDir + + def findFiles = { File dir, Closure condition = null -> + def files = [] + + if (dir.exists()) { + dir.eachFileRecurse(FILES) { f -> + if (condition == null || condition(f)) { + def relativePath = dir.relativePath(f) + files << relativePath + } + } + } - def originalClasses = findFiles(originalDestinationDir) - def identicalInstrumentedClasses = findFiles(destinationDir, { f -> - def relativePath = destinationDir.relativePath(f) - return originalClasses.contains(relativePath) && isSameFile(relativePath) - }) + return files + } + + def isSameFile = { String relativePath -> + def fileA = new File(originalDestinationDir, relativePath) + def fileB = new File(destinationDir, relativePath) + return FileUtils.contentEquals(fileA, fileB) + } - identicalInstrumentedClasses.each { f -> - Files.deleteIfExists(destinationDir.toPath().resolve(f)) + def originalClasses = findFiles(originalDestinationDir) + def identicalInstrumentedClasses = findFiles(destinationDir, { f -> + def relativePath = destinationDir.relativePath(f) + return originalClasses.contains(relativePath) && isSameFile(relativePath) + }) + + identicalInstrumentedClasses.each { f -> + Files.deleteIfExists(destinationDir.toPath().resolve(f)) + } } } } @@ -255,5 +268,10 @@ class ScoveragePlugin implements Plugin { } } + private Set recursiveDependenciesOf(Task task) { + def directDependencies = task.getTaskDependencies().getDependencies(task) + def nestedDependencies = directDependencies.collect {recursiveDependenciesOf(it) }.flatten() + return directDependencies + nestedDependencies + } } \ No newline at end of file From e5e0e90cdd39756be936e58d3f71517510d0a1cf Mon Sep 17 00:00:00 2001 From: Eyal Roth Date: Sun, 3 Feb 2019 16:30:47 +0200 Subject: [PATCH 17/17] Update README.md with release history and instructions on how to migrate to 3.x --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c03c22c..278f371 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,12 @@ gradle-scoverage ================ A plugin to enable the use of Scoverage in a gradle Scala project. -Getting started ---------------- +Usage +----- -http://plugins.gradle.org/plugin/org.scoverage +You can find instructions on how to apply the plugin at: http://plugins.gradle.org/plugin/org.scoverage -Available tasks ---------------- +### Available tasks 1. `reportScoverage`: Produces XML and HTML reports for analysing test code coverage. @@ -29,8 +28,7 @@ Available tasks `gradle checkScoverage` will automatically invoke `reportScoverage` but it won't generate aggregated reports. In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`. -Configuration -------------- +### Configuration The plugin exposes multiple options that can be configured by setting them in an `scoverage` block within the project's build script. These options are as follows: @@ -58,8 +56,7 @@ required for the validation to pass (otherwise `checkScoverage` will fail the bu `checkScoverage` task. For more information on the different types, please refer to the documentation of the scalac plugin (https://github.com/scoverage/scalac-scoverage-plugin). -Run without normal compilation ------------------------------- +### Run without normal compilation By default, running any of the plugin tasks will compile the code both using "normal" compilation (`compileScala`) and using the scoverage scalac plugin (`compileScoverageScala`). @@ -67,4 +64,50 @@ and using the scoverage scalac plugin (`compileScoverageScala`). In cases where you only wish to generate reports / validate coverage, but are not interested in publishing the code, it is possible to only compile the code with the scoverage scalac plugin, thus reducing build times significantly. In order to do so, simply add the arguments `-x compileScala` to the gradle execution. -For example: `gradle reportScoverage -x compileScala`. \ No newline at end of file +For example: `gradle reportScoverage -x compileScala`. + +Migration to 3.x +---------------- + +* No more `testScoverage` task; instead, `test` will run coverage whenever the build is invoked with any of the scoverage tasks. + +* No more need to declare scalac dependencies: +```groovy +// can safely delete this from build scripts +dependencies { + scoverage group: 'org.scoverage', name: 'scalac-scoverage-plugin_2.12', version: '1.3.1' + scoverage group: 'org.scoverage', name: 'scalac-scoverage-runtime_2.12', version: '1.3.1' +} +``` + +* All configurations are configured in `scoverage` block. For instance: +```groovy +// do this +scoverage { + minimumRate = 0.5 +} + +// instead of this +checkScoverage { + minimumRate = 0.5 +} +``` + +* No more need to declare aggregation task: +```groovy +// can safely delete this from build scripts +task aggregateScoverage(type: org.scoverage.ScoverageAggregate) +checkScoverage { + reportDir = file("$buildDir/scoverage-aggregate") +} +``` + +Release history +--------------- + +##### (not released yet) - 3.0.0 + +* Auto resolution of scalac plugin dependencies. +* Aggregation task declared by default. +* Deletion of non-instrumented classes, allowing for better integration with other coverage tools such as cobertura. +* Ability to execute coverage without "normal" compilation, thus reducing build times. \ No newline at end of file