Skip to content

Commit

Permalink
Expand run-coverage to support the remaining coverage-reports tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Zalathar committed Jun 28, 2023
1 parent d05653c commit 9d2564a
Showing 1 changed file with 115 additions and 3 deletions.
118 changes: 115 additions & 3 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,18 @@ impl<'test> TestCx<'test> {
}
drop(proc_res);

let mut profraw_paths = vec![profraw_path];
let mut bin_paths = vec![self.make_exe_name()];

if self.config.suite == "run-coverage-rustdoc" {
self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths);
}

// Run `llvm-profdata merge` to index the raw coverage output.
let proc_res = self.run_llvm_tool("llvm-profdata", |cmd| {
cmd.args(["merge", "--sparse", "--output"]);
cmd.arg(&profdata_path);
cmd.arg(&profraw_path);
cmd.args(&profraw_paths);
});
if !proc_res.status.success() {
self.fatal_proc_rec("llvm-profdata merge failed!", &proc_res);
Expand All @@ -523,8 +530,10 @@ impl<'test> TestCx<'test> {
cmd.arg("--instr-profile");
cmd.arg(&profdata_path);

cmd.arg("--object");
cmd.arg(&self.make_exe_name());
for bin in &bin_paths {
cmd.arg("--object");
cmd.arg(bin);
}
});
if !proc_res.status.success() {
self.fatal_proc_rec("llvm-cov show failed!", &proc_res);
Expand Down Expand Up @@ -553,6 +562,82 @@ impl<'test> TestCx<'test> {
}
}

/// Run any doctests embedded in this test file, and add any resulting
/// `.profraw` files and doctest executables to the given vectors.
fn run_doctests_for_coverage(
&self,
profraw_paths: &mut Vec<PathBuf>,
bin_paths: &mut Vec<PathBuf>,
) {
// Put .profraw files and doctest executables in dedicated directories,
// to make it easier to glob them all later.
let profraws_dir = self.output_base_dir().join("doc_profraws");
let bins_dir = self.output_base_dir().join("doc_bins");

// Remove existing directories to prevent cross-run interference.
if profraws_dir.try_exists().unwrap() {
std::fs::remove_dir_all(&profraws_dir).unwrap();
}
if bins_dir.try_exists().unwrap() {
std::fs::remove_dir_all(&bins_dir).unwrap();
}

let mut rustdoc_cmd =
Command::new(self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed"));

// In general there will be multiple doctest binaries running, so we
// tell the profiler runtime to write their coverage data into separate
// profraw files.
rustdoc_cmd.env("LLVM_PROFILE_FILE", profraws_dir.join("%p-%m.profraw"));

rustdoc_cmd.args(["--test", "-Cinstrument-coverage"]);

// Without this, the doctests complain about not being able to find
// their enclosing file's crate for some reason.
rustdoc_cmd.args(["--crate-name", "workaround_for_79771"]);

// Persist the doctest binaries so that `llvm-cov show` can read their
// embedded coverage mappings later.
rustdoc_cmd.arg("-Zunstable-options");
rustdoc_cmd.arg("--persist-doctests");
rustdoc_cmd.arg(&bins_dir);

rustdoc_cmd.arg("-L");
rustdoc_cmd.arg(self.aux_output_dir_name());

rustdoc_cmd.arg(&self.testpaths.file);

let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None);
if !proc_res.status.success() {
self.fatal_proc_rec("rustdoc --test failed!", &proc_res)
}

fn glob_iter(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
let path_str = path.as_ref().to_str().unwrap();
let iter = glob(path_str).unwrap();
iter.map(Result::unwrap)
}

// Find all profraw files in the profraw directory.
for p in glob_iter(profraws_dir.join("*.profraw")) {
profraw_paths.push(p);
}
// Find all executables in the `--persist-doctests` directory, while
// avoiding other file types (e.g. `.pdb` on Windows). This doesn't
// need to be perfect, as long as it can handle the files actually
// produced by `rustdoc --test`.
for p in glob_iter(bins_dir.join("**/*")) {
let is_bin = p.is_file()
&& match p.extension() {
None => true,
Some(ext) => ext == OsStr::new("exe"),
};
if is_bin {
bin_paths.push(p);
}
}
}

fn run_llvm_tool(&self, name: &str, configure_cmd_fn: impl FnOnce(&mut Command)) -> ProcRes {
let tool_path = self
.config
Expand Down Expand Up @@ -582,12 +667,39 @@ impl<'test> TestCx<'test> {

let mut lines = normalized.lines().collect::<Vec<_>>();

Self::sort_coverage_file_sections(&mut lines)?;
Self::sort_coverage_subviews(&mut lines)?;

let joined_lines = lines.iter().flat_map(|line| [line, "\n"]).collect::<String>();
Ok(joined_lines)
}

/// Coverage reports can describe multiple source files, separated by
/// blank lines. The order of these files is unpredictable (since it
/// depends on implementation details), so we need to sort the file
/// sections into a consistent order before comparing against a snapshot.
fn sort_coverage_file_sections(coverage_lines: &mut Vec<&str>) -> Result<(), String> {
// Group the lines into file sections, separated by blank lines.
let mut sections = coverage_lines.split(|line| line.is_empty()).collect::<Vec<_>>();

// The last section should be empty, representing an extra trailing blank line.
if !sections.last().is_some_and(|last| last.is_empty()) {
return Err("coverage report should end with an extra blank line".to_owned());
}

// Sort the file sections (not including the final empty "section").
let except_last = sections.len() - 1;
(&mut sections[..except_last]).sort();

// Join the file sections back into a flat list of lines, with
// sections separated by blank lines.
let joined = sections.join(&[""] as &[_]);
assert_eq!(joined.len(), coverage_lines.len());
*coverage_lines = joined;

Ok(())
}

fn sort_coverage_subviews(coverage_lines: &mut Vec<&str>) -> Result<(), String> {
let mut output_lines = Vec::new();

Expand Down

0 comments on commit 9d2564a

Please sign in to comment.