bootstrap/core/build_steps/test/
failed_tests.rs1use std::collections::BTreeSet;
2use std::fs::{self, File};
3use std::io::{BufRead, BufReader, ErrorKind};
4use std::path::{Path, PathBuf};
5
6use crate::core::builder::{Builder, Step};
7use crate::t;
8
9#[derive(Clone)]
10pub struct RecordFailedTests {
11 failed_tests_path: Option<PathBuf>,
12}
13
14impl RecordFailedTests {
15 pub fn path(&self) -> Option<&Path> {
16 self.failed_tests_path.as_deref()
17 }
18}
19
20#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
31pub struct SetupFailedTestsFile;
32impl Step for SetupFailedTestsFile {
33 type Output = RecordFailedTests;
34
35 fn run(self, builder: &Builder<'_>) -> Self::Output {
36 if !builder.config.cmd.record() || builder.config.dry_run() {
37 return RecordFailedTests { failed_tests_path: None };
38 }
39
40 let failed_tests_path = builder.config.record_failed_tests_path.clone();
41 println!(
42 "setting up tracking of failed tests in {} (`--record` was passed)",
43 failed_tests_path.display()
44 );
45 if failed_tests_path.exists() {
46 println!("deleting previously recorded failed tests");
47 t!(fs::remove_file(&failed_tests_path));
48 }
49 RecordFailedTests { failed_tests_path: Some(failed_tests_path) }
50 }
51}
52
53pub fn collect_previously_failed_tests(failed_tests_file_path: &PathBuf) -> Vec<PathBuf> {
54 let mut paths = BTreeSet::new();
55
56 println!(
57 "`--rerun` passed so looking for failed tests in {}",
58 failed_tests_file_path.display()
59 );
60
61 let lines: Vec<String> = match File::open(failed_tests_file_path) {
62 Ok(f) => t!(BufReader::new(f).lines().collect()),
63 Err(e) if e.kind() == ErrorKind::NotFound => {
64 println!(
65 "WARNING: failed tests file doesn't exist: `--rerun` only makes sense after a previous test run with `--record`"
66 );
67 return Vec::new();
68 }
69 Err(e) => t!(Err(e)),
70 };
71
72 const MAX_RERUN_PRINTS: usize = 10;
73
74 for line in lines {
75 let trimmed = line.as_str().trim();
76 let without_revision =
77 trimmed.rsplit_once("#").map(|(before, _)| before).unwrap_or(trimmed);
78 let without_suite_prefix = without_revision
79 .strip_prefix("[")
80 .and_then(|rest| rest.split_once("]"))
81 .map(|(_, after)| after.trim())
82 .unwrap_or(without_revision);
83
84 let failed_test_path = PathBuf::from(without_suite_prefix.to_string());
85 if paths.insert(failed_test_path.clone()) {
86 if paths.len() == 1 {
87 println!("rerunning previously failed tests:");
88 }
89 if paths.len() <= MAX_RERUN_PRINTS {
90 println!(" {}", failed_test_path.display());
91 }
92 }
93 }
94
95 if paths.len() > MAX_RERUN_PRINTS {
96 println!(" and {} more...", paths.len() - MAX_RERUN_PRINTS)
97 }
98
99 if paths.is_empty() {
100 println!(
101 "WARNING: failed tests file doesn't contain any failed tests: `--rerun` only makes sense after a previous test run with `--record`"
102 );
103 }
104
105 paths.into_iter().collect()
106}