1use crate::diff::{Diff, Render};
2use crate::error::Error;
3use crate::{normalize, term, Expected, Test};
4use std::env;
5use std::path::Path;
6use std::process::Output;
7use termcolor::Color::{self, *};
8
9pub(crate) enum Level {
10 Fail,
11 Warn,
12}
13
14pub(crate) use self::Level::*;
15
16pub(crate) fn prepare_fail(err: Error) {
17 if err.already_printed() {
18 return;
19 }
20
21 term::bold_color(Red);
22 print!("ERROR");
23 term::reset();
24 println!(": {}", err);
25 println!();
26}
27
28pub(crate) fn test_fail(err: Error) {
29 if err.already_printed() {
30 return;
31 }
32
33 term::bold_color(Red);
34 println!("error");
35 term::color(Red);
36 println!("{}", err);
37 term::reset();
38 println!();
39}
40
41pub(crate) fn no_tests_enabled() {
42 term::color(Yellow);
43 println!("There are no trybuild tests enabled yet.");
44 term::reset();
45}
46
47pub(crate) fn ok() {
48 term::color(Green);
49 println!("ok");
50 term::reset();
51}
52
53pub(crate) fn begin_test(test: &Test, show_expected: bool) {
54 let display_name = test.path.as_os_str().to_string_lossy();
55
56 print!("test ");
57 term::bold();
58 print!("{}", display_name);
59 term::reset();
60
61 if show_expected {
62 match test.expected {
63 Expected::Pass => print!(" [should pass]"),
64 Expected::CompileFail => print!(" [should fail to compile]"),
65 }
66 }
67
68 print!(" ... ");
69}
70
71pub(crate) fn failed_to_build(stderr: &str) {
72 term::bold_color(Red);
73 println!("error");
74 snippet(Red, stderr);
75 println!();
76}
77
78pub(crate) fn should_not_have_compiled() {
79 term::bold_color(Red);
80 println!("error");
81 term::color(Red);
82 println!("Expected test case to fail to compile, but it succeeded.");
83 term::reset();
84 println!();
85}
86
87pub(crate) fn write_stderr_wip(wip_path: &Path, stderr_path: &Path, stderr: &str) {
88 let wip_path = wip_path.to_string_lossy();
89 let stderr_path = stderr_path.to_string_lossy();
90
91 term::bold_color(Yellow);
92 println!("wip");
93 println!();
94 print!("NOTE");
95 term::reset();
96 println!(": writing the following output to `{}`.", wip_path);
97 println!(
98 "Move this file to `{}` to accept it as correct.",
99 stderr_path,
100 );
101 snippet(Yellow, stderr);
102 println!();
103}
104
105pub(crate) fn overwrite_stderr(stderr_path: &Path, stderr: &str) {
106 let stderr_path = stderr_path.to_string_lossy();
107
108 term::bold_color(Yellow);
109 println!("wip");
110 println!();
111 print!("NOTE");
112 term::reset();
113 println!(": writing the following output to `{}`.", stderr_path);
114 snippet(Yellow, stderr);
115 println!();
116}
117
118pub(crate) fn mismatch(expected: &str, actual: &str) {
119 term::bold_color(Red);
120 println!("mismatch");
121 term::reset();
122 println!();
123 let diff = if env::var_os("TERM").map_or(true, |term| term == "dumb") {
124 // No diff in dumb terminal or when TERM is unset.
125 None
126 } else {
127 Diff::compute(expected, actual)
128 };
129 term::bold_color(Blue);
130 println!("EXPECTED:");
131 snippet_diff(Blue, expected, diff.as_ref());
132 println!();
133 term::bold_color(Red);
134 println!("ACTUAL OUTPUT:");
135 snippet_diff(Red, actual, diff.as_ref());
136 print!("note: If the ");
137 term::color(Red);
138 print!("actual output");
139 term::reset();
140 println!(" is the correct output you can bless it by rerunning");
141 println!(" your test with the environment variable TRYBUILD=overwrite");
142 println!();
143}
144
145pub(crate) fn output(warnings: &str, output: &Output) {
146 let success = output.status.success();
147 let stdout = normalize::trim(&output.stdout);
148 let stderr = normalize::trim(&output.stderr);
149 let has_output = !stdout.is_empty() || !stderr.is_empty();
150
151 if success {
152 ok();
153 if has_output || !warnings.is_empty() {
154 println!();
155 }
156 } else {
157 term::bold_color(Red);
158 println!("error");
159 term::color(Red);
160 if has_output {
161 println!("Test case failed at runtime.");
162 } else {
163 println!("Execution of the test case was unsuccessful but there was no output.");
164 }
165 term::reset();
166 println!();
167 }
168
169 self::warnings(warnings);
170
171 let color = if success { Yellow } else { Red };
172
173 for (name, content) in &[("STDOUT", stdout), ("STDERR", stderr)] {
174 if !content.is_empty() {
175 term::bold_color(color);
176 println!("{}:", name);
177 snippet(color, &normalize::trim(content));
178 println!();
179 }
180 }
181}
182
183pub(crate) fn fail_output(level: Level, stdout: &str) {
184 let color = match level {
185 Fail => Red,
186 Warn => Yellow,
187 };
188
189 if !stdout.is_empty() {
190 term::bold_color(color);
191 println!("STDOUT:");
192 snippet(color, &normalize::trim(stdout));
193 println!();
194 }
195}
196
197pub(crate) fn warnings(warnings: &str) {
198 if warnings.is_empty() {
199 return;
200 }
201
202 term::bold_color(Yellow);
203 println!("WARNINGS:");
204 snippet(Yellow, warnings);
205 println!();
206}
207
208fn snippet(color: Color, content: &str) {
209 snippet_diff(color, content, None);
210}
211
212fn snippet_diff(color: Color, content: &str, diff: Option<&Diff>) {
213 fn dotted_line() {
214 println!("{}", "┈".repeat(60));
215 }
216
217 term::color(color);
218 dotted_line();
219
220 match diff {
221 Some(diff) => {
222 for chunk in diff.iter(content) {
223 match chunk {
224 Render::Common(s) => {
225 term::color(color);
226 print!("{}", s);
227 }
228 Render::Unique(s) => {
229 term::bold_color(color);
230 print!("\x1B[7m{}", s);
231 }
232 }
233 }
234 }
235 None => print!("{}", content),
236 }
237
238 term::color(color);
239 dotted_line();
240 term::reset();
241}
242