1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! Structures to the track the results generated by running a test.
use crate::{cli, errors::RuntError, printer};
use std::path::PathBuf;
use tokio::fs;
use super::suite;
/// Track the state of TestResult.
#[derive(Debug, PartialEq)]
pub enum State {
/// The test timed out.
Timeout,
/// The comparison succeeded.
Correct,
/// The test was skipped because of a .skip file
Skip,
/// The .expect file is missing. Contains the generated expectation string.
Missing(String),
/// The comparison failed. Contains the the generated expectation string
/// and the contents of the expect file.
Mismatch(
String, // Generated expect string.
String, // Contents of the expect file.
),
}
/// Store information related to a test.
#[derive(Debug)]
pub struct Test {
/// Path of the test
pub path: PathBuf,
/// Location of the expect string.
pub expect_path: PathBuf,
/// Result of comparison
pub state: State,
/// The results of this structure were saved.
pub saved: bool,
/// Id for the test suite that owns this test.
pub test_suite: suite::Id,
}
impl Test {
/// Save the results of the test suite into the expect file.
pub async fn save_results(&mut self) -> Result<(), RuntError> {
match &self.state {
State::Skip | State::Correct | State::Timeout => Ok(()),
State::Missing(expect) | State::Mismatch(expect, _) => {
self.saved = true;
fs::write(&self.expect_path, expect).await.map_err(|err| {
RuntError(format!(
"{}: {}.",
self.expect_path.to_str().unwrap(),
err
))
})
}
}
}
// Helper method to select if this test should be accepted with
// opt.
fn with_only_opt(&self, only: &cli::OnlyOpt) -> bool {
use cli::OnlyOpt as O;
match (only, &self.state) {
(O::Fail, State::Mismatch(..)) => true,
(O::Fail, State::Timeout) => true,
(O::Pass, State::Correct) => true,
(O::Missing, State::Missing(..)) => true,
(O::Fail, _) | (O::Pass, _) | (O::Missing, _) => false,
}
}
/// Returns true if the current options require this test to be saved.
pub fn should_save(&self, opts: &cli::Opts) -> bool {
if !opts.save {
return false;
}
if let Some(only) = &opts.post_filter {
return self.with_only_opt(only);
}
true
}
/// Returns true if this test should be printed with the current options.
pub fn should_print(&self, opts: &cli::Opts) -> bool {
// Print everything if verbose mode is enabled
if opts.verbose {
return true;
}
// Selectively print things if post_filter is enabled.
if let Some(only) = &opts.post_filter {
return self.with_only_opt(only);
}
// Otherwise just print failing and missing tests
!matches!(self.state, State::Correct)
}
/// Generate colorized string to report the results of this test.
pub fn report_str(
&self,
suite: Option<&String>,
show_diff: bool,
) -> String {
use colored::*;
let mut buf = String::new();
let path_str = self.path.to_str().unwrap();
match &self.state {
State::Skip => {
assert!(!self.saved, "Skipped files cannot be saved");
buf.push_str(&"- ".yellow().dimmed());
suite.into_iter().for_each(|suite_name| {
buf.push_str(&suite_name.bold().yellow().dimmed());
buf.push_str(&":".yellow().dimmed())
});
buf.push_str(&path_str.yellow().dimmed());
}
State::Missing(expect_string) => {
buf.push_str(&"? ".yellow());
suite.into_iter().for_each(|suite_name| {
buf.push_str(&suite_name.bold().yellow());
buf.push_str(&":".yellow())
});
buf.push_str(&path_str.yellow());
if self.saved {
buf.push_str(&" (saved)".dimmed());
}
if show_diff {
let diff = printer::gen_diff("", expect_string);
buf.push('\n');
buf.push_str(&diff);
}
}
State::Timeout => {
buf.push_str(&"✗ ".red());
suite.into_iter().for_each(|suite_name| {
buf.push_str(&suite_name.bold().red());
buf.push_str(&":".red())
});
buf.push_str(&path_str.red());
buf.push_str(&" (timeout)".dimmed());
}
State::Correct => {
buf.push_str(&"✓ ".green());
suite.into_iter().for_each(|suite_name| {
buf.push_str(&suite_name.bold().green());
buf.push_str(&":".green())
});
buf.push_str(&path_str.green());
}
State::Mismatch(expect_string, contents) => {
buf.push_str(&"✗ ".red());
suite.into_iter().for_each(|suite_name| {
buf.push_str(&suite_name.bold().red());
buf.push_str(&":".red())
});
buf.push_str(&path_str.red());
if self.saved {
buf.push_str(&" (saved)".dimmed());
}
if show_diff {
let diff = printer::gen_diff(contents, expect_string);
buf.push('\n');
buf.push_str(&diff);
}
}
};
buf
}
}