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 // There are no visible documentation elements in this module; the declarative
16 // macro is documented in the matcher module.
17 #![doc(hidden)]
18 
19 /// Generates a matcher which matches a container each of whose elements match
20 /// the given matcher name applied respectively to each element of the given
21 /// container.
22 ///
23 /// For example, the following matches a container of integers each of which
24 /// does not exceed the given upper bounds:
25 ///
26 /// ```
27 /// # use googletest::prelude::*;
28 /// # fn should_pass() -> Result<()> {
29 /// let value = vec![1, 2, 3];
30 /// verify_that!(value, pointwise!(le, [1, 3, 3]))?; // Passes
31 /// verify_that!(value, pointwise!(le, vec![1, 3, 3]))?; // Passes
32 /// #     Ok(())
33 /// # }
34 /// # fn should_fail() -> Result<()> {
35 /// #     let value = vec![1, 2, 3];
36 /// verify_that!(value, pointwise!(le, [1, 1, 3]))?; // Fails
37 /// #     Ok(())
38 /// # }
39 /// # should_pass().unwrap();
40 /// # should_fail().unwrap_err();
41 /// ```
42 ///
43 /// One can also use a closure which returns a matcher:
44 ///
45 /// ```
46 /// # use googletest::prelude::*;
47 /// # fn should_pass() -> Result<()> {
48 /// let value = vec![1.00001, 2.000001, 3.00001];
49 /// verify_that!(value, pointwise!(|v| near(v, 0.001), [1.0, 2.0, 3.0]))?;
50 /// #     Ok(())
51 /// # }
52 /// # should_pass().unwrap();
53 /// ```
54 ///
55 /// One can pass up to three containers to supply arguments to the function
56 /// creating the matcher:
57 ///
58 /// ```
59 /// # use googletest::prelude::*;
60 /// # fn should_pass() -> Result<()> {
61 /// let value = vec![1.00001, 2.000001, 3.00001];
62 /// verify_that!(value, pointwise!(|v, t| near(v, t), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
63 /// verify_that!(value, pointwise!(near, [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?; // Same as above
64 /// verify_that!(
65 ///     value,
66 ///     pointwise!(
67 ///         |v, t, u| near(v, t * u),
68 ///         [1.0, 2.0, 3.0],
69 ///         [0.001, 0.0001, 0.01],
70 ///         [0.5, 0.5, 1.0]
71 ///     )
72 /// )?;
73 /// #     Ok(())
74 /// # }
75 /// # should_pass().unwrap();
76 /// ```
77 ///
78 /// When using `pointwise!` with multiple containers, the caller must ensure
79 /// that all of the containers have the same size. This matcher does not check
80 /// whether the sizes match.
81 ///
82 /// The actual value must be a container such as a `Vec`, an array, or a
83 /// dereferenced slice. More precisely, a shared borrow of the actual value must
84 /// implement [`IntoIterator`].
85 ///
86 /// ```
87 /// # use googletest::prelude::*;
88 /// # fn should_pass() -> Result<()> {
89 /// let value = vec![1, 2, 3];
90 /// verify_that!(*value.as_slice(), pointwise!(le, [1, 3, 3]))?; // Passes
91 /// verify_that!([1, 2, 3], pointwise!(le, [1, 3, 3]))?; // Passes
92 /// #     Ok(())
93 /// # }
94 /// # should_pass().unwrap();
95 /// ```
96 ///
97 /// This matcher does not support matching directly against an [`Iterator`]. To
98 /// match against an iterator, use [`Iterator::collect`] to build a [`Vec`]
99 /// first.
100 ///
101 /// The second argument can be any value implementing `IntoIterator`, such as a
102 /// `Vec` or an array. The container does not have to have the same type as the
103 /// actual value, but the value type must be the same.
104 ///
105 /// **Note for users of the [`Pointwise`] matcher in C++ GoogleTest:**
106 ///
107 /// This macro differs from `Pointwise` in that the first parameter is not a
108 /// matcher which matches a pair but rather the name of a function of one
109 /// argument whose output is a matcher. This means that one can use standard
110 /// matchers like `eq`, `le`, and so on with `pointwise!` but certain C++ tests
111 /// using `Pointwise` will require some extra work to port.
112 ///
113 /// [`IntoIterator`]: std::iter::IntoIterator
114 /// [`Iterator`]: std::iter::Iterator
115 /// [`Iterator::collect`]: std::iter::Iterator::collect
116 /// [`Pointwise`]: https://google.github.io/googletest/reference/matchers.html#container-matchers
117 /// [`Vec`]: std::vec::Vec
118 #[macro_export]
119 #[doc(hidden)]
120 macro_rules! __pointwise {
121     ($matcher:expr, $container:expr) => {{
122         use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
123         PointwiseMatcher::new($container.into_iter().map($matcher).collect())
124     }};
125 
126     ($matcher:expr, $left_container:expr, $right_container:expr) => {{
127         use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
128         PointwiseMatcher::new(
129             $left_container
130                 .into_iter()
131                 .zip($right_container.into_iter())
132                 .map(|(l, r)| $matcher(l, r))
133                 .collect(),
134         )
135     }};
136 
137     ($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
138         use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
139         PointwiseMatcher::new(
140             $left_container
141                 .into_iter()
142                 .zip($right_container.into_iter().zip($middle_container.into_iter()))
143                 .map(|(l, (m, r))| $matcher(l, m, r))
144                 .collect(),
145         )
146     }};
147 }
148 
149 /// Module for use only by the procedural macros in this module.
150 ///
151 /// **For internal use only. API stablility is not guaranteed!**
152 #[doc(hidden)]
153 pub mod internal {
154     use crate::description::Description;
155     use crate::matcher::{Matcher, MatcherResult};
156     use crate::matcher_support::zipped_iterator::zip;
157     use std::{fmt::Debug, marker::PhantomData};
158 
159     /// This struct is meant to be used only through the `pointwise` macro.
160     ///
161     /// **For internal use only. API stablility is not guaranteed!**
162     #[doc(hidden)]
163     pub struct PointwiseMatcher<ContainerT: ?Sized, MatcherT> {
164         matchers: Vec<MatcherT>,
165         phantom: PhantomData<ContainerT>,
166     }
167 
168     impl<ContainerT: ?Sized, MatcherT> PointwiseMatcher<ContainerT, MatcherT> {
new(matchers: Vec<MatcherT>) -> Self169         pub fn new(matchers: Vec<MatcherT>) -> Self {
170             Self { matchers, phantom: Default::default() }
171         }
172     }
173 
174     impl<T: Debug, MatcherT: Matcher<ActualT = T>, ContainerT: ?Sized + Debug> Matcher
175         for PointwiseMatcher<ContainerT, MatcherT>
176     where
177         for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
178     {
179         type ActualT = ContainerT;
180 
matches(&self, actual: &ContainerT) -> MatcherResult181         fn matches(&self, actual: &ContainerT) -> MatcherResult {
182             let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
183             for (element, matcher) in zipped_iterator.by_ref() {
184                 if matcher.matches(element).is_no_match() {
185                     return MatcherResult::NoMatch;
186                 }
187             }
188             if zipped_iterator.has_size_mismatch() {
189                 MatcherResult::NoMatch
190             } else {
191                 MatcherResult::Match
192             }
193         }
194 
explain_match(&self, actual: &ContainerT) -> Description195         fn explain_match(&self, actual: &ContainerT) -> Description {
196             // TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
197             // extract as a separate library. (or implement pointwise! with
198             // elements_are)
199             let actual_iterator = actual.into_iter();
200             let mut zipped_iterator = zip(actual_iterator, self.matchers.iter());
201             let mut mismatches = Vec::new();
202             for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
203                 if e.matches(a).is_no_match() {
204                     mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
205                 }
206             }
207             if mismatches.is_empty() {
208                 if !zipped_iterator.has_size_mismatch() {
209                     "which matches all elements".into()
210                 } else {
211                     format!(
212                         "which has size {} (expected {})",
213                         zipped_iterator.left_size(),
214                         self.matchers.len()
215                     )
216                     .into()
217                 }
218             } else if mismatches.len() == 1 {
219                 format!("where {}", mismatches[0]).into()
220             } else {
221                 let mismatches = mismatches.into_iter().collect::<Description>();
222                 format!("where:\n{}", mismatches.bullet_list().indent()).into()
223             }
224         }
225 
describe(&self, matcher_result: MatcherResult) -> Description226         fn describe(&self, matcher_result: MatcherResult) -> Description {
227             format!(
228                 "{} elements satisfying respectively:\n{}",
229                 if matcher_result.into() { "has" } else { "doesn't have" },
230                 self.matchers
231                     .iter()
232                     .map(|m| m.describe(MatcherResult::Match))
233                     .collect::<Description>()
234                     .enumerate()
235                     .indent()
236             )
237             .into()
238         }
239     }
240 }
241