Skip to main content

googletest/matchers/
result_of_matcher.rs

1// Copyright 2024 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/// Matches a value where the result of `callable` applied to the value matches
16/// the inner matcher.
17///
18/// The `callable` will be called twice, so make sure it is pure.
19/// ```
20/// use googletest::prelude::*;
21/// fn should_pass() -> googletest::Result<()> {
22///    verify_that!(100, result_of!(|value| value + 1, eq(101)))?; // Passes
23///    Ok(())
24/// }
25///
26/// fn should_fail() -> googletest::Result<()> {
27///    verify_that!(100, result_of!(|value| value * 2, eq(100)))?; // Fails
28///    Ok(())
29/// }
30/// should_pass().unwrap();
31/// should_fail().unwrap_err();
32/// ```
33#[macro_export]
34macro_rules! __result_of {
35    ($function: expr, $matcher: expr) => {{
36        $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of(
37            $function,
38            $matcher,
39            stringify!($function),
40        )
41    }};
42}
43
44/// Matches a value where the reference to the result of `callable` applied to
45/// the value matches the inner matcher.
46///
47/// The `callable` will be called twice, so make sure it is pure.
48/// ```
49/// use googletest::prelude::*;
50/// fn should_pass_1() -> googletest::Result<()> {
51///    verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
52///    Ok(())
53/// }
54///
55/// fn should_pass_2() -> googletest::Result<()> {
56///    verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))?; // Passes
57///    Ok(())
58/// }
59///
60/// fn should_fail() -> googletest::Result<()> {
61///    verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
62///    Ok(())
63/// }
64/// should_pass_1().unwrap();
65/// should_pass_2().unwrap();
66/// should_fail().unwrap_err();
67/// ```
68#[macro_export]
69macro_rules! __result_of_ref {
70    ($function: expr, $matcher: expr) => {{
71        $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of_ref(
72            $function,
73            $matcher,
74            stringify!($function),
75        )
76    }};
77}
78
79/// Items for use only by the declarative macros in this module.
80///
81/// **For internal use only. API stability is not guaranteed!**
82#[doc(hidden)]
83pub mod internal {
84    use crate::description::Description;
85    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
86    use std::fmt::Debug;
87
88    pub fn result_of<Callable, InnerMatcher>(
89        callable: Callable,
90        inner_matcher: InnerMatcher,
91        callable_description: &'static str,
92    ) -> ResultOfMatcher<Callable, InnerMatcher> {
93        ResultOfMatcher { callable, inner_matcher, callable_description }
94    }
95
96    #[derive(MatcherBase)]
97    pub struct ResultOfMatcher<Callable, InnerMatcher> {
98        callable: Callable,
99        inner_matcher: InnerMatcher,
100        callable_description: &'static str,
101    }
102
103    impl<I: Copy + Debug, T: Debug + Copy, CallableT: Fn(I) -> T, InnerMatcherT: Matcher<T>>
104        Matcher<I> for ResultOfMatcher<CallableT, InnerMatcherT>
105    {
106        fn matches(&self, actual: I) -> MatcherResult {
107            self.inner_matcher.matches((self.callable)(actual))
108        }
109
110        fn describe(&self, matcher_result: MatcherResult) -> Description {
111            Description::new()
112                .text(format!("by applying {},", self.callable_description))
113                .nested(self.inner_matcher.describe(matcher_result))
114        }
115
116        fn explain_match(&self, actual: I) -> Description {
117            let actual_result = (self.callable)(actual);
118            Description::new()
119                .text(format!("which, results into {actual_result:?}",))
120                .nested(self.describe(self.matches(actual)))
121        }
122    }
123
124    pub fn result_of_ref<Callable, InnerMatcher>(
125        callable: Callable,
126        inner_matcher: InnerMatcher,
127        callable_description: &'static str,
128    ) -> ResultOfRefMatcher<Callable, InnerMatcher> {
129        ResultOfRefMatcher { callable, inner_matcher, callable_description }
130    }
131    #[derive(MatcherBase)]
132    pub struct ResultOfRefMatcher<Callable, InnerMatcher> {
133        callable: Callable,
134        inner_matcher: InnerMatcher,
135        callable_description: &'static str,
136    }
137
138    impl<
139            I: Copy + Debug,
140            T: Debug,
141            Callable: Fn(I) -> T,
142            InnerMatcherT: for<'a> Matcher<&'a T>,
143        > Matcher<I> for ResultOfRefMatcher<Callable, InnerMatcherT>
144    {
145        fn matches(&self, actual: I) -> MatcherResult {
146            self.inner_matcher.matches(&(self.callable)(actual))
147        }
148
149        fn describe(&self, matcher_result: MatcherResult) -> Description {
150            Description::new()
151                .text(format!("by applying {},", self.callable_description))
152                .nested(self.inner_matcher.describe(matcher_result))
153        }
154
155        fn explain_match(&self, actual: I) -> Description {
156            let actual_result = (self.callable)(actual);
157            Description::new()
158                .text(format!("which, results into {actual_result:?}",))
159                .nested(self.describe(self.matches(actual)))
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use crate::prelude::*;
167    use crate::Result;
168    use indoc::indoc;
169
170    #[test]
171    fn result_of_match_with_value() -> Result<()> {
172        verify_that!(1, result_of!(|value| value + 1, eq(2)))
173    }
174
175    #[test]
176    fn result_of_match_with_value_function() -> Result<()> {
177        fn inc_by_one(value: i32) -> i32 {
178            value + 1
179        }
180        verify_that!(1, result_of!(inc_by_one, eq(2)))
181    }
182
183    #[test]
184    fn result_of_match_with_different_value() -> Result<()> {
185        let result = verify_that!(0, result_of!(|value| value - 1, eq(2)));
186        verify_that!(
187            result,
188            err(displays_as(contains_substring(indoc!(
189                "
190                Value of: 0
191                Expected: by applying |value| value - 1,
192                  is equal to 2
193                Actual: 0,
194                  which, results into -1
195                    by applying |value| value - 1,
196                      isn't equal to 2
197                "
198            ))))
199        )
200    }
201
202    #[test]
203    fn result_of_match_with_different_value_block_closure() -> Result<()> {
204        let result = verify_that!(0, result_of!(|value| { value - 1 }, eq(2)));
205        verify_that!(
206            result,
207            err(displays_as(contains_substring(indoc!(
208                "
209                Value of: 0
210                Expected: by applying |value| { value - 1 },
211                  is equal to 2
212                Actual: 0,
213                  which, results into -1
214                    by applying |value| { value - 1 },
215                      isn't equal to 2
216                "
217            ))))
218        )
219    }
220
221    #[test]
222    fn result_of_match_with_different_value_multiline_closure() -> Result<()> {
223        let result = verify_that!(
224            0,
225            result_of!(
226                |value| {
227                    let dec = value - 1;
228                    let inc = dec + 1;
229                    inc - 2
230                },
231                eq(2)
232            )
233        );
234        verify_that!(
235            result,
236            err(displays_as(contains_substring(indoc!(
237                "
238                Value of: 0
239                Expected: by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
240                  is equal to 2
241                Actual: 0,
242                  which, results into -2
243                    by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
244                      isn't equal to 2
245                "
246            ))))
247        )
248    }
249
250    #[test]
251    fn result_of_match_with_different_value_function() -> Result<()> {
252        fn dec_by_one(value: i32) -> i32 {
253            value - 1
254        }
255        let result = verify_that!(0, result_of!(dec_by_one, eq(2)));
256        verify_that!(
257            result,
258            err(displays_as(contains_substring(indoc!(
259                "
260                Value of: 0
261                Expected: by applying dec_by_one,
262                  is equal to 2
263                Actual: 0,
264                  which, results into -1
265                    by applying dec_by_one,
266                      isn't equal to 2
267                "
268            ))))
269        )
270    }
271
272    #[test]
273    fn result_of_ref_match_with_string_reference() -> Result<()> {
274        verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))
275    }
276
277    #[test]
278    fn result_of_ref_match_with_string_reference_function() -> Result<()> {
279        fn to_upper_case<S: AsRef<str>>(s: S) -> String {
280            s.as_ref().to_uppercase()
281        }
282        verify_that!("hello", result_of_ref!(to_upper_case, eq("HELLO")))
283    }
284
285    #[test]
286    fn result_of_ref_match_with_copy_types() -> Result<()> {
287        verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))
288    }
289
290    #[test]
291    fn result_of_ref_match_with_different_value() -> Result<()> {
292        let result = verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")));
293        verify_that!(
294            result,
295            err(displays_as(contains_substring(indoc!(
296                r#"
297                Value of: "world"
298                Expected: by applying |s: &str| s.to_uppercase(),
299                  is equal to "HELLO"
300                Actual: "world",
301                  which, results into "WORLD"
302                    by applying |s: &str| s.to_uppercase(),
303                      isn't equal to "HELLO""#
304            ))))
305        )
306    }
307
308    #[test]
309    fn result_of_ref_match_with_different_value_block_closure() -> Result<()> {
310        let result =
311            verify_that!("world", result_of_ref!(|s: &str| { s.to_uppercase() }, eq("HELLO")));
312        verify_that!(
313            result,
314            err(displays_as(contains_substring(indoc!(
315                r#"
316            Value of: "world"
317            Expected: by applying |s: &str| { s.to_uppercase() },
318              is equal to "HELLO"
319            Actual: "world",
320              which, results into "WORLD"
321                by applying |s: &str| { s.to_uppercase() },
322                  isn't equal to "HELLO"
323            "#
324            ))))
325        )
326    }
327
328    #[test]
329    fn result_of_ref_match_with_different_value_function() -> Result<()> {
330        fn to_upper_case<S: AsRef<str>>(s: S) -> String {
331            s.as_ref().to_uppercase()
332        }
333        let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
334        verify_that!(
335            result,
336            err(displays_as(contains_substring(indoc!(
337                r#"
338            Value of: "world"
339            Expected: by applying to_upper_case,
340              is equal to "HELLO"
341            Actual: "world",
342              which, results into "WORLD"
343                by applying to_upper_case,
344                  isn't equal to "HELLO"
345            "#
346            ))))
347        )
348    }
349
350    #[test]
351    fn result_of_ref_match_different_with_closure_variable() -> Result<()> {
352        let to_upper_case = |s: &str| s.to_uppercase();
353        let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
354        verify_that!(
355            result,
356            err(displays_as(contains_substring(indoc!(
357                r#"
358                Value of: "world"
359                Expected: by applying to_upper_case,
360                  is equal to "HELLO"
361                Actual: "world",
362                  which, results into "WORLD"
363                    by applying to_upper_case,
364                      isn't equal to "HELLO"
365            "#
366            ))))
367        )
368    }
369
370    #[test]
371    fn result_of_ref_match_different_with_method_literal() -> Result<()> {
372        let result = verify_that!("world", result_of_ref!(str::to_uppercase, eq("HELLO")));
373        verify_that!(
374            result,
375            err(displays_as(contains_substring(indoc!(
376                r#"
377                Value of: "world"
378                Expected: by applying str::to_uppercase,
379                  is equal to "HELLO"
380                Actual: "world",
381                  which, results into "WORLD"
382                    by applying str::to_uppercase,
383                      isn't equal to "HELLO"
384            "#
385            ))))
386        )
387    }
388
389    #[test]
390    fn result_of_ref_match_different_with_function_return_closure() -> Result<()> {
391        fn upper_case() -> impl Fn(&str) -> String {
392            |s: &str| s.to_uppercase()
393        }
394        let result = verify_that!("world", result_of_ref!(upper_case(), eq("HELLO")));
395        verify_that!(
396            result,
397            err(displays_as(contains_substring(indoc!(
398                r#"
399            Value of: "world"
400            Expected: by applying upper_case(),
401              is equal to "HELLO"
402            Actual: "world",
403              which, results into "WORLD"
404                by applying upper_case(),
405                  isn't equal to "HELLO"
406            "#
407            ))))
408        )
409    }
410
411    #[test]
412    fn test_describe_simple() -> Result<()> {
413        let matcher = result_of!(|x| x + 1, eq(2));
414        let description = matcher.describe(matcher.matches(0));
415        verify_that!(
416            description,
417            displays_as(eq(indoc!(
418                r#"
419        by applying |x| x + 1,
420          isn't equal to 2"#
421            )))
422        )
423    }
424
425    #[test]
426    fn test_describe_complicated() -> Result<()> {
427        let matcher = result_of_ref!(
428            |s: &str| s.chars().collect::<Vec<_>>(),
429            each(predicate(char::is_ascii_alphabetic))
430        );
431        let description = matcher.describe(matcher.matches("A quick brown fox"));
432        verify_that!(
433            description,
434            displays_as(eq(indoc!(
435                r#"
436        by applying |s: &str| s.chars().collect::<Vec<_>>(),
437          contains no element that matches"#
438            )))
439        )
440    }
441}