Skip to content

Commit 01c0279

Browse files
committed
Add ResultOf and ResultOfRef matcher
1 parent c13878c commit 01c0279

File tree

2 files changed

+342
-2
lines changed

2 files changed

+342
-2
lines changed

googletest/src/matchers/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mod points_to_matcher;
5050
mod pointwise_matcher;
5151
mod predicate_matcher;
5252
mod property_matcher;
53+
mod result_of_matcher;
5354
mod some_matcher;
5455
mod str_matcher;
5556
mod subset_of_matcher;
@@ -95,8 +96,8 @@ pub use superset_of_matcher::superset_of;
9596
pub use crate::{
9697
__all as all, __any as any, __contains_each as contains_each, __elements_are as elements_are,
9798
__field as field, __is_contained_in as is_contained_in, __matches_pattern as matches_pattern,
98-
__pat as pat, __pointwise as pointwise, __property as property,
99-
__unordered_elements_are as unordered_elements_are,
99+
__pat as pat, __pointwise as pointwise, __property as property, __result_of as result_of,
100+
__result_of_ref as result_of_ref, __unordered_elements_are as unordered_elements_are,
100101
};
101102

102103
// Types and functions used by macros matchers.
@@ -113,6 +114,7 @@ pub mod __internal_unstable_do_not_depend_on_these {
113114
pub use super::matches_pattern::internal::pattern_only;
114115
pub use super::pointwise_matcher::internal::PointwiseMatcher;
115116
pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
117+
pub use super::result_of_matcher::internal::{result_of, result_of_ref};
116118
pub use super::unordered_elements_are_matcher::internal::{
117119
Requirements, UnorderedElementsAreMatcher,
118120
};
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
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]
34+
#[doc(hidden)]
35+
macro_rules! __result_of {
36+
($($t: tt)*) => { $crate::result_of_internal!($($t)*) };
37+
}
38+
39+
#[macro_export]
40+
macro_rules! result_of_internal {
41+
($function: expr, $matcher: expr) => {{
42+
$crate::matchers::__internal_unstable_do_not_depend_on_these::result_of(
43+
$function,
44+
$matcher,
45+
stringify!($function),
46+
)
47+
}};
48+
}
49+
50+
/// Matches a value where the reference to the result of `callable` applied to
51+
/// the value matches the inner matcher.
52+
///
53+
/// The `callable` will be called twice, so make sure it is pure.
54+
/// ```
55+
/// # use googletest::prelude::*;
56+
/// # fn should_pass_1() -> googletest::Result<()> {
57+
/// # verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
58+
/// # Ok(())
59+
/// # }
60+
///
61+
/// # fn should_pass_2() -> googletest::Result<()> {
62+
/// # verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))?; // Passes
63+
/// # Ok(())
64+
/// # }
65+
///
66+
/// # fn should_fail() -> googletest::Result<()> {
67+
/// # verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
68+
/// # Ok(())
69+
/// # }
70+
/// # should_pass_1().unwrap();
71+
/// # should_pass_2().unwrap();
72+
/// # should_fail().unwrap_err();
73+
/// ```
74+
#[macro_export]
75+
#[doc(hidden)]
76+
macro_rules! __result_of_ref {
77+
($($t: tt)*) => { $crate::result_of_ref_internal!($($t)*)};
78+
}
79+
80+
#[macro_export]
81+
macro_rules! result_of_ref_internal {
82+
($function: expr, $matcher: expr) => {{
83+
$crate::matchers::__internal_unstable_do_not_depend_on_these::result_of_ref(
84+
$function,
85+
$matcher,
86+
stringify!($function),
87+
)
88+
}};
89+
}
90+
91+
/// Items for use only by the declarative macros in this module.
92+
///
93+
/// **For internal use only. API stability is not guaranteed!**
94+
#[doc(hidden)]
95+
pub mod internal {
96+
use crate::description::Description;
97+
use crate::matcher::{Matcher, MatcherBase, MatcherResult};
98+
use std::fmt::Debug;
99+
100+
pub fn result_of<Callable, InnerMatcher>(
101+
callable: Callable,
102+
inner_matcher: InnerMatcher,
103+
callable_description: &'static str,
104+
) -> ResultOfMatcher<Callable, InnerMatcher> {
105+
ResultOfMatcher { callable, inner_matcher, callable_description }
106+
}
107+
108+
#[derive(MatcherBase)]
109+
pub struct ResultOfMatcher<Callable, InnerMatcher> {
110+
callable: Callable,
111+
inner_matcher: InnerMatcher,
112+
callable_description: &'static str,
113+
}
114+
115+
impl<I: Copy + Debug, T: Debug + Copy, CallableT: Fn(I) -> T, InnerMatcherT: Matcher<T>>
116+
Matcher<I> for ResultOfMatcher<CallableT, InnerMatcherT>
117+
{
118+
fn matches(&self, actual: I) -> MatcherResult {
119+
self.inner_matcher.matches((self.callable)(actual))
120+
}
121+
122+
fn describe(&self, matcher_result: MatcherResult) -> Description {
123+
self.inner_matcher.describe(matcher_result)
124+
}
125+
126+
fn explain_match(&self, actual: I) -> Description {
127+
let actual_result = (self.callable)(actual);
128+
format!(
129+
"where the result of applying {actual:?} to the callable `{}` is {actual_result:?} which {}",
130+
self.callable_description,
131+
self.describe(self.inner_matcher.matches(actual_result))
132+
)
133+
.into()
134+
}
135+
}
136+
137+
pub fn result_of_ref<Callable, InnerMatcher>(
138+
callable: Callable,
139+
inner_matcher: InnerMatcher,
140+
callable_description: &'static str,
141+
) -> ResultOfRefMatcher<Callable, InnerMatcher> {
142+
ResultOfRefMatcher { callable, inner_matcher, callable_description }
143+
}
144+
#[derive(MatcherBase)]
145+
pub struct ResultOfRefMatcher<Callable, InnerMatcher> {
146+
callable: Callable,
147+
inner_matcher: InnerMatcher,
148+
callable_description: &'static str,
149+
}
150+
151+
impl<
152+
I: Copy + Debug,
153+
T: Debug,
154+
Callable: Fn(I) -> T,
155+
InnerMatcherT: for<'a> Matcher<&'a T>,
156+
> Matcher<I> for ResultOfRefMatcher<Callable, InnerMatcherT>
157+
{
158+
fn matches(&self, actual: I) -> MatcherResult {
159+
self.inner_matcher.matches(&(self.callable)(actual))
160+
}
161+
162+
fn describe(&self, matcher_result: MatcherResult) -> Description {
163+
self.inner_matcher.describe(matcher_result)
164+
}
165+
166+
fn explain_match(&self, actual: I) -> Description {
167+
let actual_result = (self.callable)(actual);
168+
Description::new().text(format!("where the result of applying {actual:?} to the callable `{}` is {actual_result:?}", self.callable_description)).nested(self.inner_matcher.explain_match(&actual_result))
169+
}
170+
}
171+
}
172+
173+
#[cfg(test)]
174+
mod tests {
175+
use crate::prelude::*;
176+
177+
#[test]
178+
fn result_of_match_with_value() -> Result<()> {
179+
verify_that!(1, result_of!(|value| value + 1, eq(2)))
180+
}
181+
182+
#[test]
183+
fn result_of_match_with_value_function() -> Result<()> {
184+
fn inc_by_one(value: i32) -> i32 {
185+
value + 1
186+
}
187+
verify_that!(1, result_of!(inc_by_one, eq(2)))
188+
}
189+
190+
#[test]
191+
fn result_of_match_with_different_value() -> Result<()> {
192+
let result = verify_that!(0, result_of!(|value| value - 1, eq(2)));
193+
verify_that!(
194+
result,
195+
err(displays_as(contains_substring(
196+
"where the result of applying 0 to the callable `|value| value - 1` is -1 which isn't equal to 2"
197+
)))
198+
)
199+
}
200+
201+
#[test]
202+
fn result_of_match_with_different_value_block_closure() -> Result<()> {
203+
let result = verify_that!(0, result_of!(|value| { value - 1 }, eq(2)));
204+
verify_that!(
205+
result,
206+
err(displays_as(contains_substring(
207+
"where the result of applying 0 to the callable `|value| { value - 1 }` is -1 which isn't equal to 2"
208+
)))
209+
)
210+
}
211+
212+
#[test]
213+
fn result_of_match_with_different_value_multiline_closure() -> Result<()> {
214+
let result = verify_that!(
215+
0,
216+
result_of!(
217+
|value| {
218+
let dec = value - 1;
219+
let inc = dec + 1;
220+
inc - 2
221+
},
222+
eq(2)
223+
)
224+
);
225+
verify_that!(
226+
result,
227+
err(displays_as(contains_substring(
228+
"where the result of applying 0 to the callable `|value| { let dec = value - 1; let inc = dec + 1; inc - 2 }` is -2 which isn't equal to 2"
229+
)))
230+
)
231+
}
232+
233+
#[test]
234+
fn result_of_match_with_different_value_function() -> Result<()> {
235+
fn dec_by_one(value: i32) -> i32 {
236+
value - 1
237+
}
238+
let result = verify_that!(0, result_of!(dec_by_one, eq(2)));
239+
verify_that!(
240+
result,
241+
err(displays_as(contains_substring(
242+
"where the result of applying 0 to the callable `dec_by_one` is -1 which isn't equal to 2"
243+
)))
244+
)
245+
}
246+
247+
#[test]
248+
fn result_of_ref_match_with_string_reference() -> Result<()> {
249+
verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))
250+
}
251+
252+
#[test]
253+
fn result_of_ref_match_with_string_reference_function() -> Result<()> {
254+
fn to_upper_case<S: AsRef<str>>(s: S) -> String {
255+
s.as_ref().to_uppercase()
256+
}
257+
verify_that!("hello", result_of_ref!(to_upper_case, eq("HELLO")))
258+
}
259+
260+
#[test]
261+
fn result_of_ref_match_with_copy_types() -> Result<()> {
262+
verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))
263+
}
264+
265+
#[test]
266+
fn result_of_ref_match_with_different_value() -> Result<()> {
267+
let result = verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")));
268+
verify_that!(
269+
result,
270+
err(displays_as(contains_substring(
271+
"where the result of applying \"world\" to the callable `|s: &str| s.to_uppercase()` is \"WORLD\"\n which isn't equal to \"HELLO\""
272+
)))
273+
)
274+
}
275+
276+
#[test]
277+
fn result_of_ref_match_with_different_value_block_closure() -> Result<()> {
278+
let result =
279+
verify_that!("world", result_of_ref!(|s: &str| { s.to_uppercase() }, eq("HELLO")));
280+
verify_that!(
281+
result,
282+
err(displays_as(contains_substring(
283+
"where the result of applying \"world\" to the callable `|s: &str| { s.to_uppercase() }` is \"WORLD\"\n which isn't equal to \"HELLO\""
284+
)))
285+
)
286+
}
287+
288+
#[test]
289+
fn result_of_ref_match_with_different_value_function() -> Result<()> {
290+
fn to_upper_case<S: AsRef<str>>(s: S) -> String {
291+
s.as_ref().to_uppercase()
292+
}
293+
let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
294+
verify_that!(
295+
result,
296+
err(displays_as(contains_substring(
297+
"where the result of applying \"world\" to the callable `to_upper_case` is \"WORLD\"\n which isn't equal to \"HELLO\""
298+
)))
299+
)
300+
}
301+
302+
#[test]
303+
fn result_of_ref_match_different_with_closure_variable() -> Result<()> {
304+
let to_upper_case = |s: &str| s.to_uppercase();
305+
let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
306+
verify_that!(
307+
result,
308+
err(displays_as(contains_substring(
309+
"where the result of applying \"world\" to the callable `to_upper_case` is \"WORLD\"\n which isn't equal to \"HELLO\""
310+
)))
311+
)
312+
}
313+
314+
#[test]
315+
fn result_of_ref_match_different_with_method_literal() -> Result<()> {
316+
let result = verify_that!("world", result_of_ref!(str::to_uppercase, eq("HELLO")));
317+
verify_that!(
318+
result,
319+
err(displays_as(contains_substring(
320+
"where the result of applying \"world\" to the callable `str::to_uppercase` is \"WORLD\"\n which isn't equal to \"HELLO\""
321+
)))
322+
)
323+
}
324+
325+
#[test]
326+
fn result_of_ref_match_different_with_function_return_closure() -> Result<()> {
327+
fn upper_case() -> impl Fn(&str) -> String {
328+
|s: &str| s.to_uppercase()
329+
}
330+
let result = verify_that!("world", result_of_ref!(upper_case(), eq("HELLO")));
331+
verify_that!(
332+
result,
333+
err(displays_as(contains_substring(
334+
"where the result of applying \"world\" to the callable `upper_case()` is \"WORLD\"\n which isn't equal to \"HELLO\""
335+
)))
336+
)
337+
}
338+
}

0 commit comments

Comments
 (0)