• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::description::Description;
16 use crate::matcher::{Matcher, MatcherBase, MatcherResult};
17 use crate::matcher_support::edit_distance;
18 use crate::matcher_support::summarize_diff::create_diff;
19 
20 use std::fmt::Debug;
21 
22 /// Matches a value equal (in the sense of `==`) to `expected`.
23 ///
24 /// The type of `expected` must implement the [`PartialEq`] trait so that the
25 /// expected and actual values can be compared.
26 ///
27 /// ```
28 /// # use googletest::prelude::*;
29 /// # fn should_pass() -> Result<()> {
30 /// verify_that!(123, eq(123))?; // Passes
31 /// #     Ok(())
32 /// # }
33 /// # fn should_fail() -> Result<()> {
34 /// verify_that!(123, eq(234))?; // Fails
35 /// #     Ok(())
36 /// # }
37 /// # should_pass().unwrap();
38 /// # should_fail().unwrap_err();
39 /// ```
40 ///
41 /// `expected` to `actual` must be comparable with one another via the
42 /// [`PartialEq`] trait. In most cases, this means that they must be of the same
43 /// type. However, there are a few cases where different but closely related
44 /// types are comparable, for example `String` with `&str`.
45 ///
46 /// ```
47 /// # use googletest::prelude::*;
48 /// # fn should_pass() -> Result<()> {
49 /// verify_that!(String::from("Some value"), eq("Some value"))?; // Passes
50 /// #     Ok(())
51 /// # }
52 /// # should_pass().unwrap();
53 /// ```
54 ///
55 /// In most cases however, one must convert one of the arguments explicitly.
56 /// This can be surprising when comparing integer types or references.
57 ///
58 /// ```compile_fail
59 /// verify_that!(123u32, eq(123u64))?; // Does not compile
60 /// verify_that!(123u32 as u64, eq(123u64))?; // Passes
61 /// ```
62 ///
63 /// ```ignore
64 /// let actual: &T = ...;
65 /// let expected: T = T{...};
66 /// verify_that(actual, eq(expected))?; // Does not compile
67 /// verify_that(actual, eq(&expected))?; // Compiles
68 /// ```
69 ///
70 /// When matching with string types (`&str` and `String`), one can set more
71 /// options on how equality is checked through the
72 /// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
73 /// extension trait, which is implemented for this matcher.
eq<T>(expected: T) -> EqMatcher<T>74 pub fn eq<T>(expected: T) -> EqMatcher<T> {
75     EqMatcher { expected }
76 }
77 
78 /// A matcher which matches a value equal to `expected`.
79 ///
80 /// See [`eq`].
81 #[derive(MatcherBase)]
82 pub struct EqMatcher<T> {
83     pub(crate) expected: T,
84 }
85 
86 impl<T: Debug, A: Debug + Copy + PartialEq<T>> Matcher<A> for EqMatcher<T> {
matches(&self, actual: A) -> MatcherResult87     fn matches(&self, actual: A) -> MatcherResult {
88         (actual == self.expected).into()
89     }
90 
describe(&self, matcher_result: MatcherResult) -> Description91     fn describe(&self, matcher_result: MatcherResult) -> Description {
92         match matcher_result {
93             MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
94             MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
95         }
96     }
97 
explain_match(&self, actual: A) -> Description98     fn explain_match(&self, actual: A) -> Description {
99         let expected_debug = format!("{:#?}", self.expected);
100         let actual_debug = format!("{:#?}", actual);
101         let description = Matcher::<A>::describe(self, self.matches(actual));
102 
103         let diff = if is_multiline_string_debug(&actual_debug)
104             && is_multiline_string_debug(&expected_debug)
105         {
106             create_diff(
107                 // The two calls below return None if and only if the strings expected_debug
108                 // respectively actual_debug are not enclosed in ". The calls to
109                 // is_multiline_string_debug above ensure that they are. So the calls cannot
110                 // actually return None and unwrap() should not panic.
111                 &to_display_output(&actual_debug).unwrap(),
112                 &to_display_output(&expected_debug).unwrap(),
113                 edit_distance::Mode::Exact,
114             )
115         } else {
116             create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
117         };
118 
119         if diff.is_empty() {
120             format!("which {description}").into()
121         } else {
122             format!("which {description}\n\n{diff}").into()
123         }
124     }
125 }
126 
is_multiline_string_debug(string: &str) -> bool127 fn is_multiline_string_debug(string: &str) -> bool {
128     string.starts_with('"')
129         && string.ends_with('"')
130         && !string.contains('\n')
131         && string.contains("\\n")
132 }
133 
to_display_output(string: &str) -> Option<String>134 fn to_display_output(string: &str) -> Option<String> {
135     Some(string.strip_prefix('"')?.strip_suffix('"')?.split("\\n").collect::<Vec<_>>().join("\n"))
136 }
137 
138 #[cfg(test)]
139 mod tests {
140     use crate::prelude::*;
141     use indoc::indoc;
142 
143     #[test]
eq_matches_string_reference_with_string_reference() -> Result<()>144     fn eq_matches_string_reference_with_string_reference() -> Result<()> {
145         verify_that!("A string", eq("A string"))
146     }
147 
148     #[test]
eq_matches_owned_string_with_string_reference() -> Result<()>149     fn eq_matches_owned_string_with_string_reference() -> Result<()> {
150         let value = "A string".to_string();
151         verify_that!(value, eq("A string"))
152     }
153 
154     #[test]
eq_matches_owned_string_reference_with_string_reference() -> Result<()>155     fn eq_matches_owned_string_reference_with_string_reference() -> Result<()> {
156         let value = "A string".to_string();
157         verify_that!(&value, eq("A string"))
158     }
159 
160     #[test]
eq_matches_i32_with_i32() -> Result<()>161     fn eq_matches_i32_with_i32() -> Result<()> {
162         verify_that!(123, eq(123))
163     }
164 
165     #[test]
eq_struct_debug_diff() -> Result<()>166     fn eq_struct_debug_diff() -> Result<()> {
167         #[derive(Debug, PartialEq)]
168         struct Strukt {
169             int: i32,
170             string: String,
171         }
172 
173         let result = verify_that!(
174             Strukt { int: 123, string: "something".into() },
175             eq(&Strukt { int: 321, string: "someone".into() })
176         );
177         verify_that!(
178             result,
179             err(displays_as(contains_substring(indoc! {
180             "
181             Actual: Strukt { int: 123, string: \"something\" },
182               which isn't equal to Strukt { int: 321, string: \"someone\" }
183 
184               Difference(-actual / +expected):
185                Strukt {
186               -    int: 123,
187               +    int: 321,
188               -    string: \"something\",
189               +    string: \"someone\",
190                }
191             "})))
192         )
193     }
194 
195     #[test]
eq_vec_debug_diff() -> Result<()>196     fn eq_vec_debug_diff() -> Result<()> {
197         let result = verify_that!(vec![1, 2, 3], eq(&vec![1, 3, 4]));
198         verify_that!(
199             result,
200             err(displays_as(contains_substring(indoc! {
201             "
202             Value of: vec![1, 2, 3]
203             Expected: is equal to [1, 3, 4]
204             Actual: [1, 2, 3],
205               which isn't equal to [1, 3, 4]
206 
207               Difference(-actual / +expected):
208                [
209                    1,
210               -    2,
211                    3,
212               +    4,
213                ]
214             "})))
215         )
216     }
217 
218     #[test]
eq_vec_debug_diff_length_mismatch() -> Result<()>219     fn eq_vec_debug_diff_length_mismatch() -> Result<()> {
220         let result = verify_that!(vec![1, 2, 3, 4, 5], eq(&vec![1, 3, 5]));
221         verify_that!(
222             result,
223             err(displays_as(contains_substring(indoc! {
224             "
225             Value of: vec![1, 2, 3, 4, 5]
226             Expected: is equal to [1, 3, 5]
227             Actual: [1, 2, 3, 4, 5],
228               which isn't equal to [1, 3, 5]
229 
230               Difference(-actual / +expected):
231                [
232                    1,
233               -    2,
234                    3,
235               -    4,
236                    5,
237                ]
238             "})))
239         )
240     }
241 
242     #[test]
eq_debug_diff_common_lines_omitted() -> Result<()>243     fn eq_debug_diff_common_lines_omitted() -> Result<()> {
244         let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
245         verify_that!(
246             result,
247             err(displays_as(contains_substring(indoc! {
248             "
249             ],
250               which isn't equal to [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]
251 
252               Difference(-actual / +expected):
253                [
254               -    1,
255               -    2,
256                    3,
257                    4,
258                <---- 43 common lines omitted ---->
259                    48,
260                    49,
261               +    50,
262               +    51,
263                ]"})))
264         )
265     }
266 
267     #[test]
eq_debug_diff_5_common_lines_not_omitted() -> Result<()>268     fn eq_debug_diff_5_common_lines_not_omitted() -> Result<()> {
269         let result = verify_that!((1..8).collect::<Vec<_>>(), eq(&(3..10).collect::<Vec<_>>()));
270         verify_that!(
271             result,
272             err(displays_as(contains_substring(indoc! {
273             "
274             Actual: [1, 2, 3, 4, 5, 6, 7],
275               which isn't equal to [3, 4, 5, 6, 7, 8, 9]
276 
277               Difference(-actual / +expected):
278                [
279               -    1,
280               -    2,
281                    3,
282                    4,
283                    5,
284                    6,
285                    7,
286               +    8,
287               +    9,
288                ]"})))
289         )
290     }
291 
292     #[test]
eq_debug_diff_start_common_lines_omitted() -> Result<()>293     fn eq_debug_diff_start_common_lines_omitted() -> Result<()> {
294         let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(1..52).collect::<Vec<_>>()));
295         verify_that!(
296             result,
297             err(displays_as(contains_substring(indoc! {
298             "
299             ],
300               which isn't equal to [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]
301 
302               Difference(-actual / +expected):
303                [
304                    1,
305                <---- 46 common lines omitted ---->
306                    48,
307                    49,
308               +    50,
309               +    51,
310                ]"})))
311         )
312     }
313 
314     #[test]
eq_debug_diff_end_common_lines_omitted() -> Result<()>315     fn eq_debug_diff_end_common_lines_omitted() -> Result<()> {
316         let result = verify_that!((1..52).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
317         verify_that!(
318             result,
319             err(displays_as(contains_substring(indoc! {
320             "
321             ],
322               which isn't equal to [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]
323 
324               Difference(-actual / +expected):
325                [
326               -    1,
327               -    2,
328                    3,
329                    4,
330                <---- 46 common lines omitted ---->
331                    51,
332                ]"})))
333         )
334     }
335 
336     #[test]
eq_multi_line_string_debug_diff() -> Result<()>337     fn eq_multi_line_string_debug_diff() -> Result<()> {
338         let result = verify_that!("One\nTwo\nThree", eq("One\nSix\nThree"));
339         // TODO: b/257454450 - Make this more useful, by potentially unescaping the
340         // line return.
341         verify_that!(
342             result,
343             err(displays_as(contains_substring(indoc! {
344             r#"
345             Value of: "One\nTwo\nThree"
346             Expected: is equal to "One\nSix\nThree"
347             Actual: "One\nTwo\nThree",
348               which isn't equal to "One\nSix\nThree"
349             "#})))
350         )
351     }
352 
353     #[test]
match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()>354     fn match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()> {
355         let result = verify_that!(
356             indoc!(
357                 "
358                     First line
359                     Second line
360                     Third line
361                 "
362             ),
363             eq(indoc!(
364                 "
365                     First line
366                     Second lines
367                     Third line
368                 "
369             ))
370         );
371 
372         verify_that!(
373             result,
374             err(displays_as(contains_substring(
375                 "\
376    First line
377   -Second line
378   +Second lines
379    Third line"
380             )))
381         )
382     }
383 
384     #[test]
match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()>385     fn match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()> {
386         let result = verify_that!(
387             "First line",
388             eq(indoc!(
389                 "
390                     First line
391                     Second line
392                     Third line
393                 "
394             ))
395         );
396 
397         verify_that!(
398             result,
399             err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
400         )
401     }
402 
403     #[test]
match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()>404     fn match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()> {
405         let result = verify_that!(
406             indoc!(
407                 "
408                     First line
409                     Second line
410                     Third line
411                 "
412             ),
413             eq("First line")
414         );
415 
416         verify_that!(
417             result,
418             err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
419         )
420     }
421 }
422