• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &2, &3]))?; // Passes
31 /// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 2, 3]))?; // Passes
32 /// verify_that!(value, pointwise!(|e| points_to(le(e)), vec![1, 3, 3]))?; // Passes
33 /// #     Ok(())
34 /// # }
35 /// # fn should_fail() -> Result<()> {
36 /// #     let value = vec![1, 2, 3];
37 /// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 1, 3]))?; // Fails
38 /// #     Ok(())
39 /// # }
40 /// # should_pass().unwrap();
41 /// # should_fail().unwrap_err();
42 /// ```
43 ///
44 /// One can also use a closure which returns a matcher:
45 ///
46 /// ```
47 /// # use googletest::prelude::*;
48 /// # fn should_pass() -> Result<()> {
49 /// let value = vec![1.00001, 2.000001, 3.00001];
50 /// verify_that!(value, pointwise!(|v| points_to(near(v, 0.001)), [1.0, 2.0, 3.0]))?;
51 /// #     Ok(())
52 /// # }
53 /// # should_pass().unwrap();
54 /// ```
55 ///
56 /// One can pass up to three containers to supply arguments to the function
57 /// creating the matcher:
58 ///
59 /// ```
60 /// # use googletest::prelude::*;
61 /// # fn should_pass() -> Result<()> {
62 /// let value = vec![1.00001, 2.000001, 3.00001];
63 /// verify_that!(value, pointwise!(|v, t| points_to(near(v, t)), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
64 /// verify_that!(
65 ///     value,
66 ///     pointwise!(
67 ///         |v, t, u| points_to(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 /// slice. More precisely, the actual value must implement [`IntoIterator`].
84 ///
85 /// ```
86 /// # use googletest::prelude::*;
87 /// # fn should_pass() -> Result<()> {
88 /// let value = vec![1, 2, 3];
89 /// verify_that!(value, pointwise!(|i| points_to(le(i)), [1, 3, 3]))?; // Passes
90 /// verify_that!([1, 2, 3], pointwise!(le, [1, 3, 3]))?; // Passes
91 /// #     Ok(())
92 /// # }
93 /// # should_pass().unwrap();
94 /// ```
95 ///
96 /// The second argument can be any value implementing `IntoIterator`, such as a
97 /// `Vec` or an array. The container does not have to have the same type as the
98 /// actual value, but the value type must be the same.
99 ///
100 /// **Note for users of the [`Pointwise`] matcher in C++ GoogleTest:**
101 ///
102 /// This macro differs from `Pointwise` in that the first parameter is not a
103 /// matcher which matches a pair but rather the name of a function of one
104 /// argument whose output is a matcher. This means that one can use standard
105 /// matchers like `eq`, `le`, and so on with `pointwise!` but certain C++ tests
106 /// using `Pointwise` will require some extra work to port.
107 ///
108 /// [`IntoIterator`]: std::iter::IntoIterator
109 /// [`Iterator`]: std::iter::Iterator
110 /// [`Iterator::collect`]: std::iter::Iterator::collect
111 /// [`Pointwise`]: https://google.github.io/googletest/reference/matchers.html#container-matchers
112 /// [`Vec`]: std::vec::Vec
113 #[macro_export]
114 #[doc(hidden)]
115 macro_rules! __pointwise {
116     ($matcher:expr, $container:expr) => {{
117         $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
118             $container.into_iter().map($matcher).collect(),
119         )
120     }};
121 
122     ($matcher:expr, $left_container:expr, $right_container:expr) => {{
123         $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
124             $left_container
125                 .into_iter()
126                 .zip($right_container.into_iter())
127                 .map(|(l, r)| $matcher(l, r))
128                 .collect(),
129         )
130     }};
131 
132     ($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
133         $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
134             $left_container
135                 .into_iter()
136                 .zip($right_container.into_iter().zip($middle_container.into_iter()))
137                 .map(|(l, (m, r))| $matcher(l, m, r))
138                 .collect(),
139         )
140     }};
141 }
142 
143 /// Module for use only by the procedural macros in this module.
144 ///
145 /// **For internal use only. API stablility is not guaranteed!**
146 #[doc(hidden)]
147 pub mod internal {
148     use crate::description::Description;
149     use crate::matcher::{Matcher, MatcherBase, MatcherResult};
150     use crate::matcher_support::zipped_iterator::zip;
151     use std::fmt::Debug;
152 
153     /// This struct is meant to be used only through the `pointwise` macro.
154     ///
155     /// **For internal use only. API stablility is not guaranteed!**
156     #[doc(hidden)]
157     #[derive(MatcherBase)]
158     pub struct PointwiseMatcher<MatcherT> {
159         matchers: Vec<MatcherT>,
160     }
161 
162     impl<MatcherT> PointwiseMatcher<MatcherT> {
new(matchers: Vec<MatcherT>) -> Self163         pub fn new(matchers: Vec<MatcherT>) -> Self {
164             Self { matchers }
165         }
166     }
167 
168     impl<T: Debug + Copy, MatcherT: Matcher<T>, ContainerT: Copy + Debug> Matcher<ContainerT>
169         for PointwiseMatcher<MatcherT>
170     where
171         ContainerT: IntoIterator<Item = T>,
172     {
matches(&self, actual: ContainerT) -> MatcherResult173         fn matches(&self, actual: ContainerT) -> MatcherResult {
174             let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
175             for (element, matcher) in zipped_iterator.by_ref() {
176                 if matcher.matches(element).is_no_match() {
177                     return MatcherResult::NoMatch;
178                 }
179             }
180             if zipped_iterator.has_size_mismatch() {
181                 MatcherResult::NoMatch
182             } else {
183                 MatcherResult::Match
184             }
185         }
186 
explain_match(&self, actual: ContainerT) -> Description187         fn explain_match(&self, actual: ContainerT) -> Description {
188             // TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
189             // extract as a separate library. (or implement pointwise! with
190             // elements_are)
191             let actual_iterator = actual.into_iter();
192             let mut zipped_iterator = zip(actual_iterator, self.matchers.iter());
193             let mut mismatches = Vec::new();
194             for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
195                 if e.matches(a).is_no_match() {
196                     mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
197                 }
198             }
199             if mismatches.is_empty() {
200                 if !zipped_iterator.has_size_mismatch() {
201                     "which matches all elements".into()
202                 } else {
203                     format!(
204                         "which has size {} (expected {})",
205                         zipped_iterator.left_size(),
206                         self.matchers.len()
207                     )
208                     .into()
209                 }
210             } else if mismatches.len() == 1 {
211                 format!("where {}", mismatches[0]).into()
212             } else {
213                 let mismatches = mismatches.into_iter().collect::<Description>();
214                 format!("where:\n{}", mismatches.bullet_list().indent()).into()
215             }
216         }
217 
describe(&self, matcher_result: MatcherResult) -> Description218         fn describe(&self, matcher_result: MatcherResult) -> Description {
219             format!(
220                 "{} elements satisfying respectively:\n{}",
221                 if matcher_result.into() { "has" } else { "doesn't have" },
222                 self.matchers
223                     .iter()
224                     .map(|m| m.describe(MatcherResult::Match))
225                     .collect::<Description>()
226                     .enumerate()
227                     .indent()
228             )
229             .into()
230         }
231     }
232 }
233