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::{
16     description::Description,
17     matcher::{Matcher, MatcherResult},
18 };
19 use std::{fmt::Debug, marker::PhantomData};
20 
21 /// Matches an iterable type whose elements contain a value matched by `inner`.
22 ///
23 /// By default, this matches a container with any number of elements matched
24 /// by `inner`. Use the method [`ContainsMatcher::times`] to constrain the
25 /// matched containers to a specific number of matching elements.
26 ///
27 /// ```
28 /// # use googletest::prelude::*;
29 /// # fn should_pass() -> Result<()> {
30 /// verify_that!(["Some value"], contains(eq("Some value")))?;  // Passes
31 /// verify_that!(vec!["Some value"], contains(eq("Some value")))?;  // Passes
32 /// #     Ok(())
33 /// # }
34 /// # fn should_fail_1() -> Result<()> {
35 /// verify_that!([] as [String; 0], contains(eq("Some value")))?;   // Fails
36 /// #     Ok(())
37 /// # }
38 /// # fn should_fail_2() -> Result<()> {
39 /// verify_that!(["Some value"], contains(eq("Some other value")))?;   // Fails
40 /// #     Ok(())
41 /// # }
42 /// # should_pass().unwrap();
43 /// # should_fail_1().unwrap_err();
44 /// # should_fail_2().unwrap_err();
45 /// ```
contains<T, InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<T, InnerMatcherT>46 pub fn contains<T, InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<T, InnerMatcherT> {
47     ContainsMatcher { inner, count: None, phantom: Default::default() }
48 }
49 
50 /// A matcher which matches a container containing one or more elements a given
51 /// inner [`Matcher`] matches.
52 pub struct ContainsMatcher<T, InnerMatcherT> {
53     inner: InnerMatcherT,
54     count: Option<Box<dyn Matcher<ActualT = usize>>>,
55     phantom: PhantomData<T>,
56 }
57 
58 impl<T, InnerMatcherT> ContainsMatcher<T, InnerMatcherT> {
59     /// Configures this instance to match containers which contain a number of
60     /// matching items matched by `count`.
61     ///
62     /// For example, to assert that exactly three matching items must be
63     /// present, use:
64     ///
65     /// ```ignore
66     /// contains(...).times(eq(3))
67     /// ```
68     ///
69     /// One can also use `times(eq(0))` to test for the *absence* of an item
70     /// matching the expected value.
times(mut self, count: impl Matcher<ActualT = usize> + 'static) -> Self71     pub fn times(mut self, count: impl Matcher<ActualT = usize> + 'static) -> Self {
72         self.count = Some(Box::new(count));
73         self
74     }
75 }
76 
77 // TODO(hovinen): Revisit the trait bounds to see whether this can be made more
78 //  flexible. Namely, the following doesn't compile currently:
79 //
80 //      let matcher = contains(eq(&42));
81 //      let val = 42;
82 //      let _ = matcher.matches(&vec![&val]);
83 //
84 //  because val is dropped before matcher but the trait bound requires that
85 //  the argument to matches outlive the matcher. It works fine if one defines
86 //  val before matcher.
87 impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>, ContainerT: Debug> Matcher
88     for ContainsMatcher<ContainerT, InnerMatcherT>
89 where
90     for<'a> &'a ContainerT: IntoIterator<Item = &'a T>,
91 {
92     type ActualT = ContainerT;
93 
matches(&self, actual: &Self::ActualT) -> MatcherResult94     fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
95         if let Some(count) = &self.count {
96             count.matches(&self.count_matches(actual))
97         } else {
98             for v in actual.into_iter() {
99                 if self.inner.matches(v).into() {
100                     return MatcherResult::Match;
101                 }
102             }
103             MatcherResult::NoMatch
104         }
105     }
106 
explain_match(&self, actual: &Self::ActualT) -> Description107     fn explain_match(&self, actual: &Self::ActualT) -> Description {
108         let count = self.count_matches(actual);
109         match (count, &self.count) {
110             (_, Some(_)) => format!("which contains {} matching elements", count).into(),
111             (0, None) => "which does not contain a matching element".into(),
112             (_, None) => "which contains a matching element".into(),
113         }
114     }
115 
describe(&self, matcher_result: MatcherResult) -> Description116     fn describe(&self, matcher_result: MatcherResult) -> Description {
117         match (matcher_result, &self.count) {
118             (MatcherResult::Match, Some(count)) => format!(
119                 "contains n elements which {}\n  where n {}",
120                 self.inner.describe(MatcherResult::Match),
121                 count.describe(MatcherResult::Match)
122             )
123             .into(),
124             (MatcherResult::NoMatch, Some(count)) => format!(
125                 "doesn't contain n elements which {}\n  where n {}",
126                 self.inner.describe(MatcherResult::Match),
127                 count.describe(MatcherResult::Match)
128             )
129             .into(),
130             (MatcherResult::Match, None) => format!(
131                 "contains at least one element which {}",
132                 self.inner.describe(MatcherResult::Match)
133             )
134             .into(),
135             (MatcherResult::NoMatch, None) => {
136                 format!("contains no element which {}", self.inner.describe(MatcherResult::Match))
137                     .into()
138             }
139         }
140     }
141 }
142 
143 impl<ActualT, InnerMatcherT> ContainsMatcher<ActualT, InnerMatcherT> {
count_matches<T: Debug, ContainerT>(&self, actual: &ContainerT) -> usize where for<'b> &'b ContainerT: IntoIterator<Item = &'b T>, InnerMatcherT: Matcher<ActualT = T>,144     fn count_matches<T: Debug, ContainerT>(&self, actual: &ContainerT) -> usize
145     where
146         for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
147         InnerMatcherT: Matcher<ActualT = T>,
148     {
149         let mut count = 0;
150         for v in actual.into_iter() {
151             if self.inner.matches(v).into() {
152                 count += 1;
153             }
154         }
155         count
156     }
157 }
158 
159 #[cfg(test)]
160 mod tests {
161     use super::{contains, ContainsMatcher};
162     use crate::matcher::{Matcher, MatcherResult};
163     use crate::prelude::*;
164 
165     #[test]
contains_matches_singleton_slice_with_value() -> Result<()>166     fn contains_matches_singleton_slice_with_value() -> Result<()> {
167         let matcher = contains(eq(1));
168 
169         let result = matcher.matches(&vec![1]);
170 
171         verify_that!(result, eq(MatcherResult::Match))
172     }
173 
174     #[test]
contains_matches_singleton_vec_with_value() -> Result<()>175     fn contains_matches_singleton_vec_with_value() -> Result<()> {
176         let matcher = contains(eq(1));
177 
178         let result = matcher.matches(&vec![1]);
179 
180         verify_that!(result, eq(MatcherResult::Match))
181     }
182 
183     #[test]
contains_matches_two_element_slice_with_value() -> Result<()>184     fn contains_matches_two_element_slice_with_value() -> Result<()> {
185         let matcher = contains(eq(1));
186 
187         let result = matcher.matches(&[0, 1]);
188 
189         verify_that!(result, eq(MatcherResult::Match))
190     }
191 
192     #[test]
contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()>193     fn contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()> {
194         let matcher = contains(eq(1));
195 
196         let result = matcher.matches(&[0]);
197 
198         verify_that!(result, eq(MatcherResult::NoMatch))
199     }
200 
201     #[test]
contains_does_not_match_empty_slice() -> Result<()>202     fn contains_does_not_match_empty_slice() -> Result<()> {
203         let matcher = contains(eq::<i32, _>(1));
204 
205         let result = matcher.matches(&[]);
206 
207         verify_that!(result, eq(MatcherResult::NoMatch))
208     }
209 
210     #[test]
contains_matches_slice_with_repeated_value() -> Result<()>211     fn contains_matches_slice_with_repeated_value() -> Result<()> {
212         let matcher = contains(eq(1)).times(eq(2));
213 
214         let result = matcher.matches(&[1, 1]);
215 
216         verify_that!(result, eq(MatcherResult::Match))
217     }
218 
219     #[test]
contains_does_not_match_slice_with_too_few_of_value() -> Result<()>220     fn contains_does_not_match_slice_with_too_few_of_value() -> Result<()> {
221         let matcher = contains(eq(1)).times(eq(2));
222 
223         let result = matcher.matches(&[0, 1]);
224 
225         verify_that!(result, eq(MatcherResult::NoMatch))
226     }
227 
228     #[test]
contains_does_not_match_slice_with_too_many_of_value() -> Result<()>229     fn contains_does_not_match_slice_with_too_many_of_value() -> Result<()> {
230         let matcher = contains(eq(1)).times(eq(1));
231 
232         let result = matcher.matches(&[1, 1]);
233 
234         verify_that!(result, eq(MatcherResult::NoMatch))
235     }
236 
237     #[test]
contains_formats_without_multiplicity_by_default() -> Result<()>238     fn contains_formats_without_multiplicity_by_default() -> Result<()> {
239         let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1));
240 
241         verify_that!(
242             Matcher::describe(&matcher, MatcherResult::Match),
243             displays_as(eq("contains at least one element which is equal to 1"))
244         )
245     }
246 
247     #[test]
contains_formats_with_multiplicity_when_specified() -> Result<()>248     fn contains_formats_with_multiplicity_when_specified() -> Result<()> {
249         let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1)).times(eq(2));
250 
251         verify_that!(
252             Matcher::describe(&matcher, MatcherResult::Match),
253             displays_as(eq("contains n elements which is equal to 1\n  where n is equal to 2"))
254         )
255     }
256 
257     #[test]
contains_mismatch_shows_number_of_times_element_was_found() -> Result<()>258     fn contains_mismatch_shows_number_of_times_element_was_found() -> Result<()> {
259         verify_that!(
260             contains(eq(3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
261             displays_as(eq("which contains 2 matching elements"))
262         )
263     }
264 
265     #[test]
contains_mismatch_shows_when_matches() -> Result<()>266     fn contains_mismatch_shows_when_matches() -> Result<()> {
267         verify_that!(
268             contains(eq(3)).explain_match(&vec![1, 2, 3, 3]),
269             displays_as(eq("which contains a matching element"))
270         )
271     }
272 
273     #[test]
contains_mismatch_shows_when_no_matches() -> Result<()>274     fn contains_mismatch_shows_when_no_matches() -> Result<()> {
275         verify_that!(
276             contains(eq(3)).explain_match(&vec![1, 2]),
277             displays_as(eq("which does not contain a matching element"))
278         )
279     }
280 }
281