Skip to main content

compiletest/runtest/
incremental.rs

1use std::sync::LazyLock;
2
3use crate::runtest::{Emit, TestCx, WillExecute};
4use crate::util::string_enum;
5
6string_enum!(
7    /// How far an incremental test revision should proceed through the compile/run
8    /// sequence, and whether the last step should succeed or fail, as determined
9    /// from the start of the revision name.
10    #[derive(Clone, Copy, PartialEq, Eq)]
11    enum IncrRevKind {
12        CheckPass => "cpass",
13        BuildFail => "bfail",
14        BuildPass => "bpass",
15        RunPass => "rpass",
16    }
17);
18
19impl IncrRevKind {
20    fn for_revision_name(rev_name: &str) -> Result<Self, &'static str> {
21        static MESSAGE: LazyLock<String> = LazyLock::new(|| {
22            let values = IncrRevKind::STR_VARIANTS
23                .iter()
24                .map(|s| format!("`{s}`"))
25                .collect::<Vec<_>>()
26                .join(", ");
27            format!("incremental revision name must begin with one of: {values}")
28        });
29
30        IncrRevKind::VARIANTS
31            .iter()
32            .copied()
33            .find(|kind| rev_name.starts_with(kind.to_str()))
34            .ok_or_else(|| MESSAGE.as_str())
35    }
36}
37
38impl TestCx<'_> {
39    /// Runs a single revision of an incremental test.
40    pub(super) fn run_incremental_test(&self) {
41        let revision = self.revision.expect("incremental tests require a list of revisions");
42
43        // Incremental workproduct directory should have already been created.
44        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
45        assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
46
47        if self.config.verbose {
48            write!(self.stdout, "revision={:?} props={:#?}", revision, self.props);
49        }
50
51        // Determine the revision kind from the revision name.
52        // The revision kind should be matched exhaustively to ensure that no cases are missed.
53        let rev_kind = IncrRevKind::for_revision_name(revision).unwrap_or_else(|e| self.fatal(e));
54
55        // Compile the test for this revision.
56        let emit = match rev_kind {
57            IncrRevKind::CheckPass => Emit::Metadata, // Do a check build.
58            IncrRevKind::BuildFail | IncrRevKind::BuildPass | IncrRevKind::RunPass => Emit::None,
59        };
60        let will_execute = match rev_kind {
61            IncrRevKind::CheckPass | IncrRevKind::BuildFail | IncrRevKind::BuildPass => {
62                WillExecute::No
63            }
64            IncrRevKind::RunPass => {
65                // Yes, unless running test binaries is disabled.
66                self.run_if_enabled()
67            }
68        };
69        let proc_res = &self.compile_test(will_execute, emit);
70
71        // Check the compiler's exit status.
72        match rev_kind {
73            IncrRevKind::CheckPass | IncrRevKind::BuildPass | IncrRevKind::RunPass => {
74                // Compilation should have succeeded.
75                if !proc_res.status.success() {
76                    self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
77                }
78            }
79
80            IncrRevKind::BuildFail => {
81                // Compilation should have failed, with the expected status code.
82                if proc_res.status.success() {
83                    self.fatal_proc_rec("incremental test did not emit an error", proc_res);
84                }
85                if !self.props.dont_check_failure_status {
86                    self.check_correct_failure_status(proc_res);
87                }
88            }
89        }
90
91        // Check compilation output.
92        let output_to_check = self.get_output(proc_res);
93        self.check_expected_errors(&proc_res);
94        self.check_all_error_patterns(&output_to_check, proc_res);
95        self.check_forbid_output(&output_to_check, proc_res);
96
97        // Run the binary and check its exit status, if appropriate.
98        match rev_kind {
99            IncrRevKind::CheckPass | IncrRevKind::BuildFail | IncrRevKind::BuildPass => {}
100            IncrRevKind::RunPass => {
101                if self.config.run_enabled() {
102                    let run_proc_res = self.exec_compiled_test();
103                    if !run_proc_res.status.success() {
104                        self.fatal_proc_rec("test run failed!", &run_proc_res);
105                    }
106                }
107            }
108        }
109    }
110}