]>
Commit | Line | Data |
---|---|---|
5099ac24 FG |
1 | //! # Pretty Assertions |
2 | //! | |
3 | //! When writing tests in Rust, you'll probably use `assert_eq!(a, b)` _a lot_. | |
4 | //! | |
5 | //! If such a test fails, it will present all the details of `a` and `b`. | |
6 | //! But you have to spot the differences yourself, which is not always straightforward, | |
7 | //! like here: | |
8 | //! | |
9 | //! ![standard assertion](https://raw.githubusercontent.com/colin-kiegel/rust-pretty-assertions/2d2357ff56d22c51a86b2f1cfe6efcee9f5a8081/examples/standard_assertion.png) | |
10 | //! | |
11 | //! Wouldn't that task be _much_ easier with a colorful diff? | |
12 | //! | |
13 | //! ![pretty assertion](https://raw.githubusercontent.com/colin-kiegel/rust-pretty-assertions/2d2357ff56d22c51a86b2f1cfe6efcee9f5a8081/examples/pretty_assertion.png) | |
14 | //! | |
15 | //! Yep — and you only need **one line of code** to make it happen: | |
16 | //! | |
17 | //! ```rust | |
18 | //! use pretty_assertions::{assert_eq, assert_ne}; | |
19 | //! ``` | |
20 | //! | |
21 | //! <details> | |
22 | //! <summary>Show the example behind the screenshots above.</summary> | |
23 | //! | |
24 | //! ```rust,should_panic | |
25 | //! // 1. add the `pretty_assertions` dependency to `Cargo.toml`. | |
26 | //! // 2. insert this line at the top of each module, as needed | |
27 | //! use pretty_assertions::{assert_eq, assert_ne}; | |
28 | //! | |
29 | //! #[derive(Debug, PartialEq)] | |
30 | //! struct Foo { | |
31 | //! lorem: &'static str, | |
32 | //! ipsum: u32, | |
33 | //! dolor: Result<String, String>, | |
34 | //! } | |
35 | //! | |
36 | //! let x = Some(Foo { lorem: "Hello World!", ipsum: 42, dolor: Ok("hey".to_string())}); | |
37 | //! let y = Some(Foo { lorem: "Hello Wrold!", ipsum: 42, dolor: Ok("hey ho!".to_string())}); | |
38 | //! | |
39 | //! assert_eq!(x, y); | |
40 | //! ``` | |
41 | //! </details> | |
42 | //! | |
43 | //! ## Tip | |
44 | //! | |
45 | //! Specify it as [`[dev-dependencies]`](http://doc.crates.io/specifying-dependencies.html#development-dependencies) | |
46 | //! and it will only be used for compiling tests, examples, and benchmarks. | |
47 | //! This way the compile time of `cargo build` won't be affected! | |
48 | //! | |
49 | //! Also add `#[cfg(test)]` to your `use` statements, like this: | |
50 | //! | |
51 | //! ```rust | |
52 | //! #[cfg(test)] | |
53 | //! use pretty_assertions::{assert_eq, assert_ne}; | |
54 | //! ``` | |
55 | //! | |
56 | //! ## Note | |
57 | //! | |
58 | //! * Since `Rust 2018` edition, you need to declare | |
59 | //! `use pretty_assertions::{assert_eq, assert_ne};` per module. | |
60 | //! Before you would write `#[macro_use] extern crate pretty_assertions;`. | |
61 | //! * The replacement is only effective in your own crate, not in other libraries | |
62 | //! you include. | |
63 | //! * `assert_ne` is also switched to multi-line presentation, but does _not_ show | |
64 | //! a diff. | |
65 | ||
66 | #![deny(clippy::all, missing_docs, unsafe_code)] | |
67 | ||
68 | pub use ansi_term::Style; | |
69 | use std::fmt::{self, Debug, Display}; | |
70 | ||
71 | mod printer; | |
72 | ||
73 | #[cfg(windows)] | |
74 | use ctor::*; | |
75 | #[cfg(windows)] | |
76 | #[ctor] | |
77 | fn init() { | |
78 | output_vt100::try_init().ok(); // Do not panic on fail | |
79 | } | |
80 | ||
81 | /// A comparison of two values. | |
82 | /// | |
83 | /// Where both values implement `Debug`, the comparison can be displayed as a pretty diff. | |
84 | /// | |
85 | /// ``` | |
86 | /// use pretty_assertions::Comparison; | |
87 | /// | |
88 | /// print!("{}", Comparison::new(&123, &134)); | |
89 | /// ``` | |
90 | /// | |
91 | /// The values may have different types, although in practice they are usually the same. | |
92 | pub struct Comparison<'a, TLeft, TRight> | |
93 | where | |
94 | TLeft: ?Sized, | |
95 | TRight: ?Sized, | |
96 | { | |
97 | left: &'a TLeft, | |
98 | right: &'a TRight, | |
99 | } | |
100 | ||
101 | impl<'a, TLeft, TRight> Comparison<'a, TLeft, TRight> | |
102 | where | |
103 | TLeft: ?Sized, | |
104 | TRight: ?Sized, | |
105 | { | |
106 | /// Store two values to be compared in future. | |
107 | /// | |
108 | /// Expensive diffing is deferred until calling `Debug::fmt`. | |
109 | pub fn new(left: &'a TLeft, right: &'a TRight) -> Comparison<'a, TLeft, TRight> { | |
110 | Comparison { left, right } | |
111 | } | |
112 | } | |
113 | ||
114 | impl<'a, TLeft, TRight> Display for Comparison<'a, TLeft, TRight> | |
115 | where | |
116 | TLeft: Debug + ?Sized, | |
117 | TRight: Debug + ?Sized, | |
118 | { | |
119 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
120 | // To diff arbitary types, render them as debug strings | |
121 | let left_debug = format!("{:#?}", self.left); | |
122 | let right_debug = format!("{:#?}", self.right); | |
123 | // And then diff the debug output | |
124 | printer::write_header(f)?; | |
125 | printer::write_lines(f, &left_debug, &right_debug) | |
126 | } | |
127 | } | |
128 | ||
129 | /// Asserts that two expressions are equal to each other (using [`PartialEq`]). | |
130 | /// | |
131 | /// On panic, this macro will print a diff derived from [`Debug`] representation of | |
132 | /// each value. | |
133 | /// | |
134 | /// This is a drop in replacement for [`std::assert_eq!`]. | |
135 | /// You can provide a custom panic message if desired. | |
136 | /// | |
137 | /// # Examples | |
138 | /// | |
139 | /// ``` | |
140 | /// use pretty_assertions::assert_eq; | |
141 | /// | |
142 | /// let a = 3; | |
143 | /// let b = 1 + 2; | |
144 | /// assert_eq!(a, b); | |
145 | /// | |
146 | /// assert_eq!(a, b, "we are testing addition with {} and {}", a, b); | |
147 | /// ``` | |
148 | #[macro_export] | |
149 | macro_rules! assert_eq { | |
150 | ($left:expr , $right:expr,) => ({ | |
151 | $crate::assert_eq!($left, $right) | |
152 | }); | |
153 | ($left:expr , $right:expr) => ({ | |
154 | match (&($left), &($right)) { | |
155 | (left_val, right_val) => { | |
156 | if !(*left_val == *right_val) { | |
157 | ::std::panic!("assertion failed: `(left == right)`\ | |
158 | \n\ | |
159 | \n{}\ | |
160 | \n", | |
161 | $crate::Comparison::new(left_val, right_val)) | |
162 | } | |
163 | } | |
164 | } | |
165 | }); | |
166 | ($left:expr , $right:expr, $($arg:tt)*) => ({ | |
167 | match (&($left), &($right)) { | |
168 | (left_val, right_val) => { | |
169 | if !(*left_val == *right_val) { | |
170 | ::std::panic!("assertion failed: `(left == right)`: {}\ | |
171 | \n\ | |
172 | \n{}\ | |
173 | \n", | |
174 | format_args!($($arg)*), | |
175 | $crate::Comparison::new(left_val, right_val)) | |
176 | } | |
177 | } | |
178 | } | |
179 | }); | |
180 | } | |
181 | ||
182 | /// Asserts that two expressions are not equal to each other (using [`PartialEq`]). | |
183 | /// | |
184 | /// On panic, this macro will print the values of the expressions with their | |
185 | /// [`Debug`] representations. | |
186 | /// | |
187 | /// This is a drop in replacement for [`std::assert_ne!`]. | |
188 | /// You can provide a custom panic message if desired. | |
189 | /// | |
190 | /// # Examples | |
191 | /// | |
192 | /// ``` | |
193 | /// use pretty_assertions::assert_ne; | |
194 | /// | |
195 | /// let a = 3; | |
196 | /// let b = 2; | |
197 | /// assert_ne!(a, b); | |
198 | /// | |
199 | /// assert_ne!(a, b, "we are testing that the values are not equal"); | |
200 | /// ``` | |
201 | #[macro_export] | |
202 | macro_rules! assert_ne { | |
203 | ($left:expr, $right:expr) => ({ | |
204 | $crate::assert_ne!(@ $left, $right, "", ""); | |
205 | }); | |
206 | ($left:expr, $right:expr,) => ({ | |
207 | $crate::assert_ne!(@ $left, $right, "", ""); | |
208 | }); | |
209 | ($left:expr, $right:expr, $($arg:tt)+) => ({ | |
210 | $crate::assert_ne!(@ $left, $right, ": ", $($arg)+); | |
211 | }); | |
212 | (@ $left:expr, $right:expr, $maybe_semicolon:expr, $($arg:tt)+) => ({ | |
213 | match (&($left), &($right)) { | |
214 | (left_val, right_val) => { | |
215 | if *left_val == *right_val { | |
216 | let left_dbg = ::std::format!("{:?}", &*left_val); | |
217 | let right_dbg = ::std::format!("{:?}", &*right_val); | |
218 | if left_dbg != right_dbg { | |
219 | ||
220 | ::std::panic!("assertion failed: `(left != right)`{}{}\ | |
221 | \n\ | |
222 | \n{}\ | |
223 | \n{}: According to the `PartialEq` implementation, both of the values \ | |
224 | are partially equivalent, even if the `Debug` outputs differ.\ | |
225 | \n\ | |
226 | \n", | |
227 | $maybe_semicolon, | |
228 | format_args!($($arg)+), | |
229 | $crate::Comparison::new(left_val, right_val), | |
230 | $crate::Style::new() | |
231 | .bold() | |
232 | .underline() | |
233 | .paint("Note")) | |
234 | } | |
235 | ||
236 | ::std::panic!("assertion failed: `(left != right)`{}{}\ | |
237 | \n\ | |
238 | \n{}:\ | |
239 | \n{:#?}\ | |
240 | \n\ | |
241 | \n", | |
242 | $maybe_semicolon, | |
243 | format_args!($($arg)+), | |
244 | $crate::Style::new().bold().paint("Both sides"), | |
245 | left_val) | |
246 | } | |
247 | } | |
248 | } | |
249 | }); | |
250 | } | |
251 | ||
252 | #[cfg(test)] | |
253 | #[allow(clippy::eq_op)] | |
254 | #[no_implicit_prelude] | |
255 | mod test { | |
256 | mod assert_eq { | |
257 | use ::std::string::{String, ToString}; | |
258 | ||
259 | #[test] | |
260 | fn passes() { | |
261 | let a = "some value"; | |
262 | crate::assert_eq!(a, a); | |
263 | } | |
264 | ||
265 | #[test] | |
266 | fn passes_unsized() { | |
267 | let a: &[u8] = b"e"; | |
268 | crate::assert_eq!(*a, *a); | |
269 | } | |
270 | ||
271 | #[test] | |
272 | fn passes_comparable_types() { | |
273 | let s0: &'static str = "foo"; | |
274 | let s1: String = "foo".to_string(); | |
275 | crate::assert_eq!(s0, s1); | |
276 | } | |
277 | ||
278 | #[test] | |
279 | #[should_panic(expected = r#"assertion failed: `(left == right)` | |
280 | ||
281 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
282 | \e[31m<\e[0m\e[1;48;5;52;31m666\e[0m | |
283 | \e[32m>\e[0m\e[1;48;5;22;32m999\e[0m | |
284 | ||
285 | "#)] | |
286 | fn fails() { | |
287 | crate::assert_eq!(666, 999); | |
288 | } | |
289 | ||
290 | #[test] | |
291 | #[should_panic(expected = r#"assertion failed: `(left == right)` | |
292 | ||
293 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
294 | \e[31m<\e[0m\e[1;48;5;52;31m666\e[0m | |
295 | \e[32m>\e[0m\e[1;48;5;22;32m999\e[0m | |
296 | ||
297 | "#)] | |
298 | fn fails_trailing_comma() { | |
299 | crate::assert_eq!(666, 999,); | |
300 | } | |
301 | ||
302 | #[test] | |
303 | #[should_panic(expected = r#"assertion failed: `(left == right)` | |
304 | ||
305 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
306 | [ | |
307 | 101, | |
308 | \e[32m> 101,\e[0m | |
309 | ] | |
310 | ||
311 | "#)] | |
312 | fn fails_unsized() { | |
313 | let a: &[u8] = b"e"; | |
314 | let b: &[u8] = b"ee"; | |
315 | crate::assert_eq!(*a, *b); | |
316 | } | |
317 | ||
318 | #[test] | |
319 | #[should_panic( | |
320 | expected = r#"assertion failed: `(left == right)`: custom panic message | |
321 | ||
322 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
323 | \e[31m<\e[0m\e[1;48;5;52;31m666\e[0m | |
324 | \e[32m>\e[0m\e[1;48;5;22;32m999\e[0m | |
325 | ||
326 | "# | |
327 | )] | |
328 | fn fails_custom() { | |
329 | crate::assert_eq!(666, 999, "custom panic message"); | |
330 | } | |
331 | ||
332 | #[test] | |
333 | #[should_panic( | |
334 | expected = r#"assertion failed: `(left == right)`: custom panic message | |
335 | ||
336 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
337 | \e[31m<\e[0m\e[1;48;5;52;31m666\e[0m | |
338 | \e[32m>\e[0m\e[1;48;5;22;32m999\e[0m | |
339 | ||
340 | "# | |
341 | )] | |
342 | fn fails_custom_trailing_comma() { | |
343 | crate::assert_eq!(666, 999, "custom panic message",); | |
344 | } | |
345 | } | |
346 | ||
347 | mod assert_ne { | |
348 | use ::std::string::{String, ToString}; | |
349 | ||
350 | #[test] | |
351 | fn passes() { | |
352 | let a = "a"; | |
353 | let b = "b"; | |
354 | crate::assert_ne!(a, b); | |
355 | } | |
356 | ||
357 | #[test] | |
358 | fn passes_unsized() { | |
359 | let a: &[u8] = b"e"; | |
360 | let b: &[u8] = b"ee"; | |
361 | crate::assert_ne!(*a, *b); | |
362 | } | |
363 | ||
364 | #[test] | |
365 | fn passes_comparable_types() { | |
366 | let s0: &'static str = "foo"; | |
367 | let s1: String = "bar".to_string(); | |
368 | crate::assert_ne!(s0, s1); | |
369 | } | |
370 | ||
371 | #[test] | |
372 | #[should_panic(expected = r#"assertion failed: `(left != right)` | |
373 | ||
374 | \e[1mBoth sides\e[0m: | |
375 | 666 | |
376 | "#)] | |
377 | fn fails() { | |
378 | crate::assert_ne!(666, 666); | |
379 | } | |
380 | ||
381 | #[test] | |
382 | #[should_panic(expected = r#"assertion failed: `(left != right)` | |
383 | ||
384 | \e[1mBoth sides\e[0m: | |
385 | 666 | |
386 | "#)] | |
387 | fn fails_trailing_comma() { | |
388 | crate::assert_ne!(666, 666,); | |
389 | } | |
390 | ||
391 | #[test] | |
392 | #[should_panic(expected = r#"assertion failed: `(left != right)` | |
393 | ||
394 | \e[1mBoth sides\e[0m: | |
395 | [ | |
396 | 101, | |
397 | ] | |
398 | ||
399 | "#)] | |
400 | fn fails_unsized() { | |
401 | let a: &[u8] = b"e"; | |
402 | crate::assert_ne!(*a, *a); | |
403 | } | |
404 | ||
405 | #[test] | |
406 | #[should_panic( | |
407 | expected = r#"assertion failed: `(left != right)`: custom panic message | |
408 | ||
409 | \e[1mBoth sides\e[0m: | |
410 | 666 | |
411 | "# | |
412 | )] | |
413 | fn fails_custom() { | |
414 | crate::assert_ne!(666, 666, "custom panic message"); | |
415 | } | |
416 | ||
417 | #[test] | |
418 | #[should_panic( | |
419 | expected = r#"assertion failed: `(left != right)`: custom panic message | |
420 | ||
421 | \e[1mBoth sides\e[0m: | |
422 | 666 | |
423 | "# | |
424 | )] | |
425 | fn fails_custom_trailing_comma() { | |
426 | crate::assert_ne!(666, 666, "custom panic message",); | |
427 | } | |
428 | ||
429 | // If the values are equal but their debug outputs are not | |
430 | // show a specific warning | |
431 | ||
432 | #[test] | |
433 | #[should_panic(expected = r#"assertion failed: `(left != right)` | |
434 | ||
435 | \e[1mDiff\e[0m \e[31m< left\e[0m / \e[32mright >\e[0m : | |
436 | \e[31m<\e[0m\e[1;48;5;52;31m-\e[0m\e[31m0.0\e[0m | |
437 | \e[32m>0.0\e[0m | |
438 | ||
439 | \e[1;4mNote\e[0m: According to the `PartialEq` implementation, both of the values are partially equivalent, even if the `Debug` outputs differ. | |
440 | ||
441 | "#)] | |
442 | fn assert_ne_partial() { | |
443 | // Workaround for https://github.com/rust-lang/rust/issues/47619 | |
444 | // can be removed, when we require rust 1.25 or higher | |
445 | struct Foo(f32); | |
446 | ||
447 | use ::std::fmt; | |
448 | impl fmt::Debug for Foo { | |
449 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
450 | ::std::write!(f, "{:.1?}", self.0) | |
451 | } | |
452 | } | |
453 | ||
454 | impl ::std::cmp::PartialEq for Foo { | |
455 | fn eq(&self, other: &Self) -> bool { | |
456 | self.0 == other.0 | |
457 | } | |
458 | } | |
459 | ||
460 | crate::assert_ne!(Foo(-0.0), Foo(0.0)); | |
461 | } | |
462 | ||
463 | // Regression tests | |
464 | ||
465 | #[test] | |
466 | #[should_panic] | |
467 | fn assert_ne_non_empty_return() { | |
468 | fn not_zero(x: u32) -> u32 { | |
469 | crate::assert_ne!(x, 0); | |
470 | x | |
471 | } | |
472 | not_zero(0); | |
473 | } | |
474 | } | |
475 | } |