compiletest/runtest/
ui.rs1use std::collections::HashSet;
2use std::fs::OpenOptions;
3use std::io::Write;
4
5use rustfix::{Filter, apply_suggestions, get_suggestions_from_json};
6use tracing::debug;
7
8use super::{
9 AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation,
10 TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
11};
12use crate::json;
13use crate::runtest::ProcRes;
14
15impl TestCx<'_> {
16 pub(super) fn run_ui_test(&self) {
17 if let Some(FailMode::Build) = self.props.fail_mode {
18 let pm = Some(PassMode::Check);
20 let proc_res =
21 self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
22 self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
23 }
24
25 let pm = self.pass_mode();
26 let should_run = self.should_run(pm);
27 let emit_metadata = self.should_emit_metadata(pm);
28 let proc_res = self.compile_test(should_run, emit_metadata);
29 self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
30 if matches!(proc_res.truncated, Truncated::Yes)
31 && !self.props.dont_check_compiler_stdout
32 && !self.props.dont_check_compiler_stderr
33 {
34 self.fatal_proc_rec(
35 "compiler output got truncated, cannot compare with reference file",
36 &proc_res,
37 );
38 }
39
40 let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format"));
44
45 let expected_fixed = self.load_expected_output(UI_FIXED);
46
47 self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]);
48
49 let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
50 let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
51
52 if self.config.compare_mode.is_some() {
53 } else if self.config.rustfix_coverage {
55 let suggestions = get_suggestions_from_json(
61 &rustfix_input,
62 &HashSet::new(),
63 Filter::MachineApplicableOnly,
64 )
65 .unwrap_or_default();
66 if !suggestions.is_empty()
67 && !self.props.run_rustfix
68 && !self.props.rustfix_only_machine_applicable
69 {
70 let mut coverage_file_path = self.config.build_test_suite_root.clone();
71 coverage_file_path.push("rustfix_missing_coverage.txt");
72 debug!("coverage_file_path: {}", coverage_file_path);
73
74 let mut file = OpenOptions::new()
75 .create(true)
76 .append(true)
77 .open(coverage_file_path.as_path())
78 .expect("could not create or open file");
79
80 if let Err(e) = writeln!(file, "{}", self.testpaths.file) {
81 panic!("couldn't write to {}: {e:?}", coverage_file_path);
82 }
83 }
84 } else if self.props.run_rustfix {
85 let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
87 let suggestions = get_suggestions_from_json(
88 &rustfix_input,
89 &HashSet::new(),
90 if self.props.rustfix_only_machine_applicable {
91 Filter::MachineApplicableOnly
92 } else {
93 Filter::Everything
94 },
95 )
96 .unwrap();
97 let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| {
98 panic!(
99 "failed to apply suggestions for {:?} with rustfix: {}",
100 self.testpaths.file, e
101 )
102 });
103
104 if self
105 .compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed)
106 .should_error()
107 {
108 errors += 1;
109 }
110 } else if !expected_fixed.is_empty() {
111 panic!(
112 "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
113 file was found"
114 );
115 }
116
117 if errors > 0 {
118 println!("To update references, rerun the tests and pass the `--bless` flag");
119 let relative_path_to_file =
120 self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
121 println!(
122 "To only update this specific test, also pass `--test-args {}`",
123 relative_path_to_file,
124 );
125 self.fatal_proc_rec(
126 &format!("{} errors occurred comparing output.", errors),
127 &proc_res,
128 );
129 }
130
131 let mut run_proc_res: Option<ProcRes> = None;
134 let output_to_check = if let WillExecute::Yes = should_run {
135 let proc_res = self.exec_compiled_test();
136 let run_output_errors = if self.props.check_run_results {
137 self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
138 } else {
139 0
140 };
141 if run_output_errors > 0 {
142 self.fatal_proc_rec(
143 &format!("{} errors occurred comparing run output.", run_output_errors),
144 &proc_res,
145 );
146 }
147 let code = proc_res.status.code();
148 let run_result = if proc_res.status.success() {
149 RunResult::Pass
150 } else if code.is_some_and(|c| c >= 1 && c <= 127) {
151 RunResult::Fail
152 } else {
153 RunResult::Crash
154 };
155 let pass_hint = format!("code={code:?} so test would pass with `{run_result}`");
158 if self.should_run_successfully(pm) {
159 if run_result != RunResult::Pass {
160 self.fatal_proc_rec(
161 &format!("test did not exit with success! {pass_hint}"),
162 &proc_res,
163 );
164 }
165 } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Fail)) {
166 let crash_ok = !self.config.can_unwind();
170 if run_result != RunResult::Fail && !(crash_ok && run_result == RunResult::Crash) {
171 let err = if crash_ok {
172 format!(
173 "test did not exit with failure or crash (`{}` can't unwind)! {pass_hint}",
174 self.config.target
175 )
176 } else {
177 format!("test did not exit with failure! {pass_hint}")
178 };
179 self.fatal_proc_rec(&err, &proc_res);
180 }
181 } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Crash)) {
182 if run_result != RunResult::Crash {
183 self.fatal_proc_rec(&format!("test did not crash! {pass_hint}"), &proc_res);
184 }
185 } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::FailOrCrash)) {
186 if run_result != RunResult::Fail && run_result != RunResult::Crash {
187 self.fatal_proc_rec(
188 &format!("test did not exit with failure or crash! {pass_hint}"),
189 &proc_res,
190 );
191 }
192 } else {
193 unreachable!("run_ui_test() must not be called if the test should not run");
194 }
195
196 let output = self.get_output(&proc_res);
197 run_proc_res = Some(proc_res);
199 output
200 } else {
201 self.get_output(&proc_res)
202 };
203
204 debug!(
205 "run_ui_test: explicit={:?} config.compare_mode={:?} \
206 proc_res.status={:?} props.error_patterns={:?}",
207 explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns
208 );
209
210 self.check_expected_errors(&proc_res);
212
213 let pattern_proc_res = run_proc_res.as_ref().unwrap_or(&proc_res);
216 self.check_all_error_patterns(&output_to_check, pattern_proc_res);
217 self.check_forbid_output(&output_to_check, pattern_proc_res);
218
219 if self.props.run_rustfix && self.config.compare_mode.is_none() {
220 let mut rustc = self.make_compile_args(
223 &self.expected_output_path(UI_FIXED),
224 TargetLocation::ThisFile(self.make_exe_name()),
225 emit_metadata,
226 AllowUnused::No,
227 LinkToAux::Yes,
228 Vec::new(),
229 );
230
231 if self.revision.is_some() {
237 let crate_name =
238 self.testpaths.file.file_stem().expect("test must have a file stem");
239 let crate_name = crate_name.replace('.', "__");
243 let crate_name = crate_name.replace('-', "_");
244 rustc.arg("--crate-name");
245 rustc.arg(crate_name);
246 }
247
248 let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
249 if !res.status.success() {
250 self.fatal_proc_rec("failed to compile fixed code", &res);
251 }
252 if !res.stderr.is_empty()
253 && !self.props.rustfix_only_machine_applicable
254 && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
255 {
256 self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
257 }
258 }
259 }
260}