Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,32 @@ def _rust_test_impl(ctx):
env["CC_CODE_COVERAGE_SCRIPT"] = ctx.executable._collect_cc_coverage.path
components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])

if ctx.attr.junit:
test_bin_short = output.short_path
if test_bin_short.startswith("../"):
rust_test_bin_rloc = test_bin_short[len("../"):]
else:
rust_test_bin_rloc = ctx.workspace_name + "/" + test_bin_short
env["RUST_TEST_BIN"] = rust_test_bin_rloc

junit_runner = ctx.actions.declare_file(ctx.label.name + "_junit_runner" + toolchain.binary_ext)
ctx.actions.symlink(
output = junit_runner,
target_file = ctx.executable._junit_runner,
is_executable = True,
)

original_default_info = providers[0]
runner_runfiles = ctx.attr._junit_runner[DefaultInfo].default_runfiles
test_bin_runfiles = ctx.runfiles(files = [output])
merged_runfiles = original_default_info.default_runfiles.merge(runner_runfiles).merge(test_bin_runfiles)
providers[0] = DefaultInfo(
files = original_default_info.files,
runfiles = merged_runfiles,
executable = junit_runner,
)

providers.append(RunEnvironmentInfo(
environment = env,
inherited_environment = ctx.attr.env_inherit,
Expand Down Expand Up @@ -956,6 +982,22 @@ _RUST_TEST_ATTRS = {
E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
"""),
),
"junit": attr.bool(
default = True,
doc = dedent("""\
If True (default), wrap the test binary with a JUnit XML runner that
parses libtest output and writes JUnit XML to `$XML_OUTPUT_FILE` when
run under `bazel test`. When `$XML_OUTPUT_FILE` is not set (e.g.
`bazel run`), the runner execs the test binary directly with zero
overhead. Set to False to bypass the wrapper entirely (useful for
debugging or attaching a debugger).
"""),
),
"_junit_runner": attr.label(
default = Label("//util/junit_runner"),
executable = True,
cfg = "target",
),
} | _COVERAGE_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS

rust_library = rule(
Expand Down
14 changes: 14 additions & 0 deletions test/junit/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("//rust:defs.bzl", "rust_test")

rust_test(
name = "junit_test",
srcs = ["lib.rs"],
edition = "2021",
)

rust_test(
name = "no_junit_test",
srcs = ["lib.rs"],
edition = "2021",
junit = False,
)
11 changes: 11 additions & 0 deletions test/junit/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[cfg(test)]
mod tests {
#[test]
fn test_passing() {
assert_eq!(2 + 2, 4);
}

#[test]
#[ignore]
fn test_ignored() {}
}
56 changes: 44 additions & 12 deletions util/collect_coverage/collect_coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ fn main() {
None => debug_log!("RUNFILES_DIR: not set (split coverage postprocessing)"),
}

let coverage_output_file = coverage_dir.join("coverage.dat");
let coverage_output_file = env::var("COVERAGE_OUTPUT_FILE")
.map(PathBuf::from)
.unwrap_or_else(|_| coverage_dir.join("coverage.dat"));
let profdata_file = coverage_dir.join("coverage.profdata");
let llvm_cov_path = env::var("RUST_LLVM_COV").unwrap();
let llvm_profdata_path = env::var("RUST_LLVM_PROFDATA").unwrap();
Expand All @@ -141,15 +143,43 @@ fn main() {
Some(ref rd) => find_metadata_file(&execroot, rd, &llvm_profdata_path),
None => execroot.join(&llvm_profdata_path),
};
let test_binary = match runfiles_dir {
Some(ref rd) => find_test_binary(&execroot, rd),
None => {
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
let test_binary = execroot
.join(bin_dir)
.join(env::var("TEST_BINARY").unwrap());
debug_log!("Resolved TEST_BINARY to: {}", test_binary.display());
test_binary
// When the JUnit runner wraps the test, TEST_BINARY points to the runner
// and RUST_TEST_BIN holds the actual instrumented binary that llvm-cov needs.
let test_binary = if let Ok(rust_test_bin) = env::var("RUST_TEST_BIN") {
debug_log!("Using RUST_TEST_BIN: {}", rust_test_bin);
match runfiles_dir {
Some(ref rd) => {
let candidate = rd
.join(env::var("TEST_WORKSPACE").unwrap_or_default())
.join(&rust_test_bin);
if candidate.exists() {
candidate
} else {
let candidate = rd.join(&rust_test_bin);
if candidate.exists() {
candidate
} else {
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
execroot.join(bin_dir).join(&rust_test_bin)
}
}
}
None => {
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
execroot.join(bin_dir).join(&rust_test_bin)
}
}
} else {
match runfiles_dir {
Some(ref rd) => find_test_binary(&execroot, rd),
None => {
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
let test_binary = execroot
.join(bin_dir)
.join(env::var("TEST_BINARY").unwrap());
debug_log!("Resolved TEST_BINARY to: {}", test_binary.display());
test_binary
}
}
};
let profraw_files: Vec<PathBuf> = fs::read_dir(coverage_dir)
Expand Down Expand Up @@ -189,7 +219,8 @@ fn main() {
.arg("-format=lcov")
.arg("-instr-profile")
.arg(&profdata_file)
.arg("-ignore-filename-regex=.*external/.+")
.arg(r"-ignore-filename-regex=.*external[/\\].+")
.arg(r"-ignore-filename-regex=.*rustc[/\\].+")
.arg("-ignore-filename-regex=/tmp/.+")
.arg(format!("-path-equivalence=.,{}", execroot.display()))
.arg(test_binary)
Expand Down Expand Up @@ -224,7 +255,8 @@ fn main() {
coverage_output_file,
report_str
.replace("#/proc/self/cwd/", "")
.replace(&execroot.display().to_string(), ""),
.replace(&execroot.display().to_string(), "")
.replace('\\', "/"),
)
.unwrap();

Expand Down
20 changes: 20 additions & 0 deletions util/junit_runner/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("//rust:defs.bzl", "rust_binary", "rust_test")

rust_binary(
name = "junit_runner",
srcs = ["junit_runner.rs"],
edition = "2021",
rustc_flags = select({
"@platforms//os:linux": ["-Cstrip=debuginfo"],
"@platforms//os:macos": ["-Cstrip=symbols"],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
)

rust_test(
name = "junit_runner_test",
srcs = ["junit_runner.rs"],
edition = "2021",
junit = False,
)
Loading