| 1 | use crate::diff::{Diff, Render}; |
| 2 | use crate::error::Error; |
| 3 | use crate::{normalize, term, Expected, Test}; |
| 4 | use std::env; |
| 5 | use std::path::Path; |
| 6 | use std::process::Output; |
| 7 | use termcolor::Color::{self, *}; |
| 8 | |
| 9 | pub(crate) enum Level { |
| 10 | Fail, |
| 11 | Warn, |
| 12 | } |
| 13 | |
| 14 | pub(crate) use self::Level::*; |
| 15 | |
| 16 | pub(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 | |
| 28 | pub(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 | |
| 41 | pub(crate) fn no_tests_enabled() { |
| 42 | term::color(Yellow); |
| 43 | println!("There are no trybuild tests enabled yet." ); |
| 44 | term::reset(); |
| 45 | } |
| 46 | |
| 47 | pub(crate) fn ok() { |
| 48 | term::color(Green); |
| 49 | println!("ok" ); |
| 50 | term::reset(); |
| 51 | } |
| 52 | |
| 53 | pub(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 | |
| 71 | pub(crate) fn failed_to_build(stderr: &str) { |
| 72 | term::bold_color(Red); |
| 73 | println!("error" ); |
| 74 | snippet(Red, stderr); |
| 75 | println!(); |
| 76 | } |
| 77 | |
| 78 | pub(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 | |
| 87 | pub(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 | |
| 105 | pub(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 | |
| 118 | pub(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 | |
| 145 | pub(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 | |
| 183 | pub(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 | |
| 197 | pub(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 | |
| 208 | fn snippet(color: Color, content: &str) { |
| 209 | snippet_diff(color, content, None); |
| 210 | } |
| 211 | |
| 212 | fn 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 | |