googletest/matchers/
contains_matcher.rs1use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherBase, MatcherResult},
18};
19use std::fmt::Debug;
20
21pub fn contains<InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<InnerMatcherT> {
48 ContainsMatcher { inner, count: None }
49}
50
51#[derive(MatcherBase)]
54pub struct ContainsMatcher<InnerMatcherT> {
55 inner: InnerMatcherT,
56 count: Option<Box<dyn Matcher<usize>>>,
57}
58
59impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
60 pub fn times(mut self, count: impl Matcher<usize> + 'static) -> Self {
73 self.count = Some(Box::new(count));
74 self
75 }
76}
77
78impl<T: Debug + Copy, InnerMatcherT: Matcher<T>, ContainerT: Debug + Copy> Matcher<ContainerT>
89 for ContainsMatcher<InnerMatcherT>
90where
91 ContainerT: IntoIterator<Item = T>,
92{
93 fn matches(&self, actual: ContainerT) -> MatcherResult {
94 if let Some(count) = &self.count {
95 count.matches(self.count_matches(actual))
96 } else {
97 for v in actual.into_iter() {
98 if self.inner.matches(v).into() {
99 return MatcherResult::Match;
100 }
101 }
102 MatcherResult::NoMatch
103 }
104 }
105
106 fn explain_match(&self, actual: ContainerT) -> Description {
107 let count = self.count_matches(actual);
108 match (count, &self.count) {
109 (_, Some(_)) => format!("which contains {count} matching elements").into(),
110 (0, None) => "which does not contain a matching element".into(),
111 (_, None) => "which contains a matching element".into(),
112 }
113 }
114
115 fn describe(&self, matcher_result: MatcherResult) -> Description {
116 match (matcher_result, &self.count) {
117 (MatcherResult::Match, Some(count)) => format!(
118 "contains n elements which {}\n where n {}",
119 self.inner.describe(MatcherResult::Match),
120 count.describe(MatcherResult::Match)
121 )
122 .into(),
123 (MatcherResult::NoMatch, Some(count)) => format!(
124 "doesn't contain n elements which {}\n where n {}",
125 self.inner.describe(MatcherResult::Match),
126 count.describe(MatcherResult::Match)
127 )
128 .into(),
129 (MatcherResult::Match, None) => format!(
130 "contains at least one element which {}",
131 self.inner.describe(MatcherResult::Match)
132 )
133 .into(),
134 (MatcherResult::NoMatch, None) => {
135 format!("contains no element which {}", self.inner.describe(MatcherResult::Match))
136 .into()
137 }
138 }
139 }
140}
141
142impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
143 fn count_matches<T: Debug + Copy, ContainerT>(&self, actual: ContainerT) -> usize
144 where
145 ContainerT: IntoIterator<Item = T>,
146 InnerMatcherT: Matcher<T>,
147 {
148 let mut count = 0;
149 for v in actual.into_iter() {
150 if self.inner.matches(v).into() {
151 count += 1;
152 }
153 }
154 count
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use crate::matcher::MatcherResult;
161 use crate::prelude::*;
162 use crate::Result;
163
164 #[test]
165 fn contains_matches_singleton_slice_with_value() -> Result<()> {
166 let matcher = contains(eq(&1));
167
168 let result = matcher.matches(&vec![1]);
169
170 verify_that!(result, eq(MatcherResult::Match))
171 }
172
173 #[test]
174 fn contains_matches_singleton_vec_with_value() -> Result<()> {
175 let matcher = contains(eq(&1));
176
177 let result = matcher.matches(&vec![1]);
178
179 verify_that!(result, eq(MatcherResult::Match))
180 }
181
182 #[test]
183 fn contains_matches_two_element_slice_with_value() -> Result<()> {
184 let matcher = contains(eq(&1));
185
186 let result = matcher.matches(&[0, 1]);
187
188 verify_that!(result, eq(MatcherResult::Match))
189 }
190
191 #[test]
192 fn contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()> {
193 let matcher = contains(eq(&1));
194
195 let result = matcher.matches(&[0]);
196
197 verify_that!(result, eq(MatcherResult::NoMatch))
198 }
199
200 #[test]
201 fn contains_does_not_match_empty_slice() -> Result<()> {
202 let matcher = contains(eq(&1));
203
204 let result = matcher.matches(&[1; 0]);
205
206 verify_that!(result, eq(MatcherResult::NoMatch))
207 }
208
209 #[test]
210 fn contains_matches_slice_with_repeated_value() -> Result<()> {
211 let matcher = contains(eq(&1)).times(eq(2));
212
213 let result = matcher.matches(&[1, 1]);
214
215 verify_that!(result, eq(MatcherResult::Match))
216 }
217
218 #[test]
219 fn contains_does_not_match_slice_with_too_few_of_value() -> Result<()> {
220 let matcher = contains(eq(&1)).times(eq(2));
221
222 let result = matcher.matches(&[0, 1]);
223
224 verify_that!(result, eq(MatcherResult::NoMatch))
225 }
226
227 #[test]
228 fn contains_does_not_match_slice_with_too_many_of_value() -> Result<()> {
229 let matcher = contains(eq(&1)).times(eq(1));
230
231 let result = matcher.matches(&[1, 1]);
232
233 verify_that!(result, eq(MatcherResult::NoMatch))
234 }
235
236 #[test]
237 fn contains_formats_without_multiplicity_by_default() -> Result<()> {
238 let matcher = contains(eq(&1));
239
240 verify_that!(
241 Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
242 displays_as(eq("contains at least one element which is equal to 1"))
243 )
244 }
245
246 #[test]
247 fn contains_formats_with_multiplicity_when_specified() -> Result<()> {
248 let matcher = contains(eq(&1)).times(eq(2));
249
250 verify_that!(
251 Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
252 displays_as(eq("contains n elements which is equal to 1\n where n is equal to 2"))
253 )
254 }
255
256 #[test]
257 fn contains_mismatch_shows_number_of_times_element_was_found() -> Result<()> {
258 verify_that!(
259 contains(eq(&3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
260 displays_as(eq("which contains 2 matching elements"))
261 )
262 }
263
264 #[test]
265 fn contains_mismatch_shows_when_matches() -> Result<()> {
266 verify_that!(
267 contains(eq(&3)).explain_match(&vec![1, 2, 3, 3]),
268 displays_as(eq("which contains a matching element"))
269 )
270 }
271
272 #[test]
273 fn contains_mismatch_shows_when_no_matches() -> Result<()> {
274 verify_that!(
275 contains(eq(&3)).explain_match(&vec![1, 2]),
276 displays_as(eq("which does not contain a matching element"))
277 )
278 }
279}