compiletest/runtest/
ui.rs
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, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, TestOutput,
10 Truncated, UI_FIXED, WillExecute,
11};
12use crate::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);
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) {
80 panic!("couldn't write to {}: {e:?}", coverage_file_path);
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,
123 );
124 self.fatal_proc_rec(
125 &format!("{} errors occurred comparing output.", errors),
126 &proc_res,
127 );
128 }
129
130 let output_to_check = if let WillExecute::Yes = should_run {
131 let proc_res = self.exec_compiled_test();
132 let run_output_errors = if self.props.check_run_results {
133 self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
134 } else {
135 0
136 };
137 if run_output_errors > 0 {
138 self.fatal_proc_rec(
139 &format!("{} errors occurred comparing run output.", run_output_errors),
140 &proc_res,
141 );
142 }
143 if self.should_run_successfully(pm) {
144 if !proc_res.status.success() {
145 self.fatal_proc_rec("test run failed!", &proc_res);
146 }
147 } else if proc_res.status.success() {
148 self.fatal_proc_rec("test run succeeded!", &proc_res);
149 }
150
151 self.get_output(&proc_res)
152 } else {
153 self.get_output(&proc_res)
154 };
155
156 debug!(
157 "run_ui_test: explicit={:?} config.compare_mode={:?} \
158 proc_res.status={:?} props.error_patterns={:?}",
159 explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns
160 );
161
162 self.check_expected_errors(&proc_res);
163 self.check_all_error_patterns(&output_to_check, &proc_res);
164 self.check_forbid_output(&output_to_check, &proc_res);
165
166 if self.props.run_rustfix && self.config.compare_mode.is_none() {
167 let mut rustc = self.make_compile_args(
170 &self.expected_output_path(UI_FIXED),
171 TargetLocation::ThisFile(self.make_exe_name()),
172 emit_metadata,
173 AllowUnused::No,
174 LinkToAux::Yes,
175 Vec::new(),
176 );
177
178 if self.revision.is_some() {
184 let crate_name =
185 self.testpaths.file.file_stem().expect("test must have a file stem");
186 let crate_name = crate_name.replace('.', "__");
190 let crate_name = crate_name.replace('-', "_");
191 rustc.arg("--crate-name");
192 rustc.arg(crate_name);
193 }
194
195 let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
196 if !res.status.success() {
197 self.fatal_proc_rec("failed to compile fixed code", &res);
198 }
199 if !res.stderr.is_empty()
200 && !self.props.rustfix_only_machine_applicable
201 && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
202 {
203 self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
204 }
205 }
206 }
207}