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, ShouldRun, 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 should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
36 run.never()
37 }
38
39 fn run(self, builder: &Builder<'_>) -> Self::Output {
40 if !builder.config.cmd.record() || builder.config.dry_run() {
41 return RecordFailedTests { failed_tests_path: None };
42 }
43
44 let failed_tests_path = builder.config.record_failed_tests_path.clone();
45 println!(
46 "setting up tracking of failed tests in {} (`--record` was passed)",
47 failed_tests_path.display()
48 );
49 if failed_tests_path.exists() {
50 println!("deleting previously recorded failed tests");
51 t!(fs::remove_file(&failed_tests_path));
52 }
53 RecordFailedTests { failed_tests_path: Some(failed_tests_path) }
54 }
55}
56
57pub fn collect_previously_failed_tests(failed_tests_file_path: &PathBuf) -> Vec<PathBuf> {
58 let mut paths = BTreeSet::new();
59
60 println!(
61 "`--rerun` passed so looking for failed tests in {}",
62 failed_tests_file_path.display()
63 );
64
65 let lines: Vec<String> = match File::open(failed_tests_file_path) {
66 Ok(f) => t!(BufReader::new(f).lines().collect()),
67 Err(e) if e.kind() == ErrorKind::NotFound => {
68 println!(
69 "WARNING: failed tests file doesn't exist: `--rerun` only makes sense after a previous test run with `--record`"
70 );
71 return Vec::new();
72 }
73 Err(e) => t!(Err(e)),
74 };
75
76 const MAX_RERUN_PRINTS: usize = 10;
77
78 for line in lines {
79 let trimmed = line.as_str().trim();
80 let without_revision =
81 trimmed.rsplit_once("#").map(|(before, _)| before).unwrap_or(trimmed);
82 let without_suite_prefix = without_revision
83 .strip_prefix("[")
84 .and_then(|rest| rest.split_once("]"))
85 .map(|(_, after)| after.trim())
86 .unwrap_or(without_revision);
87
88 let failed_test_path = PathBuf::from(without_suite_prefix.to_string());
89 if paths.insert(failed_test_path.clone()) {
90 if paths.len() == 1 {
91 println!("rerunning previously failed tests:");
92 }
93 if paths.len() <= MAX_RERUN_PRINTS {
94 println!(" {}", failed_test_path.display());
95 }
96 }
97 }
98
99 if paths.len() > MAX_RERUN_PRINTS {
100 println!(" and {} more...", paths.len() - MAX_RERUN_PRINTS)
101 }
102
103 if paths.is_empty() {
104 println!(
105 "WARNING: failed tests file doesn't contain any failed tests: `--rerun` only makes sense after a previous test run with `--record`"
106 );
107 }
108
109 paths.into_iter().collect()
110}