• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // There are no visible documentation elements in this module; the declarative
16 // macro is documented in the matcher module.
17 #![doc(hidden)]
18 
19 /// Matches an object which, upon calling the given method on it with the given
20 /// arguments, produces a value matched by the given inner matcher.
21 ///
22 /// This is particularly useful as a nested matcher when the desired
23 /// property cannot be accessed through a field and must instead be
24 /// extracted through a method call. For example:
25 ///
26 /// ```
27 /// # use googletest::prelude::*;
28 /// #[derive(Debug)]
29 /// pub struct MyStruct {
30 ///     a_field: u32,
31 /// }
32 ///
33 /// impl MyStruct {
34 ///     pub fn get_a_field(&self) -> u32 { self.a_field }
35 /// }
36 ///
37 /// let value = vec![MyStruct { a_field: 100 }];
38 /// verify_that!(value, contains(property!(&MyStruct.get_a_field(), eq(100))))
39 /// #    .unwrap();
40 /// ```
41 ///
42 ///
43 /// If the inner matcher is `eq(...)`, it can be omitted:
44 ///
45 /// ```
46 /// # use googletest::prelude::*;
47 /// #[derive(Debug)]
48 /// pub struct MyStruct {
49 ///     a_field: u32,
50 /// }
51 ///
52 /// impl MyStruct {
53 ///     pub fn get_a_field(&self) -> u32 { self.a_field }
54 /// }
55 ///
56 /// let value = vec![MyStruct { a_field: 100 }];
57 /// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
58 /// #    .unwrap();
59 /// ```
60 ///
61 /// **Important**: The method should be pure function with a deterministic
62 /// output and no side effects. In particular, in the event of an assertion
63 /// failure, it will be invoked a second time, with the assertion failure output
64 /// reflecting the *second* invocation.
65 ///
66 /// The method may also take additional litteral arguments:
67 ///
68 /// ```
69 /// # use googletest::prelude::*;
70 /// # #[derive(Debug)]
71 /// # pub struct MyStruct {
72 /// #     a_field: u32,
73 /// # }
74 /// impl MyStruct {
75 ///     pub fn add_to_a_field(&self, a: u32) -> u32 { self.a_field + a }
76 /// }
77 ///
78 /// # let value = vec![MyStruct { a_field: 100 }];
79 /// verify_that!(value, contains(property!(&MyStruct.add_to_a_field(50), eq(150))))
80 /// #    .unwrap();
81 /// ```
82 ///
83 /// The arguments must be litteral as `property!` is not able to capture them.
84 ///
85 /// # Specification of the property pattern
86 ///
87 /// The specification of the field follow the syntax: `(ref)? (&)?
88 /// $TYPE.$PROPERTY\($ARGUMENT\)`.
89 ///
90 /// The `&` allows to specify whether this matcher matches against an actual of
91 /// type `$TYPE` (`$TYPE` must implement `Copy`) or a `&$TYPE`.
92 ///
93 /// For instance:
94 ///
95 /// ```
96 /// # use googletest::prelude::*;
97 /// #[derive(Debug)]
98 /// pub struct AStruct;
99 ///
100 /// impl AStruct {
101 ///   fn a_property(&self) -> i32 {32}
102 /// }
103 /// # fn should_pass() -> Result<()> {
104 /// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
105 /// #     Ok(())
106 /// # }
107 /// # should_pass().unwrap();
108 /// ```
109 ///
110 /// ```
111 /// # use googletest::prelude::*;
112 /// #[derive(Debug, Clone, Copy)]
113 /// pub struct AStruct;
114 ///
115 /// impl AStruct {
116 ///   fn a_property(self) -> i32 {32}
117 /// }
118 /// # fn should_pass() -> Result<()> {
119 /// verify_that!(AStruct, property!(AStruct.a_property(), eq(32)))?;
120 /// #     Ok(())
121 /// # }
122 /// # should_pass().unwrap();
123 /// ```
124 ///
125 /// The `ref` allows to bind the property returned value by reference, which is
126 /// required if the field type does not implement `Copy`.
127 ///
128 /// For instance:
129 ///
130 /// ```
131 /// # use googletest::prelude::*;
132 /// #[derive(Debug)]
133 /// pub struct AStruct;
134 ///
135 /// impl AStruct {
136 ///   fn a_property(&self) -> i32 {32}
137 /// }
138 /// # fn should_pass() -> Result<()> {
139 /// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
140 /// #     Ok(())
141 /// # }
142 /// # should_pass().unwrap();
143 /// ```
144 ///
145 /// If `property!` is qualified by both `&` and `ref`, they can both be omitted.
146 ///
147 /// ```
148 /// # use googletest::prelude::*;
149 /// #[derive(Debug)]
150 /// pub struct AStruct;
151 ///
152 /// impl AStruct {
153 ///   fn a_property(&self) -> String {"32".into()}
154 /// }
155 /// # fn should_pass() -> Result<()> {
156 /// verify_that!(AStruct, property!(&AStruct.a_property(), ref eq("32")))?;
157 /// verify_that!(AStruct, property!(AStruct.a_property(), eq("32")))?;
158 /// #     Ok(())
159 /// # }
160 /// # should_pass().unwrap();
161 /// ```
162 ///
163 /// This macro is analogous to [`field`][crate::matchers::field], except that it
164 /// extracts the datum to be matched from the given object by invoking a method
165 /// rather than accessing a field.
166 ///
167 /// The list of arguments may optionally have a trailing comma.
168 #[macro_export]
169 #[doc(hidden)]
170 macro_rules! __property {
171     ($($t:tt)*) => { $crate::property_internal!($($t)*) }
172 }
173 
174 // Internal-only macro created so that the macro definition does not appear in
175 // generated documentation.
176 #[doc(hidden)]
177 #[macro_export]
178 macro_rules! property_internal {
179 
180     (&$($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
181         $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
182             |o: &$($t)::+| $($t)::+::$method(o, $($argument),*),
183             &stringify!($method($($argument),*)),
184             $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
185     }};
186     ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
187         $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
188             |o: $($t)::+| $($t)::+::$method(o, $($argument),*),
189             &stringify!($method($($argument),*)),
190             $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
191     }};
192     (& $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
193         $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
194             |o: &&$($t)::+| o.$method($($argument),*),
195             &stringify!($method($($argument),*)),
196             $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
197     }};
198     ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
199         $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
200             |o: &$($t)::+| o.$method($($argument),*),
201             &stringify!($method($($argument),*)),
202             $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
203     }};
204 }
205 
206 /// Items for use only by the declarative macros in this module.
207 ///
208 /// **For internal use only. API stablility is not guaranteed!**
209 #[doc(hidden)]
210 pub mod internal {
211     use crate::{
212         description::Description,
213         matcher::{Matcher, MatcherBase, MatcherResult},
214     };
215     use std::fmt::Debug;
216 
217     /// **For internal use only. API stablility is not guaranteed!**
218     #[doc(hidden)]
property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>( extractor: fn(&OuterT) -> InnerT, property_desc: &'static str, inner: MatcherT, ) -> PropertyMatcher<OuterT, InnerT, MatcherT>219     pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
220         extractor: fn(&OuterT) -> InnerT,
221         property_desc: &'static str,
222         inner: MatcherT,
223     ) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
224         PropertyMatcher { extractor, property_desc, inner }
225     }
226 
227     #[derive(MatcherBase)]
228     pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
229         extractor: fn(&OuterT) -> InnerT,
230         property_desc: &'static str,
231         inner: MatcherT,
232     }
233 
234     impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
235     where
236         InnerT: Debug + Copy,
237         OuterT: Debug + Copy,
238         MatcherT: Matcher<InnerT>,
239     {
matches(&self, actual: OuterT) -> MatcherResult240         fn matches(&self, actual: OuterT) -> MatcherResult {
241             self.inner.matches((self.extractor)(&actual))
242         }
243 
describe(&self, matcher_result: MatcherResult) -> Description244         fn describe(&self, matcher_result: MatcherResult) -> Description {
245             format!(
246                 "has property `{}`, which {}",
247                 self.property_desc,
248                 self.inner.describe(matcher_result)
249             )
250             .into()
251         }
252 
explain_match(&self, actual: OuterT) -> Description253         fn explain_match(&self, actual: OuterT) -> Description {
254             let actual_inner = (self.extractor)(&actual);
255             format!(
256                 "whose property `{}` is `{:#?}`, {}",
257                 self.property_desc,
258                 actual_inner,
259                 self.inner.explain_match(actual_inner)
260             )
261             .into()
262         }
263     }
264 
265     impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
266     where
267         InnerT: Debug,
268         OuterT: Debug,
269         MatcherT: for<'b> Matcher<&'b InnerT>,
270     {
matches(&self, actual: &'a OuterT) -> MatcherResult271         fn matches(&self, actual: &'a OuterT) -> MatcherResult {
272             self.inner.matches(&(self.extractor)(actual))
273         }
274 
describe(&self, matcher_result: MatcherResult) -> Description275         fn describe(&self, matcher_result: MatcherResult) -> Description {
276             format!(
277                 "has property `{}`, which {}",
278                 self.property_desc,
279                 self.inner.describe(matcher_result)
280             )
281             .into()
282         }
283 
explain_match(&self, actual: &'a OuterT) -> Description284         fn explain_match(&self, actual: &'a OuterT) -> Description {
285             let actual_inner = (self.extractor)(actual);
286             format!(
287                 "whose property `{}` is `{:#?}`, {}",
288                 self.property_desc,
289                 actual_inner,
290                 self.inner.explain_match(&actual_inner)
291             )
292             .into()
293         }
294     }
295 
296     /// **For internal use only. API stablility is not guaranteed!**
297     #[doc(hidden)]
property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>( extractor: ExtractorT, property_desc: &'static str, inner: MatcherT, ) -> PropertyRefMatcher<ExtractorT, MatcherT> where OuterT: Debug, InnerT: Debug, MatcherT: for<'a> Matcher<&'a InnerT>, ExtractorT: Fn(OuterT) -> InnerT,298     pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
299         extractor: ExtractorT,
300         property_desc: &'static str,
301         inner: MatcherT,
302     ) -> PropertyRefMatcher<ExtractorT, MatcherT>
303     where
304         OuterT: Debug,
305         InnerT: Debug,
306         MatcherT: for<'a> Matcher<&'a InnerT>,
307         ExtractorT: Fn(OuterT) -> InnerT,
308     {
309         PropertyRefMatcher { extractor, property_desc, inner }
310     }
311 
312     #[derive(MatcherBase)]
313     pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
314         extractor: ExtractorT,
315         property_desc: &'static str,
316         inner: MatcherT,
317     }
318 
319     impl<
320             InnerT: Debug,
321             OuterT: Debug + Copy,
322             MatcherT: for<'a> Matcher<&'a InnerT>,
323             ExtractorT: Fn(OuterT) -> InnerT,
324         > Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
325     {
matches(&self, actual: OuterT) -> MatcherResult326         fn matches(&self, actual: OuterT) -> MatcherResult {
327             self.inner.matches(&(self.extractor)(actual))
328         }
329 
describe(&self, matcher_result: MatcherResult) -> Description330         fn describe(&self, matcher_result: MatcherResult) -> Description {
331             format!(
332                 "has property `{}`, which {}",
333                 self.property_desc,
334                 self.inner.describe(matcher_result)
335             )
336             .into()
337         }
338 
explain_match(&self, actual: OuterT) -> Description339         fn explain_match(&self, actual: OuterT) -> Description {
340             let actual_inner = (self.extractor)(actual);
341             format!(
342                 "whose property `{}` is `{:#?}`, {}",
343                 self.property_desc,
344                 actual_inner,
345                 self.inner.explain_match(&actual_inner)
346             )
347             .into()
348         }
349     }
350 }
351