Skip to content

Commit b2fbb84

Browse files
committed
Properly handle report, check and aggregation tasks in multi-module projects
1 parent 78dc56f commit b2fbb84

File tree

20 files changed

+368
-33
lines changed

20 files changed

+368
-33
lines changed

README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,27 @@ You can configure output generated by `gradle reportScoverage` using flags:
4848
Aggregating Reports
4949
-------------------
5050

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

53-
When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`.
52+
There is now experimental support for aggregating coverage statistics in composite builds.
5453

55-
This will produce a report into `build/scoverage-aggregate` directory.
54+
When applied on a project with sub-projects, the plugin will create the aggregation task `aggregateScoverage`, which
55+
will first generate reports for each project individually (including the parent project), and will then generate an
56+
aggregated result based on these reports.
5657

57-
Aggregation uses same flags as reporting for enabling/disabling different output types.
58+
The aggregated report will override the parent-project specific report (`parent-project/build/reports/scoverage`).
5859

59-
For checking coverage of the aggregated result, configure the checkScoverage task:
60+
One can still use `reportScoverage` in order to generate a report without aggregation.
6061

61-
```groovy
62-
checkScoverage {
63-
reportDir = file("$buildDir/scoverage-aggregate")
64-
}
65-
```
62+
Aggregation uses same flags as reporting for enabling/disabling different output types.
6663

6764
CheckScoverage
6865
--------------
6966

67+
The `checkScoverage` task validates coverage status according the generated reports.
68+
69+
`gradle checkScoverage` will automatically generate reports via `reportScoverage` but it won't generate aggregated reports.
70+
In order to check coverage of aggregated reports one should use `gradle checkScoverage aggregateScoverage`.
71+
7072
By default, when you launch `gradle checkScoverage` build fail if only 75% of statements in project is covered by tests.
7173

7274
To configure it as you want, add this configuration :
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package org.scoverage;
2+
3+
import org.junit.Test;
4+
5+
public class ScalaMultiModuleTest extends ScoverageFunctionalTest {
6+
7+
public ScalaMultiModuleTest() {
8+
super("scala-multi-module");
9+
}
10+
11+
@Test
12+
public void reportScoverage() {
13+
14+
AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getREPORT_NAME());
15+
16+
result.assertTaskExists(ScoveragePlugin.getREPORT_NAME());
17+
result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME());
18+
result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME());
19+
}
20+
21+
@Test
22+
public void reportScoverageOnlyRoot() {
23+
24+
AssertableBuildResult result = dryRun("clean", ":" + ScoveragePlugin.getREPORT_NAME());
25+
26+
result.assertTaskExists(ScoveragePlugin.getREPORT_NAME());
27+
result.assertTaskDoesntExist("a:" + ScoveragePlugin.getREPORT_NAME());
28+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME());
29+
}
30+
31+
@Test
32+
public void reportScoverageOnlyA() {
33+
34+
AssertableBuildResult result = dryRun("clean", ":a:" + ScoveragePlugin.getREPORT_NAME());
35+
36+
result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME());
37+
result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME());
38+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME());
39+
}
40+
41+
@Test
42+
public void aggregateScoverage() {
43+
44+
AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getAGGREGATE_NAME());
45+
46+
result.assertTaskExists(ScoveragePlugin.getREPORT_NAME());
47+
result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME());
48+
result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME());
49+
result.assertTaskExists(ScoveragePlugin.getAGGREGATE_NAME());
50+
}
51+
52+
@Test
53+
public void checkScoverage() {
54+
55+
AssertableBuildResult result = dryRun("clean", ScoveragePlugin.getCHECK_NAME());
56+
57+
result.assertTaskExists(ScoveragePlugin.getREPORT_NAME());
58+
result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME());
59+
result.assertTaskExists("b:" + ScoveragePlugin.getREPORT_NAME());
60+
result.assertTaskExists(ScoveragePlugin.getCHECK_NAME());
61+
result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME());
62+
result.assertTaskExists("b:" + ScoveragePlugin.getCHECK_NAME());
63+
result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME());
64+
}
65+
66+
@Test
67+
public void checkScoverageOnlyRoot() {
68+
69+
AssertableBuildResult result = dryRun("clean", ":" + ScoveragePlugin.getCHECK_NAME());
70+
71+
result.assertTaskExists(ScoveragePlugin.getREPORT_NAME());
72+
result.assertTaskDoesntExist("a:" + ScoveragePlugin.getREPORT_NAME());
73+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME());
74+
result.assertTaskExists(ScoveragePlugin.getCHECK_NAME());
75+
result.assertTaskDoesntExist("a:" + ScoveragePlugin.getCHECK_NAME());
76+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getCHECK_NAME());
77+
result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME());
78+
}
79+
80+
@Test
81+
public void checkScoverageOnlyA() {
82+
83+
AssertableBuildResult result = dryRun("clean", ":a:" + ScoveragePlugin.getCHECK_NAME());
84+
85+
result.assertTaskDoesntExist(ScoveragePlugin.getREPORT_NAME());
86+
result.assertTaskExists("a:" + ScoveragePlugin.getREPORT_NAME());
87+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getREPORT_NAME());
88+
result.assertTaskDoesntExist(ScoveragePlugin.getCHECK_NAME());
89+
result.assertTaskExists("a:" + ScoveragePlugin.getCHECK_NAME());
90+
result.assertTaskDoesntExist("b:" + ScoveragePlugin.getCHECK_NAME());
91+
result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME());
92+
}
93+
94+
@Test
95+
public void checkAndAggregateScoverage() {
96+
97+
AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(), ScoveragePlugin.getAGGREGATE_NAME());
98+
99+
result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME());
100+
result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME());
101+
result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME());
102+
result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME());
103+
result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME());
104+
result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME());
105+
result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME());
106+
}
107+
108+
@Test
109+
public void checkScoverageWithoutCoverageInRoot() {
110+
111+
AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(),
112+
ScoveragePlugin.getTEST_NAME(),
113+
"--tests", "org.hello.TestNothingSuite",
114+
"--tests", "org.hello.a.WorldASuite",
115+
"--tests", "org.hello.b.WorldBSuite");
116+
117+
result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME());
118+
}
119+
120+
@Test
121+
public void checkScoverageWithoutCoverageInA() {
122+
123+
AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(),
124+
ScoveragePlugin.getTEST_NAME(),
125+
"--tests", "org.hello.a.TestNothingASuite",
126+
"--tests", "org.hello.WorldSuite",
127+
"--tests", "org.hello.b.WorldBSuite");
128+
129+
result.assertTaskFailed("a:" + ScoveragePlugin.getCHECK_NAME());
130+
}
131+
132+
@Test
133+
public void checkAndAggregateScoverageWithoutCoverageInRoot() {
134+
135+
// should pass as the check on the root is for the aggregation (which covers > 50%)
136+
137+
AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME(),
138+
ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(),
139+
"--tests", "org.hello.TestNothingSuite",
140+
"--tests", "org.hello.a.WorldASuite",
141+
"--tests", "org.hello.b.WorldBSuite");
142+
143+
result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME());
144+
result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME());
145+
result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME());
146+
result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME());
147+
result.assertTaskSucceeded("a:" + ScoveragePlugin.getCHECK_NAME());
148+
result.assertTaskSucceeded("b:" + ScoveragePlugin.getCHECK_NAME());
149+
result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME());
150+
}
151+
152+
@Test
153+
public void checkAndAggregateScoverageWithoutCoverageInAll() {
154+
155+
AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(),
156+
ScoveragePlugin.getAGGREGATE_NAME(), ScoveragePlugin.getTEST_NAME(),
157+
"--tests", "org.hello.TestNothingSuite",
158+
"--tests", "org.hello.a.TestNothingASuite",
159+
"--tests", "org.hello.b.TestNothingBSuite");
160+
161+
result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME());
162+
result.assertTaskSucceeded("a:" + ScoveragePlugin.getREPORT_NAME());
163+
result.assertTaskSucceeded("b:" + ScoveragePlugin.getREPORT_NAME());
164+
result.assertTaskSucceeded(ScoveragePlugin.getAGGREGATE_NAME());
165+
result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME());
166+
}
167+
}

src/functionalTest/java/org.scoverage/ScalaSingleModuleTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ public void checkScoverage() {
7070

7171
AssertableBuildResult result = run("clean", ScoveragePlugin.getCHECK_NAME());
7272

73-
result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS);
74-
result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS);
75-
result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS);
76-
result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.SUCCESS);
73+
result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME());
74+
result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME());
75+
result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME());
76+
result.assertTaskSucceeded(ScoveragePlugin.getCHECK_NAME());
7777
result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME());
7878
}
7979

@@ -83,10 +83,10 @@ public void checkScoverageFails() {
8383
AssertableBuildResult result = runAndFail("clean", ScoveragePlugin.getCHECK_NAME(),
8484
ScoveragePlugin.getTEST_NAME(), "--tests", "org.hello.TestNothingSuite");
8585

86-
result.assertTaskOutcome(ScoveragePlugin.getCOMPILE_NAME(), TaskOutcome.SUCCESS);
87-
result.assertTaskOutcome(ScoveragePlugin.getTEST_NAME(), TaskOutcome.SUCCESS);
88-
result.assertTaskOutcome(ScoveragePlugin.getREPORT_NAME(), TaskOutcome.SUCCESS);
89-
result.assertTaskOutcome(ScoveragePlugin.getCHECK_NAME(), TaskOutcome.FAILED);
86+
result.assertTaskSucceeded(ScoveragePlugin.getCOMPILE_NAME());
87+
result.assertTaskSucceeded(ScoveragePlugin.getTEST_NAME());
88+
result.assertTaskSucceeded(ScoveragePlugin.getREPORT_NAME());
89+
result.assertTaskFailed(ScoveragePlugin.getCHECK_NAME());
9090
result.assertTaskDoesntExist(ScoveragePlugin.getAGGREGATE_NAME());
9191
}
9292
}

src/functionalTest/java/org.scoverage/ScoverageFunctionalTest.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.ArrayList;
1111
import java.util.Arrays;
1212
import java.util.List;
13+
import java.util.regex.Pattern;
1314

1415
public abstract class ScoverageFunctionalTest {
1516

@@ -99,6 +100,16 @@ public void assertTaskSkipped(String taskName) {
99100
Assert.assertTrue(task == null || task.getOutcome() == TaskOutcome.SKIPPED);
100101
}
101102

103+
public void assertTaskSucceeded(String taskName) {
104+
105+
assertTaskOutcome(taskName, TaskOutcome.SUCCESS);
106+
}
107+
108+
public void assertTaskFailed(String taskName) {
109+
110+
assertTaskOutcome(taskName, TaskOutcome.FAILED);
111+
}
112+
102113
public void assertTaskOutcome(String taskName, TaskOutcome outcome) {
103114

104115
BuildTask task = getTask(taskName);
@@ -119,7 +130,8 @@ private String fullTaskName(String taskName) {
119130

120131
private boolean taskExists(String taskName) {
121132

122-
return result.getOutput().contains(fullTaskName(taskName) + " ");
133+
Pattern regex = Pattern.compile("^" + fullTaskName(taskName), Pattern.MULTILINE);
134+
return regex.matcher(result.getOutput()).find();
123135
}
124136
}
125137
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.hello.a
2+
3+
class WorldA {
4+
5+
def fooA(): String = {
6+
val s = "a" + "a"
7+
s
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.hello.a
2+
3+
import org.junit.runner.RunWith
4+
import org.scalatest.FunSuite
5+
import org.scalatest.junit.JUnitRunner
6+
7+
@RunWith(classOf[JUnitRunner])
8+
class TestNothingASuite extends FunSuite {
9+
10+
test("nothing") {
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.hello.a
2+
3+
import org.junit.runner.RunWith
4+
import org.scalatest.FunSuite
5+
import org.scalatest.junit.JUnitRunner
6+
7+
@RunWith(classOf[JUnitRunner])
8+
class WorldASuite extends FunSuite {
9+
10+
test("fooA") {
11+
new WorldA().fooA()
12+
}
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.hello.b
2+
3+
class WorldB {
4+
5+
def fooB(): String = {
6+
val s = "b" + "b"
7+
s
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.hello.b
2+
3+
import org.junit.runner.RunWith
4+
import org.scalatest.FunSuite
5+
import org.scalatest.junit.JUnitRunner
6+
7+
@RunWith(classOf[JUnitRunner])
8+
class TestNothingBSuite extends FunSuite {
9+
10+
test("nothing") {
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.hello.b
2+
3+
import org.junit.runner.RunWith
4+
import org.scalatest.FunSuite
5+
import org.scalatest.junit.JUnitRunner
6+
7+
@RunWith(classOf[JUnitRunner])
8+
class WorldBSuite extends FunSuite {
9+
10+
test("fooB") {
11+
new WorldB().fooB()
12+
}
13+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
plugins {
2+
id 'org.scoverage'
3+
}
4+
5+
allprojects {
6+
repositories {
7+
jcenter()
8+
}
9+
}
10+
11+
description = 'a multi-module Scala project that builds successfully with 100% coverage'
12+
13+
allprojects {
14+
15+
apply plugin: 'java'
16+
apply plugin: 'scala'
17+
18+
dependencies {
19+
compile group: 'org.scala-lang', name: 'scala-library', version: "${scalaVersionMajor}.${scalaVersionMinor}.${scalaVersionBuild}"
20+
21+
testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion
22+
testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: junitPlatformVersion
23+
24+
testCompile group: 'org.scalatest', name: "scalatest_${scalaVersionMajor}.${scalaVersionMinor}", version: scalatestVersion
25+
}
26+
27+
test {
28+
useJUnitPlatform()
29+
}
30+
31+
checkScoverage {
32+
minimumRate = 0.5
33+
}
34+
}
35+
36+
checkScoverage {
37+
minimumRate = 0.5
38+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include 'a', 'b'

0 commit comments

Comments
 (0)