Skip to content

Commit 926aaca

Browse files
author
David Haxton
authored
Fix source pathing for code coverage (#1006)
* Fix source pathing for code coverage * lint fixes hopefully? * lint? idk what's going on here * Rename in_out_pair to records * Remove bad assumptions about how bazel works * Clean up lingering srcs Clean up lingering srcs * this commit should fail tests * this commit should pass tests * let this test actually fail the CI * update comments * Refactor src_paths * spelling is hard
1 parent 0366fb2 commit 926aaca

File tree

11 files changed

+128
-77
lines changed

11 files changed

+128
-77
lines changed

scala/private/phases/phase_coverage.bzl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,26 @@ def _phase_coverage(ctx, p, srcjars):
3636
output_jar = ctx.actions.declare_file(
3737
"{}-offline.jar".format(input_jar.basename.split(".")[0]),
3838
)
39-
in_out_pairs = [
40-
(input_jar, output_jar),
39+
srcs_paths = [src.path for src in ctx.files.srcs]
40+
records = [
41+
(input_jar, output_jar, srcs_paths),
4142
]
4243

4344
args = ctx.actions.args()
44-
args.add_all(in_out_pairs, map_each = _jacoco_offline_instrument_format_each)
45+
args.add_all(records, map_each = _jacoco_offline_instrument_format_each)
4546
args.set_param_file_format("multiline")
4647
args.use_param_file("@%s", use_always = True)
4748

4849
ctx.actions.run(
4950
mnemonic = "JacocoInstrumenter",
50-
inputs = [in_out_pair[0] for in_out_pair in in_out_pairs],
51-
outputs = [in_out_pair[1] for in_out_pair in in_out_pairs],
51+
inputs = [record[0] for record in records],
52+
outputs = [record[1] for record in records],
5253
executable = ctx.attr._code_coverage_instrumentation_worker.files_to_run,
5354
execution_requirements = {"supports-workers": "1"},
5455
arguments = [args],
5556
)
5657

57-
replacements = {i: o for (i, o) in in_out_pairs}
58+
replacements = {i: o for (i, o, _) in records}
5859
provider = _coverage_replacements_provider.create(
5960
replacements = replacements,
6061
)
@@ -72,5 +73,5 @@ def _phase_coverage(ctx, p, srcjars):
7273
},
7374
)
7475

75-
def _jacoco_offline_instrument_format_each(in_out_pair):
76-
return (["%s=%s" % (in_out_pair[0].path, in_out_pair[1].path)])
76+
def _jacoco_offline_instrument_format_each(records):
77+
return (["%s=%s=%s" % (records[0].path, records[1].path, ",".join(records[2]))])

src/java/io/bazel/rulesscala/coverage/instrumenter/JacocoInstrumenter.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@ public void processRequest(List < String > args) {
5252

5353
private void processArg(Instrumenter jacoco, String arg) throws Exception {
5454
String[] parts = arg.split("=");
55-
if (parts.length != 2) {
56-
throw new Exception("expected `in_path=out_path` form for argument: " + arg);
55+
if (parts.length != 3) {
56+
throw new Exception("expected `in_path=out_path=srcs` form for argument: " + arg);
5757
}
5858
Path inPath = Paths.get(parts[0]);
5959
Path outPath = Paths.get(parts[1]);
60+
String srcs = parts[2];
6061
try (
6162
FileSystem inFS = FileSystems.newFileSystem(inPath, null); FileSystem outFS = FileSystems.newFileSystem(
6263
URI.create("jar:" + outPath.toUri()), Collections.singletonMap("create", "true"));
@@ -69,6 +70,21 @@ private void processArg(Instrumenter jacoco, String arg) throws Exception {
6970
throw new RuntimeException(e);
7071
}
7172
});
73+
74+
/*
75+
* https://github.com/bazelbuild/bazel/blob/567ca633d016572f5760bfd027c10616f2b8c2e4/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoCoverageRunner.java#L411
76+
*
77+
* Bazel / JacocoCoverageRunner will look for any file that ends with '-paths-for-coverage.txt' within the JAR to be later used for reconstructing the path for source files.
78+
* This is a fairly undocumented feature within bazel at this time, but in essence, it opens all the jars, searches for all files matching '-paths-for-coverage.txt'
79+
* and then adds them to a single in memory set.
80+
*
81+
* https://github.com/bazelbuild/bazel/blob/567ca633d016572f5760bfd027c10616f2b8c2e4/src/java_tools/junitrunner/java/com/google/testing/coverage/JacocoLCOVFormatter.java#L70
82+
* Which is then used in the formatter to find the corresponding source file from the set of sources we wrote in all the JARs.
83+
*/
84+
Files.write(
85+
outFS.getPath("-paths-for-coverage.txt"),
86+
srcs.replace(",", "\n").getBytes(java.nio.charset.StandardCharsets.UTF_8)
87+
);
7288
}
7389
}
7490

