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
// This macro is useful for writing tests that deal with errors. It takes an expression of type
// `Result<_, Vec<Error>>` and a search string and asserts that the expressions evaluates to an
// `Err(_)` and that the string representation of at least one of the errors contains the given
// search string.
#[macro_export]
macro_rules! assert_fails {
($expr:expr, $search_str:expr $(,)?) => {{
// Macros are call-by-name, but we want call-by-value (or at least call-by-need) to avoid
// accidentally evaluating arguments multiple times. Here we force eager evaluation.
let expr = $expr;
let search_str = $search_str;
// Check that `$expr` fails and that the failure contains `$search_str`.
if let Err(errors) = expr {
let mut found_error = false;
let mut all_errors_string = String::new();
for error in &errors {
let error_string = error.to_string();
let _ = write!(all_errors_string, "{}\n", error_string);
if error_string.contains(search_str) {
found_error = true;
}
}
assert!(
found_error,
"The expression failed as expected, but the expected message was not found in \
any of the errors: {:?}.",
errors,
);
} else {
panic!("The expression was supposed to fail, but it succeeded.");
}
}};
}
// This macro is useful for writing equality tests for types that implement `Debug` but not `Eq`.
// It asserts that the debug representations of the two given expressions match.
#[macro_export]
macro_rules! assert_same {
($expr1:expr, $expr2:expr $(,)?) => {{
// Macros are call-by-name, but we want call-by-value (or at least call-by-need) to avoid
// accidentally evaluating arguments multiple times. Here we force eager evaluation.
let expr1 = $expr1;
let expr2 = $expr2;
// To aid in type inference, the following tells the compiler that the two expressions have
// the same type.
let mut _dummy = &expr1;
_dummy = &expr2;
// Assert that the expressions have the same debug representation.
assert_eq!(format!("{:?}", expr1), format!("{:?}", expr2));
}};
}
#[cfg(test)]
mod tests {
use {crate::error::Error, std::fmt::Write};
#[test]
#[should_panic(expected = "The expression was supposed to fail, but it succeeded.")]
fn assert_fails_empty() {
let success: Result<usize, Vec<Error>> = Ok(42);
assert_fails!(success, "search string");
}
#[test]
fn assert_fails_match() {
let success: Result<usize, Vec<Error>> = Err(vec![
Error {
message: "foo bar".to_owned(),
reason: None,
},
Error {
message: "foo search string bar".to_owned(),
reason: None,
},
Error {
message: "foo bar".to_owned(),
reason: None,
},
]);
assert_fails!(success, "search string");
}
#[test]
#[should_panic(
// This comma on the comment at the end of the line below is needed to satisfy the trailing
// commas check.
expected = "\
The expression failed as expected, but the expected message was not found in any of \
the errors: [\
Error { message: \"foo\", reason: None }, \
Error { message: \"bar\", reason: None }, \
Error { message: \"baz\", reason: None }\
].\
"
)]
fn assert_fails_mismatch() {
let success: Result<usize, Vec<Error>> = Err(vec![
Error {
message: "foo".to_owned(),
reason: None,
},
Error {
message: "bar".to_owned(),
reason: None,
},
Error {
message: "baz".to_owned(),
reason: None,
},
]);
assert_fails!(success, "search string");
}
#[test]
fn assert_same_match() {
assert_same!(42_i32, 42_i32);
}
#[test]
#[should_panic(expected = "42")]
fn assert_same_mismatch() {
assert_same!(42_i32, 43_i32);
}
}