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, MatcherResult};
17 use crate::matcher_support::edit_distance;
18 use crate::matcher_support::summarize_diff::create_diff;
19 
20 use std::{fmt::Debug, marker::PhantomData};
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<A: ?Sized, T>(expected: T) -> EqMatcher<A, T>74 pub fn eq<A: ?Sized, T>(expected: T) -> EqMatcher<A, T> {
75     EqMatcher { expected, phantom: Default::default() }
76 }
77 
78 /// A matcher which matches a value equal to `expected`.
79 ///
80 /// See [`eq`].
81 pub struct EqMatcher<A: ?Sized, T> {
82     pub(crate) expected: T,
83     phantom: PhantomData<A>,
84 }
85 
86 impl<T: Debug, A: Debug + ?Sized + PartialEq<T>> Matcher for EqMatcher<A, T> {
87     type ActualT = A;
88 
matches(&self, actual: &A) -> MatcherResult89     fn matches(&self, actual: &A) -> MatcherResult {
90         (*actual == self.expected).into()
91     }
92 
describe(&self, matcher_result: MatcherResult) -> Description93     fn describe(&self, matcher_result: MatcherResult) -> Description {
94         match matcher_result {
95             MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
96             MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
97         }
98     }
99 
explain_match(&self, actual: &A) -> Description100     fn explain_match(&self, actual: &A) -> Description {
101         let expected_debug = format!("{:#?}", self.expected);
102         let actual_debug = format!("{:#?}", actual);
103         let description = self.describe(self.matches(actual));
104 
105         let diff = if is_multiline_string_debug(&actual_debug)
106             && is_multiline_string_debug(&expected_debug)
107         {
108             create_diff(
109                 // The two calls below return None if and only if the strings expected_debug
110                 // respectively actual_debug are not enclosed in ". The calls to
111                 // is_multiline_string_debug above ensure that they are. So the calls cannot
112                 // actually return None and unwrap() should not panic.
113                 &to_display_output(&actual_debug).unwrap(),
114                 &to_display_output(&expected_debug).unwrap(),
115                 edit_distance::Mode::Exact,
116             )
117         } else {
118             create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
119         };
120 
121         format!("which {description}{diff}").into()
122     }
123 }
124 
is_multiline_string_debug(string: &str) -> bool125 fn is_multiline_string_debug(string: &str) -> bool {
126     string.starts_with('"')
127         && string.ends_with('"')
128         && !string.contains('\n')
129         && string.contains("\\n")
130 }
131 
to_display_output(string: &str) -> Option<String>132 fn to_display_output(string: &str) -> Option<String> {
133     Some(string.strip_prefix('"')?.strip_suffix('"')?.split("\\n").collect::<Vec<_>>().join("\n"))
134 }
135 
136 #[cfg(test)]
137 mod tests {
138     use super::eq;
139     use crate::prelude::*;
140     use indoc::indoc;
141 
142     #[test]
eq_matches_string_reference_with_string_reference() -> Result<()>143     fn eq_matches_string_reference_with_string_reference() -> Result<()> {
144         verify_that!("A string", eq("A string"))
145     }
146 
147     #[test]
eq_matches_owned_string_with_string_reference() -> Result<()>148     fn eq_matches_owned_string_with_string_reference() -> Result<()> {
149         let value = "A string".to_string();
150         verify_that!(value, eq("A string"))
151     }
152 
153     #[test]
eq_matches_owned_string_reference_with_string_reference() -> Result<()>154     fn eq_matches_owned_string_reference_with_string_reference() -> Result<()> {
155         let value = "A string".to_string();
156         verify_that!(&value, eq("A string"))
157     }
158 
159     #[test]
eq_matches_i32_with_i32() -> Result<()>160     fn eq_matches_i32_with_i32() -> Result<()> {
161         verify_that!(123, eq(123))
162     }
163 
164     #[test]
eq_struct_debug_diff() -> Result<()>165     fn eq_struct_debug_diff() -> Result<()> {
166         #[derive(Debug, PartialEq)]
167         struct Strukt {
168             int: i32,
169             string: String,
170         }
171 
172         let result = verify_that!(
173             Strukt { int: 123, string: "something".into() },
174             eq(Strukt { int: 321, string: "someone".into() })
175         );
176         verify_that!(
177             result,
178             err(displays_as(contains_substring(indoc! {
179             "
180             Actual: Strukt { int: 123, string: \"something\" },
181               which isn't equal to Strukt { int: 321, string: \"someone\" }
182               Difference(-actual / +expected):
183                Strukt {
184               -    int: 123,
185               +    int: 321,
186               -    string: \"something\",
187               +    string: \"someone\",
188                }
189             "})))
190         )
191     }
192 
193     #[test]
eq_vec_debug_diff() -> Result<()>194     fn eq_vec_debug_diff() -> Result<()> {
195         let result = verify_that!(vec![1, 2, 3], eq(vec![1, 3, 4]));
196         verify_that!(
197             result,
198             err(displays_as(contains_substring(indoc! {
199             "
200             Value of: vec![1, 2, 3]
201             Expected: is equal to [1, 3, 4]
202             Actual: [1, 2, 3],
203               which isn't equal to [1, 3, 4]
204               Difference(-actual / +expected):
205                [
206                    1,
207               -    2,
208                    3,
209               +    4,
210                ]
211             "})))
212         )
213     }
214 
215     #[test]
eq_vec_debug_diff_length_mismatch() -> Result<()>216     fn eq_vec_debug_diff_length_mismatch() -> Result<()> {
217         let result = verify_that!(vec![1, 2, 3, 4, 5], eq(vec![1, 3, 5]));
218         verify_that!(
219             result,
220             err(displays_as(contains_substring(indoc! {
221             "
222             Value of: vec![1, 2, 3, 4, 5]
223             Expected: is equal to [1, 3, 5]
224             Actual: [1, 2, 3, 4, 5],
225               which isn't equal to [1, 3, 5]
226               Difference(-actual / +expected):
227                [
228                    1,
229               -    2,
230                    3,
231               -    4,
232                    5,
233                ]
234             "})))
235         )
236     }
237 
238     #[test]
eq_debug_diff_common_lines_omitted() -> Result<()>239     fn eq_debug_diff_common_lines_omitted() -> Result<()> {
240         let result = verify_that!((1..50).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
241         verify_that!(
242             result,
243             err(displays_as(contains_substring(indoc! {
244             "
245             ],
246               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]
247               Difference(-actual / +expected):
248                [
249               -    1,
250               -    2,
251                    3,
252                    4,
253                <---- 43 common lines omitted ---->
254                    48,
255                    49,
256               +    50,
257               +    51,
258                ]"})))
259         )
260     }
261 
262     #[test]
eq_debug_diff_5_common_lines_not_omitted() -> Result<()>263     fn eq_debug_diff_5_common_lines_not_omitted() -> Result<()> {
264         let result = verify_that!((1..8).collect::<Vec<_>>(), eq((3..10).collect::<Vec<_>>()));
265         verify_that!(
266             result,
267             err(displays_as(contains_substring(indoc! {
268             "
269             Actual: [1, 2, 3, 4, 5, 6, 7],
270               which isn't equal to [3, 4, 5, 6, 7, 8, 9]
271               Difference(-actual / +expected):
272                [
273               -    1,
274               -    2,
275                    3,
276                    4,
277                    5,
278                    6,
279                    7,
280               +    8,
281               +    9,
282                ]"})))
283         )
284     }
285 
286     #[test]
eq_debug_diff_start_common_lines_omitted() -> Result<()>287     fn eq_debug_diff_start_common_lines_omitted() -> Result<()> {
288         let result = verify_that!((1..50).collect::<Vec<_>>(), eq((1..52).collect::<Vec<_>>()));
289         verify_that!(
290             result,
291             err(displays_as(contains_substring(indoc! {
292             "
293             ],
294               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]
295               Difference(-actual / +expected):
296                [
297                    1,
298                <---- 46 common lines omitted ---->
299                    48,
300                    49,
301               +    50,
302               +    51,
303                ]"})))
304         )
305     }
306 
307     #[test]
eq_debug_diff_end_common_lines_omitted() -> Result<()>308     fn eq_debug_diff_end_common_lines_omitted() -> Result<()> {
309         let result = verify_that!((1..52).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
310         verify_that!(
311             result,
312             err(displays_as(contains_substring(indoc! {
313             "
314             ],
315               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]
316               Difference(-actual / +expected):
317                [
318               -    1,
319               -    2,
320                    3,
321                    4,
322                <---- 46 common lines omitted ---->
323                    51,
324                ]"})))
325         )
326     }
327 
328     #[test]
eq_multi_line_string_debug_diff() -> Result<()>329     fn eq_multi_line_string_debug_diff() -> Result<()> {
330         let result = verify_that!("One\nTwo\nThree", eq("One\nSix\nThree"));
331         // TODO: b/257454450 - Make this more useful, by potentially unescaping the
332         // line return.
333         verify_that!(
334             result,
335             err(displays_as(contains_substring(indoc! {
336             r#"
337             Value of: "One\nTwo\nThree"
338             Expected: is equal to "One\nSix\nThree"
339             Actual: "One\nTwo\nThree",
340               which isn't equal to "One\nSix\nThree"
341             "#})))
342         )
343     }
344 
345     #[test]
match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()>346     fn match_explanation_contains_diff_of_strings_if_more_than_one_line() -> Result<()> {
347         let result = verify_that!(
348             indoc!(
349                 "
350                     First line
351                     Second line
352                     Third line
353                 "
354             ),
355             eq(indoc!(
356                 "
357                     First line
358                     Second lines
359                     Third line
360                 "
361             ))
362         );
363 
364         verify_that!(
365             result,
366             err(displays_as(contains_substring(
367                 "\
368    First line
369   -Second line
370   +Second lines
371    Third line"
372             )))
373         )
374     }
375 
376     #[test]
match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()>377     fn match_explanation_does_not_show_diff_if_actual_value_is_single_line() -> Result<()> {
378         let result = verify_that!(
379             "First line",
380             eq(indoc!(
381                 "
382                     First line
383                     Second line
384                     Third line
385                 "
386             ))
387         );
388 
389         verify_that!(
390             result,
391             err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
392         )
393     }
394 
395     #[test]
match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()>396     fn match_explanation_does_not_show_diff_if_expected_value_is_single_line() -> Result<()> {
397         let result = verify_that!(
398             indoc!(
399                 "
400                     First line
401                     Second line
402                     Third line
403                 "
404             ),
405             eq("First line")
406         );
407 
408         verify_that!(
409             result,
410             err(displays_as(not(contains_substring("Difference(-actual / +expected):"))))
411         )
412     }
413 }
414