test/coverage/A1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package coverage;
2+
13
object A1 {
24
def a1(flag: Boolean): B1.type =
35
if (flag) B1

test/coverage/A2.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
package coverage;
2+
13
object A2 {
24
def a2(): Unit = {
3-
println("a2: " +
4-
"" // B2.b2_a()
5+
println("a2: " + B2.b2_a()
56
)
67
}
78
}

test/coverage/B1.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package coverage;
2+
13
object B1 {
24

35
def not_called(): Unit = {

test/coverage/B2.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package coverage;
2+
13
class B2 {
24
public static String b2_a() {
35
return C2.c2("hello from b2_a");

test/coverage/BUILD

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,10 @@ scala_library(
6262
"A2.scala",
6363
],
6464
deps = [
65-
# TODO :: Understand why referencing a local java library breaks coverage
66-
# ":b2",
65+
":b2",
6766
],
6867
)
6968

70-
#
71-
# As it stands I can't seem to generate coverage for Java libraries pulled into
72-
# a scala_test target.
73-
#
74-
# The java_library is instrumented, but doesn't have the .uninstrumented files
75-
# that Bazel seems to expect. There are a few code paths for code coverage, so
76-
# down the road we can explore how to fix this...
77-
#
78-
7969
java_library(
8070
name = "b2",
8171
srcs = [

test/coverage/C2.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package coverage;
2+
13
object C2 {
24
def c2(input: String): String =
35
input.reverse

test/coverage/TestAll.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
package coverage;
12
import org.scalatest._
23

34
class TestAll extends FlatSpec {

test/coverage/TestB2.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package coverage;
2+
13
import org.junit.Test;
24
import org.junit.Assert.*;
35

test/coverage/expected-coverage.dat

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,110 @@
1-
SF:/A1.scala
2-
FN:-1,A1$::<clinit> ()V
3-
FN:5,A1$::<init> ()V
4-
FN:3,A1$::a1 (Z)LB1$;
5-
FN:-1,A1::a1 (Z)LB1$;
6-
FNDA:1,A1$::<clinit> ()V
7-
FNDA:1,A1$::<init> ()V
8-
FNDA:1,A1$::a1 (Z)LB1$;
9-
FNDA:0,A1::a1 (Z)LB1$;
1+
SF:test/coverage/A1.scala
2+
FN:-1,coverage/A1$::<clinit> ()V
3+
FN:7,coverage/A1$::<init> ()V
4+
FN:5,coverage/A1$::a1 (Z)Lcoverage/B1$;
5+
FN:-1,coverage/A1::a1 (Z)Lcoverage/B1$;
6+
FNDA:1,coverage/A1$::<clinit> ()V
7+
FNDA:1,coverage/A1$::<init> ()V
8+
FNDA:1,coverage/A1$::a1 (Z)Lcoverage/B1$;
9+
FNDA:0,coverage/A1::a1 (Z)Lcoverage/B1$;
1010
FNF:4
1111
FNH:3
12-
BA:3,2
12+
BA:5,2
1313
BRF:1
1414
BRH:1
15-
DA:3,4
16-
DA:4,0
17-
DA:5,5
15+
DA:5,4
16+
DA:6,0
17+
DA:7,5
1818
LH:2
1919
LF:3
2020
end_of_record
21-
SF:/A2.scala
22-
FN:-1,A2$::<clinit> ()V
23-
FN:7,A2$::<init> ()V
24-
FN:3,A2$::a2 ()V
25-
FN:-1,A2::a2 ()V
26-
FNDA:1,A2$::<clinit> ()V
27-
FNDA:1,A2$::<init> ()V
28-
FNDA:1,A2$::a2 ()V
29-
FNDA:0,A2::a2 ()V
21+
SF:test/coverage/A2.scala
22+
FN:-1,coverage/A2$::<clinit> ()V
23+
FN:8,coverage/A2$::<init> ()V
24+
FN:5,coverage/A2$::a2 ()V
25+
FN:-1,coverage/A2::a2 ()V
26+
FNDA:1,coverage/A2$::<clinit> ()V
27+
FNDA:1,coverage/A2$::<init> ()V
28+
FNDA:1,coverage/A2$::a2 ()V
29+
FNDA:0,coverage/A2::a2 ()V
3030
FNF:4
3131
FNH:3
32-
DA:3,4
33-
DA:7,5
32+
DA:5,11
33+
DA:8,5
3434
LH:2
3535
LF:2
3636
end_of_record
37-
SF:/B1.scala
38-
FN:-1,B1$::<clinit> ()V
39-
FN:7,B1$::<init> ()V
40-
FN:4,B1$::not_called ()V
41-
FN:-1,B1::not_called ()V
42-
FNDA:1,B1$::<clinit> ()V
43-
FNDA:1,B1$::<init> ()V
44-
FNDA:0,B1$::not_called ()V
45-
FNDA:0,B1::not_called ()V
37+
SF:test/coverage/B1.scala
38+
FN:-1,coverage/B1$::<clinit> ()V
39+
FN:9,coverage/B1$::<init> ()V
40+
FN:6,coverage/B1$::not_called ()V
41+
FN:-1,coverage/B1::not_called ()V
42+
FNDA:1,coverage/B1$::<clinit> ()V
43+
FNDA:1,coverage/B1$::<init> ()V
44+
FNDA:0,coverage/B1$::not_called ()V
45+
FNDA:0,coverage/B1::not_called ()V
4646
FNF:4
4747
FNH:2
48-
DA:4,0
49-
DA:7,5
48+
DA:6,0
49+
DA:9,5
50+
LH:1
51+
LF:2
52+
end_of_record
53+
SF:test/coverage/B2.java
54+
FN:3,coverage/B2::<init> ()V
55+
FN:5,coverage/B2::b2_a ()Ljava/lang/String;
56+
FN:9,coverage/B2::b2_b ()V
57+
FNDA:0,coverage/B2::<init> ()V
58+
FNDA:1,coverage/B2::b2_a ()Ljava/lang/String;
59+
FNDA:0,coverage/B2::b2_b ()V
60+
FNF:3
61+
FNH:1
62+
DA:3,0
63+
DA:5,3
64+
DA:9,0
65+
DA:10,0
5066
LH:1
67+
LF:4
68+
end_of_record
69+
SF:test/coverage/C2.scala
70+
FN:-1,coverage/C2$::<clinit> ()V
71+
FN:7,coverage/C2$::<init> ()V
72+
FN:5,coverage/C2$::c2 (Ljava/lang/String;)Ljava/lang/String;
73+
FN:-1,coverage/C2::c2 (Ljava/lang/String;)Ljava/lang/String;
74+
FNDA:1,coverage/C2$::<clinit> ()V
75+
FNDA:1,coverage/C2$::<init> ()V
76+
FNDA:1,coverage/C2$::c2 (Ljava/lang/String;)Ljava/lang/String;
77+
FNDA:1,coverage/C2::c2 (Ljava/lang/String;)Ljava/lang/String;
78+
FNF:4
79+
FNH:4
80+
DA:5,9
81+
DA:7,5
82+
LH:2
5183
LF:2
5284
end_of_record
53-
SF:/TestAll.scala
54-
FN:10,TestAll$$anonfun$1::<init> (LTestAll;)V
55-
FN:10,TestAll$$anonfun$1::apply ()V
56-
FN:10,TestAll$$anonfun$1::apply$mcV$sp ()V
57-
FN:6,TestAll$$anonfun$2::<init> (LTestAll;)V
58-
FN:6,TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion;
59-
FN:3,TestAll::<init> ()V
60-
FNDA:1,TestAll$$anonfun$1::<init> (LTestAll;)V
61-
FNDA:1,TestAll$$anonfun$1::apply ()V
62-
FNDA:1,TestAll$$anonfun$1::apply$mcV$sp ()V
63-
FNDA:1,TestAll$$anonfun$2::<init> (LTestAll;)V
64-
FNDA:1,TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion;
65-
FNDA:1,TestAll::<init> ()V
85+
SF:test/coverage/TestAll.scala
86+
FN:11,coverage/TestAll$$anonfun$1::<init> (Lcoverage/TestAll;)V
87+
FN:11,coverage/TestAll$$anonfun$1::apply ()V
88+
FN:11,coverage/TestAll$$anonfun$1::apply$mcV$sp ()V
89+
FN:7,coverage/TestAll$$anonfun$2::<init> (Lcoverage/TestAll;)V
90+
FN:7,coverage/TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion;
91+
FN:4,coverage/TestAll::<init> ()V
92+
FNDA:1,coverage/TestAll$$anonfun$1::<init> (Lcoverage/TestAll;)V
93+
FNDA:1,coverage/TestAll$$anonfun$1::apply ()V
94+
FNDA:1,coverage/TestAll$$anonfun$1::apply$mcV$sp ()V
95+
FNDA:1,coverage/TestAll$$anonfun$2::<init> (Lcoverage/TestAll;)V
96+
FNDA:1,coverage/TestAll$$anonfun$2::apply ()Lorg/scalatest/compatible/Assertion;
97+
FNDA:1,coverage/TestAll::<init> ()V
6698
FNF:6
6799
FNH:6
68-
BA:6,2
100+
BA:7,2
69101
BRF:1
70102
BRH:1
71-
DA:3,2
72-
DA:5,22
73-
DA:6,51
74-
DA:9,23
75-
DA:10,13
103+
DA:4,2
104+
DA:6,22
105+
DA:7,51
106+
DA:10,23
107+
DA:11,13
76108
LH:5
77109
LF:5
78110
end_of_record

0 commit comments

Comments
 (0)