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 =
42            self.variant.revision().expect("incremental tests require a list of revisions");
43
44        // Incremental workproduct directory should have already been created.
45        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
46        assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
47
48        if self.config.verbose {
49            write!(self.stdout, "revision={:?} props={:#?}", revision, self.props);
50        }
51
52        // Determine the revision kind from the revision name.
53        // The revision kind should be matched exhaustively to ensure that no cases are missed.
54        let rev_kind = IncrRevKind::for_revision_name(revision).unwrap_or_else(|e| self.fatal(e));
55
56        // Compile the test for this revision.
57        let emit = match rev_kind {
58            IncrRevKind::CheckPass => Emit::Metadata, // Do a check build.
59            IncrRevKind::BuildFail | IncrRevKind::BuildPass | IncrRevKind::RunPass => Emit::None,
60        };
61        let will_execute = match rev_kind {
62            IncrRevKind::CheckPass | IncrRevKind::BuildFail | IncrRevKind::BuildPass => {
63                WillExecute::No
64            }
65            IncrRevKind::RunPass => {
66                // Yes, unless running test binaries is disabled.
67                self.run_if_enabled()
68            }
69        };
70        let proc_res = &self.compile_test(will_execute, emit);
71
72        // Check the compiler's exit status.
73        match rev_kind {
74            IncrRevKind::CheckPass | IncrRevKind::BuildPass | IncrRevKind::RunPass => {
75                // Compilation should have succeeded.
76                if !proc_res.status.success() {
77                    self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
78                }
79            }
80
81            IncrRevKind::BuildFail => {
82                // Compilation should have failed, with the expected status code.
83                if proc_res.status.success() {
84                    self.fatal_proc_rec("incremental test did not emit an error", proc_res);
85                }
86                if !self.props.dont_check_failure_status {
87                    self.check_correct_failure_status(proc_res);
88                }
89            }
90        }
91
92        // Check compilation output.
93        let output_to_check = self.get_output(proc_res);
94        self.check_expected_errors(&proc_res);
95        self.check_all_error_patterns(&output_to_check, proc_res);
96        self.check_forbid_output(&output_to_check, proc_res);
97
98        // Run the binary and check its exit status, if appropriate.
99        match rev_kind {
100            IncrRevKind::CheckPass | IncrRevKind::BuildFail | IncrRevKind::BuildPass => {}
101            IncrRevKind::RunPass => {
102                if self.config.run_enabled() {
103                    let run_proc_res = self.exec_compiled_test();
104                    if !run_proc_res.status.success() {
105                        self.fatal_proc_rec("test run failed!", &run_proc_res);
106                    }
107                }
108            }
109        }
110    }
111}