1use 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, ErrorKind, FailMode, LinkToAux, PassMode, TargetLocation, TestCx,
10 TestOutput, Truncated, UI_FIXED, WillExecute,
11};
12use crate::{errors, json};
13
14impl TestCx<'_> {
15 pub(super) fn run_ui_test(&self) {
16 if let Some(FailMode::Build) = self.props.fail_mode {
17 let pm = Some(PassMode::Check);
19 let proc_res =
20 self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
21 self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
22 }
23
24 let pm = self.pass_mode();
25 let should_run = self.should_run(pm);
26 let emit_metadata = self.should_emit_metadata(pm);
27 let proc_res = self.compile_test(should_run, emit_metadata);
28 self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
29 if matches!(proc_res.truncated, Truncated::Yes)
30 && !self.props.dont_check_compiler_stdout
31 && !self.props.dont_check_compiler_stderr
32 {
33 self.fatal_proc_rec(
34 "compiler output got truncated, cannot compare with reference file",
35 &proc_res,
36 );
37 }
38
39 let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format"));
43
44 let expected_fixed = self.load_expected_output(UI_FIXED);
45
46 self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]);
47
48 let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
49 let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
50
51 if self.config.compare_mode.is_some() {
52 } else if self.config.rustfix_coverage {
54 let suggestions = get_suggestions_from_json(
60 &rustfix_input,
61 &HashSet::new(),
62 Filter::MachineApplicableOnly,
63 )
64 .unwrap_or_default();
65 if !suggestions.is_empty()
66 && !self.props.run_rustfix
67 && !self.props.rustfix_only_machine_applicable
68 {
69 let mut coverage_file_path = self.config.build_test_suite_root.clone();
70 coverage_file_path.push("rustfix_missing_coverage.txt");
71 debug!("coverage_file_path: {}", coverage_file_path.display());
72
73 let mut file = OpenOptions::new()
74 .create(true)
75 .append(true)
76 .open(coverage_file_path.as_path())
77 .expect("could not create or open file");
78
79 if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) {
80 panic!("couldn't write to {}: {e:?}", coverage_file_path.display());
81 }
82 }
83 } else if self.props.run_rustfix {
84 let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
86 let suggestions = get_suggestions_from_json(
87 &rustfix_input,
88 &HashSet::new(),
89 if self.props.rustfix_only_machine_applicable {
90 Filter::MachineApplicableOnly
91 } else {
92 Filter::Everything
93 },
94 )
95 .unwrap();
96 let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| {
97 panic!(
98 "failed to apply suggestions for {:?} with rustfix: {}",
99 self.testpaths.file, e
100 )
101 });
102
103 if self
104 .compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed)
105 .should_error()
106 {
107 errors += 1;
108 }
109 } else if !expected_fixed.is_empty() {
110 panic!(
111 "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
112 file was found"
113 );
114 }
115
116 if errors > 0 {
117 println!("To update references, rerun the tests and pass the `--bless` flag");
118 let relative_path_to_file =
119 self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
120 println!(
121 "To only update this specific test, also pass `--test-args {}`",
122 relative_path_to_file.display(),
123 );
124 self.fatal_proc_rec(
125 &format!("{} errors occurred comparing output.", errors),
126 &proc_res,
127 );
128 }
129
130 let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
131
132 if let WillExecute::Yes = should_run {
133 let proc_res = self.exec_compiled_test();
134 let run_output_errors = if self.props.check_run_results {
135 self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
136 } else {
137 0
138 };
139 if run_output_errors > 0 {
140 self.fatal_proc_rec(
141 &format!("{} errors occurred comparing run output.", run_output_errors),
142 &proc_res,
143 );
144 }
145 if self.should_run_successfully(pm) {
146 if !proc_res.status.success() {
147 self.fatal_proc_rec("test run failed!", &proc_res);
148 }
149 } else if proc_res.status.success() {
150 self.fatal_proc_rec("test run succeeded!", &proc_res);
151 }
152
153 let output_to_check = self.get_output(&proc_res);
154 if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
155 {
156 self.check_all_error_patterns(&output_to_check, &proc_res, pm);
158 }
159 self.check_forbid_output(&output_to_check, &proc_res)
160 }
161
162 debug!(
163 "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
164 proc_res.status={:?} props.error_patterns={:?}",
165 explicit,
166 self.config.compare_mode,
167 expected_errors,
168 proc_res.status,
169 self.props.error_patterns
170 );
171
172 let check_patterns = should_run == WillExecute::No
173 && (!self.props.error_patterns.is_empty()
174 || !self.props.regex_error_patterns.is_empty());
175 if !explicit && self.config.compare_mode.is_none() {
176 let check_annotations = !check_patterns || !expected_errors.is_empty();
177
178 if check_annotations {
179 self.check_expected_errors(expected_errors, &proc_res);
181 }
182 } else if explicit && !expected_errors.is_empty() {
183 let msg = format!(
184 "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead",
185 expected_errors[0].line_num,
186 expected_errors[0].kind.unwrap_or(ErrorKind::Error),
187 );
188 self.fatal(&msg);
189 }
190 let output_to_check = self.get_output(&proc_res);
191 if check_patterns {
192 self.check_all_error_patterns(&output_to_check, &proc_res, pm);
194 }
195 self.check_forbid_output(&output_to_check, &proc_res);
196
197 if self.props.run_rustfix && self.config.compare_mode.is_none() {
198 let mut rustc = self.make_compile_args(
201 &self.expected_output_path(UI_FIXED),
202 TargetLocation::ThisFile(self.make_exe_name()),
203 emit_metadata,
204 AllowUnused::No,
205 LinkToAux::Yes,
206 Vec::new(),
207 );
208
209 if self.revision.is_some() {
215 let crate_name =
216 self.testpaths.file.file_stem().expect("test must have a file stem");
217 let crate_name =
219 crate_name.to_str().expect("crate name implies file name must be valid UTF-8");
220 let crate_name = crate_name.replace('.', "__");
223 let crate_name = crate_name.replace('-', "_");
224 rustc.arg("--crate-name");
225 rustc.arg(crate_name);
226 }
227
228 let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
229 if !res.status.success() {
230 self.fatal_proc_rec("failed to compile fixed code", &res);
231 }
232 if !res.stderr.is_empty()
233 && !self.props.rustfix_only_machine_applicable
234 && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
235 {
236 self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
237 }
238 }
239 }
240}