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