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