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 use crate::description::Description;
16 use crate::matcher::{Matcher, MatcherResult};
17 use std::{fmt::Debug, marker::PhantomData};
18 
19 /// Matches a container all of whose elements are matched by the matcher
20 /// `inner`.
21 ///
22 /// `T` can be any container such that `&T` implements `IntoIterator`. This
23 /// includes `Vec`, arrays, and (dereferenced) slices.
24 ///
25 /// ```
26 /// # use googletest::prelude::*;
27 /// # use std::collections::HashSet;
28 /// # fn should_pass_1() -> Result<()> {
29 /// let value = vec![1, 2, 3];
30 /// verify_that!(value, each(gt(0)))?;  // Passes
31 /// let array_value = [1, 2, 3];
32 /// verify_that!(array_value, each(gt(0)))?;  // Passes
33 /// let slice_value = &[1, 2, 3];
34 /// verify_that!(*slice_value, each(gt(0)))?;  // Passes
35 /// #     Ok(())
36 /// # }
37 /// # fn should_fail() -> Result<()> {
38 /// #     let value = vec![1, 2, 3];
39 /// verify_that!(value, each(lt(2)))?;  // Fails: 2 and 3 are not less than 2
40 /// #     Ok(())
41 /// # }
42 ///
43 /// # fn should_pass_2() -> Result<()> {
44 /// let value: HashSet<i32> = [1, 2, 3].into();
45 /// verify_that!(value, each(gt(0)))?;  // Passes
46 /// #     Ok(())
47 /// # }
48 /// # should_pass_1().unwrap();
49 /// # should_fail().unwrap_err();
50 /// # should_pass_2().unwrap();
51 /// ```
52 ///
53 /// One can also verify the contents of a slice by dereferencing it:
54 ///
55 /// ```
56 /// # use googletest::prelude::*;
57 /// # fn should_pass() -> Result<()> {
58 /// let value = &[1, 2, 3];
59 /// verify_that!(*value, each(gt(0)))?;
60 /// #     Ok(())
61 /// # }
62 /// # should_pass().unwrap();
63 /// ```
each<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT>( inner: MatcherT, ) -> impl Matcher<ActualT = ActualT> where for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>, MatcherT: Matcher<ActualT = ElementT>,64 pub fn each<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT>(
65     inner: MatcherT,
66 ) -> impl Matcher<ActualT = ActualT>
67 where
68     for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
69     MatcherT: Matcher<ActualT = ElementT>,
70 {
71     EachMatcher { inner, phantom: Default::default() }
72 }
73 
74 struct EachMatcher<ActualT: ?Sized, MatcherT> {
75     inner: MatcherT,
76     phantom: PhantomData<ActualT>,
77 }
78 
79 impl<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT> Matcher for EachMatcher<ActualT, MatcherT>
80 where
81     for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
82     MatcherT: Matcher<ActualT = ElementT>,
83 {
84     type ActualT = ActualT;
85 
matches(&self, actual: &ActualT) -> MatcherResult86     fn matches(&self, actual: &ActualT) -> MatcherResult {
87         for element in actual {
88             if self.inner.matches(element).is_no_match() {
89                 return MatcherResult::NoMatch;
90             }
91         }
92         MatcherResult::Match
93     }
94 
explain_match(&self, actual: &ActualT) -> Description95     fn explain_match(&self, actual: &ActualT) -> Description {
96         let mut non_matching_elements = Vec::new();
97         for (index, element) in actual.into_iter().enumerate() {
98             if self.inner.matches(element).is_no_match() {
99                 non_matching_elements.push((index, element, self.inner.explain_match(element)));
100             }
101         }
102         if non_matching_elements.is_empty() {
103             return format!("whose each element {}", self.inner.describe(MatcherResult::Match))
104                 .into();
105         }
106         if non_matching_elements.len() == 1 {
107             let (idx, element, explanation) = non_matching_elements.remove(0);
108             return format!("whose element #{idx} is {element:?}, {explanation}").into();
109         }
110 
111         let failed_indexes = non_matching_elements
112             .iter()
113             .map(|&(idx, _, _)| format!("#{idx}"))
114             .collect::<Vec<_>>()
115             .join(", ");
116         let element_explanations = non_matching_elements
117             .iter()
118             .map(|&(_, element, ref explanation)| format!("{element:?}, {explanation}"))
119             .collect::<Description>()
120             .indent();
121         format!("whose elements {failed_indexes} don't match\n{element_explanations}").into()
122     }
123 
describe(&self, matcher_result: MatcherResult) -> Description124     fn describe(&self, matcher_result: MatcherResult) -> Description {
125         match matcher_result {
126             MatcherResult::Match => {
127                 format!("only contains elements that {}", self.inner.describe(MatcherResult::Match))
128                     .into()
129             }
130             MatcherResult::NoMatch => {
131                 format!("contains no element that {}", self.inner.describe(MatcherResult::Match))
132                     .into()
133             }
134         }
135     }
136 }
137 
138 #[cfg(test)]
139 mod tests {
140     use super::each;
141     use crate::prelude::*;
142     use indoc::indoc;
143     use std::collections::HashSet;
144 
145     #[test]
each_matches_empty_vec() -> Result<()>146     fn each_matches_empty_vec() -> Result<()> {
147         let value: Vec<i32> = vec![];
148         verify_that!(value, each(gt(0)))
149     }
150 
151     #[test]
each_matches_vec_with_one_element() -> Result<()>152     fn each_matches_vec_with_one_element() -> Result<()> {
153         let value = vec![1];
154         verify_that!(value, each(gt(0)))
155     }
156 
157     #[test]
each_matches_vec_with_two_elements() -> Result<()>158     fn each_matches_vec_with_two_elements() -> Result<()> {
159         let value = vec![1, 2];
160         verify_that!(value, each(gt(0)))
161     }
162 
163     #[test]
each_matches_slice_with_one_element() -> Result<()>164     fn each_matches_slice_with_one_element() -> Result<()> {
165         let value = &[1];
166         verify_that!(*value, each(gt(0)))
167     }
168 
169     #[test]
each_matches_hash_set_with_one_element() -> Result<()>170     fn each_matches_hash_set_with_one_element() -> Result<()> {
171         let value: HashSet<i32> = [1].into();
172         verify_that!(value, each(gt(0)))
173     }
174 
175     #[test]
each_does_not_match_when_first_element_does_not_match() -> Result<()>176     fn each_does_not_match_when_first_element_does_not_match() -> Result<()> {
177         let value = vec![0];
178         verify_that!(value, not(each(gt(1))))
179     }
180 
181     #[test]
each_does_not_match_when_second_element_does_not_match() -> Result<()>182     fn each_does_not_match_when_second_element_does_not_match() -> Result<()> {
183         let value = vec![2, 0];
184         verify_that!(value, not(each(gt(1))))
185     }
186 
187     #[test]
each_shows_correct_message_when_first_item_does_not_match() -> Result<()>188     fn each_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
189         let result = verify_that!(vec![0, 2, 3], each(gt(0)));
190 
191         verify_that!(
192             result,
193             err(displays_as(contains_substring(indoc!(
194                 "
195                 Value of: vec![0, 2, 3]
196                 Expected: only contains elements that is greater than 0
197                 Actual: [0, 2, 3],
198                   whose element #0 is 0, which is less than or equal to 0"
199             ))))
200         )
201     }
202 
203     #[test]
each_shows_correct_message_when_second_item_does_not_match() -> Result<()>204     fn each_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
205         let result = verify_that!(vec![1, 0, 3], each(gt(0)));
206 
207         verify_that!(
208             result,
209             err(displays_as(contains_substring(indoc!(
210                 "
211                 Value of: vec![1, 0, 3]
212                 Expected: only contains elements that is greater than 0
213                 Actual: [1, 0, 3],
214                   whose element #1 is 0, which is less than or equal to 0"
215             ))))
216         )
217     }
218 
219     #[test]
each_shows_correct_message_when_first_two_items_do_not_match() -> Result<()>220     fn each_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
221         let result = verify_that!(vec![0, 1, 3], each(gt(1)));
222 
223         verify_that!(
224             result,
225             err(displays_as(contains_substring(indoc!(
226                 "
227                 Value of: vec![0, 1, 3]
228                 Expected: only contains elements that is greater than 1
229                 Actual: [0, 1, 3],
230                   whose elements #0, #1 don't match
231                     0, which is less than or equal to 1
232                     1, which is less than or equal to 1"
233             ))))
234         )
235     }
236     #[test]
each_shows_inner_explanation() -> Result<()>237     fn each_shows_inner_explanation() -> Result<()> {
238         let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(1))));
239 
240         verify_that!(
241             result,
242             err(displays_as(contains_substring(indoc!(
243                 "
244                 Expected: only contains elements that only contains elements that is equal to 1
245                 Actual: [[1, 2], [1]],
246                   whose element #0 is [1, 2], whose element #1 is 2, which isn't equal to 1"
247             ))))
248         )
249     }
250 }
251