Skip to content

Commit fcdc4b7

Browse files
committed
initial
1 parent 34ea934 commit fcdc4b7

6 files changed

Lines changed: 596 additions & 10 deletions

File tree

rust/private/rust.bzl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,32 @@ def _rust_test_impl(ctx):
569569
env["CC_CODE_COVERAGE_SCRIPT"] = ctx.executable._collect_cc_coverage.path
570570
components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
571571
env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
572+
573+
if ctx.attr.junit:
574+
test_bin_short = output.short_path
575+
if test_bin_short.startswith("../"):
576+
rust_test_bin_rloc = test_bin_short[len("../"):]
577+
else:
578+
rust_test_bin_rloc = ctx.workspace_name + "/" + test_bin_short
579+
env["RUST_TEST_BIN"] = rust_test_bin_rloc
580+
581+
junit_runner = ctx.actions.declare_file(ctx.label.name + "_junit_runner" + toolchain.binary_ext)
582+
ctx.actions.symlink(
583+
output = junit_runner,
584+
target_file = ctx.executable._junit_runner,
585+
is_executable = True,
586+
)
587+
588+
original_default_info = providers[0]
589+
runner_runfiles = ctx.attr._junit_runner[DefaultInfo].default_runfiles
590+
test_bin_runfiles = ctx.runfiles(files = [output])
591+
merged_runfiles = original_default_info.default_runfiles.merge(runner_runfiles).merge(test_bin_runfiles)
592+
providers[0] = DefaultInfo(
593+
files = original_default_info.files,
594+
runfiles = merged_runfiles,
595+
executable = junit_runner,
596+
)
597+
572598
providers.append(RunEnvironmentInfo(
573599
environment = env,
574600
inherited_environment = ctx.attr.env_inherit,
@@ -956,6 +982,22 @@ _RUST_TEST_ATTRS = {
956982
E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
957983
"""),
958984
),
985+
"junit": attr.bool(
986+
default = True,
987+
doc = dedent("""\
988+
If True (default), wrap the test binary with a JUnit XML runner that
989+
parses libtest output and writes JUnit XML to `$XML_OUTPUT_FILE` when
990+
run under `bazel test`. When `$XML_OUTPUT_FILE` is not set (e.g.
991+
`bazel run`), the runner execs the test binary directly with zero
992+
overhead. Set to False to bypass the wrapper entirely (useful for
993+
debugging or attaching a debugger).
994+
"""),
995+
),
996+
"_junit_runner": attr.label(
997+
default = Label("//util/junit_runner"),
998+
executable = True,
999+
cfg = "target",
1000+
),
9591001
} | _COVERAGE_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS
9601002

9611003
rust_library = rule(

test/junit/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("//rust:defs.bzl", "rust_test")
2+
3+
rust_test(
4+
name = "junit_test",
5+
srcs = ["lib.rs"],
6+
edition = "2021",
7+
)
8+
9+
rust_test(
10+
name = "no_junit_test",
11+
srcs = ["lib.rs"],
12+
edition = "2021",
13+
junit = False,
14+
)

test/junit/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#[cfg(test)]
2+
mod tests {
3+
#[test]
4+
fn test_passing() {
5+
assert_eq!(2 + 2, 4);
6+
}
7+
8+
#[test]
9+
#[ignore]
10+
fn test_ignored() {}
11+
}

util/collect_coverage/collect_coverage.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ fn main() {
129129
None => debug_log!("RUNFILES_DIR: not set (split coverage postprocessing)"),
130130
}
131131

132-
let coverage_output_file = coverage_dir.join("coverage.dat");
132+
let coverage_output_file = env::var("COVERAGE_OUTPUT_FILE")
133+
.map(PathBuf::from)
134+
.unwrap_or_else(|_| coverage_dir.join("coverage.dat"));
133135
let profdata_file = coverage_dir.join("coverage.profdata");
134136
let llvm_cov_path = env::var("RUST_LLVM_COV").unwrap();
135137
let llvm_profdata_path = env::var("RUST_LLVM_PROFDATA").unwrap();
@@ -141,15 +143,43 @@ fn main() {
141143
Some(ref rd) => find_metadata_file(&execroot, rd, &llvm_profdata_path),
142144
None => execroot.join(&llvm_profdata_path),
143145
};
144-
let test_binary = match runfiles_dir {
145-
Some(ref rd) => find_test_binary(&execroot, rd),
146-
None => {
147-
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
148-
let test_binary = execroot
149-
.join(bin_dir)
150-
.join(env::var("TEST_BINARY").unwrap());
151-
debug_log!("Resolved TEST_BINARY to: {}", test_binary.display());
152-
test_binary
146+
// When the JUnit runner wraps the test, TEST_BINARY points to the runner
147+
// and RUST_TEST_BIN holds the actual instrumented binary that llvm-cov needs.
148+
let test_binary = if let Ok(rust_test_bin) = env::var("RUST_TEST_BIN") {
149+
debug_log!("Using RUST_TEST_BIN: {}", rust_test_bin);
150+
match runfiles_dir {
151+
Some(ref rd) => {
152+
let candidate = rd
153+
.join(env::var("TEST_WORKSPACE").unwrap_or_default())
154+
.join(&rust_test_bin);
155+
if candidate.exists() {
156+
candidate
157+
} else {
158+
let candidate = rd.join(&rust_test_bin);
159+
if candidate.exists() {
160+
candidate
161+
} else {
162+
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
163+
execroot.join(bin_dir).join(&rust_test_bin)
164+
}
165+
}
166+
}
167+
None => {
168+
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
169+
execroot.join(bin_dir).join(&rust_test_bin)
170+
}
171+
}
172+
} else {
173+
match runfiles_dir {
174+
Some(ref rd) => find_test_binary(&execroot, rd),
175+
None => {
176+
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
177+
let test_binary = execroot
178+
.join(bin_dir)
179+
.join(env::var("TEST_BINARY").unwrap());
180+
debug_log!("Resolved TEST_BINARY to: {}", test_binary.display());
181+
test_binary
182+
}
153183
}
154184
};
155185
let profraw_files: Vec<PathBuf> = fs::read_dir(coverage_dir)

util/junit_runner/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("//rust:defs.bzl", "rust_binary", "rust_test")
2+
3+
rust_binary(
4+
name = "junit_runner",
5+
srcs = ["junit_runner.rs"],
6+
edition = "2021",
7+
rustc_flags = select({
8+
"@platforms//os:linux": ["-Cstrip=debuginfo"],
9+
"@platforms//os:macos": ["-Cstrip=symbols"],
10+
"//conditions:default": [],
11+
}),
12+
visibility = ["//visibility:public"],
13+
)
14+
15+
rust_test(
16+
name = "junit_runner_test",
17+
srcs = ["junit_runner.rs"],
18+
edition = "2021",
19+
junit = False,
20+
)

0 commit comments

Comments
 (0)