1 // Copyright 2023 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::{
16 description::Description,
17 matcher::{Matcher, MatcherResult},
18 matcher_support::{edit_distance, summarize_diff::create_diff},
19 };
20 use std::{fmt::Debug, marker::PhantomData, ops::Deref};
21
22 /// Matches a value equal (in the sense of `==`) to the dereferenced value of
23 /// `expected`.
24 ///
25 /// This is similar to [`eq`][crate::matchers::eq] but takes a reference or
26 /// smart pointer to the expected value rather than consuming it. This is useful
27 /// when:
28 ///
29 /// * one has only a reference to the expected value, and
30 /// * the expected value cannot or should not be copied or cloned to create an
31 /// owned value from it.
32 ///
33 /// ```
34 /// # use googletest::{matchers::eq_deref_of, verify_that};
35 /// #[derive(Debug, PartialEq)]
36 /// struct NonCloneableStruct(i32);
37 /// let expected = NonCloneableStruct(123);
38 /// verify_that!(NonCloneableStruct(123), eq_deref_of(&expected))
39 /// # .unwrap()
40 /// ```
41 ///
42 /// **Note**: while one can use `eq_deref_of` with the configuration methods of
43 /// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
44 /// to configure string equality, it is not possible to do so when the input is
45 /// a smart pointer to a string.
46 ///
47 /// ```compile_fail
48 /// # use googletest::{matchers::{eq_deref_of, str_matcher::StrMatcherConfigurator}, verify_that};
49 /// verify_that!("A string", eq_deref_of(Box::new("A STRING")).ignoring_ascii_case()) // Does not compile
50 /// # .unwrap()
51 /// ```
52 ///
53 /// Otherwise, this has the same behaviour as [`eq`][crate::matchers::eq].
eq_deref_of<ActualT: ?Sized, ExpectedRefT>( expected: ExpectedRefT, ) -> EqDerefOfMatcher<ActualT, ExpectedRefT>54 pub fn eq_deref_of<ActualT: ?Sized, ExpectedRefT>(
55 expected: ExpectedRefT,
56 ) -> EqDerefOfMatcher<ActualT, ExpectedRefT> {
57 EqDerefOfMatcher { expected, phantom: Default::default() }
58 }
59
60 /// A matcher which matches a value equal to the derefenced value of `expected`.
61 ///
62 /// See [`eq_deref_of`].
63 pub struct EqDerefOfMatcher<ActualT: ?Sized, ExpectedRefT> {
64 pub(crate) expected: ExpectedRefT,
65 phantom: PhantomData<ActualT>,
66 }
67
68 impl<ActualT, ExpectedRefT, ExpectedT> Matcher for EqDerefOfMatcher<ActualT, ExpectedRefT>
69 where
70 ActualT: Debug + ?Sized,
71 ExpectedRefT: Deref<Target = ExpectedT> + Debug,
72 ExpectedT: PartialEq<ActualT> + Debug,
73 {
74 type ActualT = ActualT;
75
matches(&self, actual: &ActualT) -> MatcherResult76 fn matches(&self, actual: &ActualT) -> MatcherResult {
77 (self.expected.deref() == actual).into()
78 }
79
describe(&self, matcher_result: MatcherResult) -> Description80 fn describe(&self, matcher_result: MatcherResult) -> Description {
81 match matcher_result {
82 MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
83 MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
84 }
85 }
86
explain_match(&self, actual: &ActualT) -> Description87 fn explain_match(&self, actual: &ActualT) -> Description {
88 format!(
89 "which {}{}",
90 &self.describe(self.matches(actual)),
91 create_diff(
92 &format!("{:#?}", actual),
93 &format!("{:#?}", self.expected.deref()),
94 edit_distance::Mode::Exact,
95 )
96 )
97 .into()
98 }
99 }
100
101 #[cfg(test)]
102 mod tests {
103 use super::eq_deref_of;
104 use crate::prelude::*;
105 use indoc::indoc;
106
107 #[derive(Debug, PartialEq)]
108 struct NonCloneNonCopyStruct(i32);
109
110 #[test]
matches_value_with_ref_to_equal_value() -> Result<()>111 fn matches_value_with_ref_to_equal_value() -> Result<()> {
112 verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(&NonCloneNonCopyStruct(123)))
113 }
114
115 #[test]
matches_value_with_box_of_equal_value() -> Result<()>116 fn matches_value_with_box_of_equal_value() -> Result<()> {
117 verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(Box::new(NonCloneNonCopyStruct(123))))
118 }
119
120 #[test]
does_not_match_value_with_non_equal_value() -> Result<()>121 fn does_not_match_value_with_non_equal_value() -> Result<()> {
122 verify_that!(NonCloneNonCopyStruct(123), not(eq_deref_of(&NonCloneNonCopyStruct(234))))
123 }
124
125 #[test]
shows_structured_diff() -> Result<()>126 fn shows_structured_diff() -> Result<()> {
127 #[derive(Debug, PartialEq)]
128 struct Strukt {
129 int: i32,
130 string: String,
131 }
132
133 let result = verify_that!(
134 Strukt { int: 123, string: "something".into() },
135 eq_deref_of(Box::new(Strukt { int: 321, string: "someone".into() }))
136 );
137 verify_that!(
138 result,
139 err(displays_as(contains_substring(indoc! {
140 "
141 Actual: Strukt { int: 123, string: \"something\" },
142 which isn't equal to Strukt { int: 321, string: \"someone\" }
143 Difference(-actual / +expected):
144 Strukt {
145 - int: 123,
146 + int: 321,
147 - string: \"something\",
148 + string: \"someone\",
149 }
150 "})))
151 )
152 }
153 }
154