googletest/matchers/unordered_elements_are_matcher.rs
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 matchers module.
17#![doc(hidden)]
18
19/// Matches a container whose elements in any order have a 1:1 correspondence
20/// with the provided element matchers.
21///
22/// ```
23/// # use googletest::prelude::*;
24/// # fn should_pass() -> Result<()> {
25/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&2), anything()])?; // Passes
26/// # Ok(())
27/// # }
28/// # fn should_fail_1() -> Result<()> {
29/// verify_that!(vec![1], unordered_elements_are![eq(&1), ge(&2)])?; // Fails: container has wrong size
30/// # Ok(())
31/// # }
32/// # fn should_fail_2() -> Result<()> {
33/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&4), eq(&2)])?; // Fails: second matcher not matched
34/// # Ok(())
35/// # }
36/// # fn should_fail_3() -> Result<()> {
37/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(&3), ge(&3), ge(&3)])?; // Fails: no 1:1 correspondence
38/// # Ok(())
39/// # }
40/// # should_pass().unwrap();
41/// # should_fail_1().unwrap_err();
42/// # should_fail_2().unwrap_err();
43/// # should_fail_3().unwrap_err();
44/// ```
45///
46/// The actual value must be a container such as a `&Vec`, an array, or a
47/// slice. More precisely, the actual value must implement [`IntoIterator`].
48///
49/// This can also be omitted in [`verify_that!`] macros and replaced with curly
50/// brackets.
51///
52/// ```
53/// # use googletest::prelude::*;
54/// verify_that!(vec![1, 2], {eq(&2), eq(&1)})
55/// # .unwrap();
56/// ```
57///
58/// Note: This behavior is only possible in [`verify_that!`] macros. In any
59/// other cases, it is still necessary to use the
60/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are] macro.
61///
62/// ```compile_fail
63/// # use googletest::prelude::*;
64/// verify_that!(vec![vec![1,2], vec![3]], {{eq(2), eq(1)}, {eq(3)}})
65/// # .unwrap();
66/// ```
67///
68/// Use this instead:
69/// ```
70/// # use googletest::prelude::*;
71/// verify_that!(vec![vec![1,2], vec![3]],
72/// {unordered_elements_are![eq(&2), eq(&1)], unordered_elements_are![eq(&3)]})
73/// # .unwrap();
74/// ```
75///
76/// If an inner matcher is `eq(...)`, it can be omitted:
77///
78/// ```
79/// # use googletest::prelude::*;
80///
81/// verify_that!(vec![1,2,3], unordered_elements_are![lt(&2), gt(&1), &3])
82/// # .unwrap();
83/// ```
84///
85/// The matcher proceeds in three stages:
86///
87/// 1. It first checks whether the actual value is of the right size to possibly
88/// be matched by each of the given matchers. If not, then it immediately
89/// fails explaining that the size is incorrect.
90///
91/// 2. It then checks whether each matcher matches at least one corresponding
92/// element in the actual container and each element in the actual container
93/// is matched by at least one matcher. If not, it fails with a message
94/// indicating which matcher respectively container elements had no
95/// counterparts.
96///
97/// 3. Finally, it checks whether the mapping of matchers to corresponding
98/// actual elements is a 1-1 correspondence and fails if that is not the
99/// case. The failure message then shows the best matching it could find,
100/// including which matchers did not have corresponding unique elements in
101/// the container and which container elements had no corresponding matchers.
102///
103/// [`IntoIterator`]: std::iter::IntoIterator
104/// [`Iterator`]: std::iter::Iterator
105/// [`Iterator::collect`]: std::iter::Iterator::collect
106/// [`Vec`]: std::vec::Vec
107#[macro_export]
108#[doc(hidden)]
109macro_rules! __unordered_elements_are {
110 ($(,)?) => {{
111 $crate::matchers::__internal_unstable_do_not_depend_on_these::
112 UnorderedElementsAreMatcher::new(
113 [],
114 $crate::matchers::__internal_unstable_do_not_depend_on_these::
115 Requirements::PerfectMatch)
116 }};
117
118 ($($matcher:expr),* $(,)?) => {{
119 $crate::matchers::__internal_unstable_do_not_depend_on_these::
120 UnorderedElementsAreMatcher::new(
121 [$(Box::new(
122 $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
123 $matcher
124 )
125 )),*],
126 $crate::matchers::__internal_unstable_do_not_depend_on_these::
127 Requirements::PerfectMatch)
128 }};
129}
130
131/// Matches a container containing elements matched by the given matchers.
132///
133/// To match, each given matcher must have a corresponding element in the
134/// container which it matches. There must be a mapping uniquely matching each
135/// matcher to a container element. The container can, however, contain
136/// additional elements that don't correspond to any matcher.
137///
138/// Put another way, `contains_each!` matches if there is a subset of the actual
139/// container which
140/// [`unordered_elements_are`][crate::matchers::unordered_elements_are] would
141/// match.
142///
143/// ```
144/// # use googletest::prelude::*;
145/// # fn should_pass() -> Result<()> {
146/// verify_that!(vec![3, 2, 1], contains_each![eq(&2), ge(&3)])?; // Passes
147/// verify_that!(vec![3, 2, 1], contains_each![ge(&2), ge(&2)])?; // Passes
148/// # Ok(())
149/// # }
150/// # fn should_fail_1() -> Result<()> {
151/// verify_that!(vec![1], contains_each![eq(&1), ge(&2)])?; // Fails: container too small
152/// # Ok(())
153/// # }
154/// # fn should_fail_2() -> Result<()> {
155/// verify_that!(vec![3, 2, 1], contains_each![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
156/// # Ok(())
157/// # }
158/// # fn should_fail_3() -> Result<()> {
159/// verify_that!(vec![3, 2, 1], contains_each![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
160/// # Ok(())
161/// # }
162/// # should_pass().unwrap();
163/// # should_fail_1().unwrap_err();
164/// # should_fail_2().unwrap_err();
165/// # should_fail_3().unwrap_err();
166/// ```
167///
168/// The actual value must be a container such as a `&Vec`, an array, or a
169/// slice. More precisely, the actual value must implement [`IntoIterator`].
170///
171/// If an inner matcher is `eq(...)`, it can be omitted:
172///
173/// ```
174/// # use googletest::prelude::*;
175///
176/// verify_that!(vec![1,2,3], contains_each![lt(&2), &3])
177/// # .unwrap();
178/// ```
179///
180/// The matcher proceeds in three stages:
181///
182/// 1. It first checks whether the actual value is large enough to possibly be
183/// matched by each of the given matchers. If not, then it immediately fails
184/// explaining that the size is too small.
185///
186/// 2. It then checks whether each matcher matches at least one corresponding
187/// element in the actual container and fails if that is not the case. The
188/// failure message indicates which matcher had no corresponding element.
189///
190/// 3. Finally, it checks whether the mapping of matchers to corresponding
191/// actual elements is 1-1 and fails if that is not the case. The failure
192/// message then shows the best matching it could find, including which
193/// matchers did not have corresponding unique elements in the container.
194///
195/// [`IntoIterator`]: std::iter::IntoIterator
196/// [`Iterator`]: std::iter::Iterator
197/// [`Iterator::collect`]: std::iter::Iterator::collect
198/// [`Vec`]: std::vec::Vec
199#[macro_export]
200#[doc(hidden)]
201macro_rules! __contains_each {
202 ($(,)?) => {{
203 $crate::matchers::__internal_unstable_do_not_depend_on_these::
204 UnorderedElementsAreMatcher::new(
205 [],
206 $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
207 }};
208
209 ($($matcher:expr),* $(,)?) => {{
210 $crate::matchers::__internal_unstable_do_not_depend_on_these::
211 UnorderedElementsAreMatcher::new(
212 [$(Box::new(
213 $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
214 $matcher
215 )
216 )),*],
217 $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
218 }}
219}
220
221/// Matches a container all of whose elements are matched by the given matchers.
222///
223/// To match, each element in the container must have a corresponding matcher
224/// which matches it. There must be a 1-1 mapping from container elements to
225/// matchers, so that no matcher has more than one corresponding element.
226///
227/// There may, however, be matchers not corresponding to any elements in the
228/// container.
229///
230/// Put another way, `is_contained_in!` matches if there is a subset of the
231/// matchers which would match with
232/// [`unordered_elements_are`][crate::matchers::unordered_elements_are].
233///
234/// ```
235/// # use googletest::prelude::*;
236/// # fn should_pass() -> Result<()> {
237/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&2)])?; // Passes
238/// verify_that!(vec![2, 1], is_contained_in![ge(&1), ge(&1)])?; // Passes
239/// # Ok(())
240/// # }
241/// # fn should_fail_1() -> Result<()> {
242/// verify_that!(vec![1, 2, 3], is_contained_in![eq(&1), ge(&2)])?; // Fails: container too large
243/// # Ok(())
244/// # }
245/// # fn should_fail_2() -> Result<()> {
246/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
247/// # Ok(())
248/// # }
249/// # fn should_fail_3() -> Result<()> {
250/// verify_that!(vec![3, 1], is_contained_in![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
251/// # Ok(())
252/// # }
253/// # should_pass().unwrap();
254/// # should_fail_1().unwrap_err();
255/// # should_fail_2().unwrap_err();
256/// # should_fail_3().unwrap_err();
257/// ```
258///
259/// The actual value must be a container such as a `&Vec`, an array, or a slice.
260/// More precisely, the actual value must implement [`IntoIterator`].
261///
262/// If an inner matcher is `eq(...)`, it can be omitted:
263///
264/// ```
265/// # use googletest::prelude::*;
266///
267/// verify_that!(vec![1,2,3], is_contained_in![lt(&2), &3, &4, gt(&0)])
268/// # .unwrap();
269/// ```
270///
271/// The matcher proceeds in three stages:
272///
273/// 1. It first checks whether the actual value is too large to possibly be
274/// matched by each of the given matchers. If so, it immediately fails
275/// explaining that the size is too large.
276///
277/// 2. It then checks whether each actual container element is matched by at
278/// least one matcher and fails if that is not the case. The failure message
279/// indicates which element had no corresponding matcher.
280///
281/// 3. Finally, it checks whether the mapping of elements to corresponding
282/// matchers is 1-1 and fails if that is not the case. The failure message
283/// then shows the best matching it could find, including which container
284/// elements did not have corresponding matchers.
285///
286/// [`IntoIterator`]: std::iter::IntoIterator
287/// [`Iterator`]: std::iter::Iterator
288/// [`Iterator::collect`]: std::iter::Iterator::collect
289/// [`Vec`]: std::vec::Vec
290#[macro_export]
291#[doc(hidden)]
292macro_rules! __is_contained_in {
293 ($(,)?) => {{
294 $crate::matchers::__internal_unstable_do_not_depend_on_these::
295 UnorderedElementsAreMatcher::new(
296 [], $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
297 }};
298
299 ($($matcher:expr),* $(,)?) => {{
300 $crate::matchers::__internal_unstable_do_not_depend_on_these::
301 UnorderedElementsAreMatcher::new(
302 [$(Box::new(
303 $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
304 $matcher
305 )
306 )),*],
307 $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
308 }}
309}
310
311/// Module for use only by the macros in this module.
312///
313/// **For internal use only. API stablility is not guaranteed!**
314#[doc(hidden)]
315pub mod internal {
316 use crate::description::Description;
317 use crate::matcher::{Matcher, MatcherBase, MatcherResult};
318 use crate::matcher_support::match_matrix::internal::{MatchMatrix, Requirements};
319 use std::fmt::Debug;
320
321 /// This struct is meant to be used only through the
322 /// `unordered_elements_are![...]` macro.
323 ///
324 /// **For internal use only. API stablility is not guaranteed!**
325 #[doc(hidden)]
326 #[derive(MatcherBase)]
327 pub struct UnorderedElementsAreMatcher<'a, T: Debug + Copy, const N: usize> {
328 elements: [Box<dyn Matcher<T> + 'a>; N],
329 requirements: Requirements,
330 }
331
332 impl<'a, T: Debug + Copy, const N: usize> UnorderedElementsAreMatcher<'a, T, N> {
333 pub fn new(elements: [Box<dyn Matcher<T> + 'a>; N], requirements: Requirements) -> Self {
334 Self { elements, requirements }
335 }
336 }
337
338 // This matcher performs the checks in three different steps in both `matches`
339 // and `explain_match`. This is useful for performance but also to produce
340 // an actionable error message.
341 // 1. `UnorderedElementsAreMatcher` verifies that both collections have the same
342 // size
343 // 2. `UnorderedElementsAreMatcher` verifies that each actual element matches at
344 // least one expected element and vice versa.
345 // 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists
346 // using Ford-Fulkerson.
347 impl<T: Debug + Copy, ContainerT: Debug + Copy, const N: usize> Matcher<ContainerT>
348 for UnorderedElementsAreMatcher<'_, T, N>
349 where
350 ContainerT: IntoIterator<Item = T>,
351 {
352 fn matches(&self, actual: ContainerT) -> MatcherResult {
353 let match_matrix = MatchMatrix::generate(actual, &self.elements);
354 match_matrix.is_match_for(self.requirements).into()
355 }
356
357 fn explain_match(&self, actual: ContainerT) -> Description {
358 if let Some(size_mismatch_explanation) =
359 self.requirements.explain_size_mismatch(actual, N)
360 {
361 return size_mismatch_explanation;
362 }
363
364 let match_matrix = MatchMatrix::generate(actual, &self.elements);
365 if let Some(unmatchable_explanation) =
366 match_matrix.explain_unmatchable(self.requirements)
367 {
368 return unmatchable_explanation;
369 }
370
371 let best_match = match_matrix.find_best_match();
372 best_match
373 .get_explanation(actual, &self.elements, self.requirements)
374 .unwrap_or("whose elements all match".into())
375 }
376
377 fn describe(&self, matcher_result: MatcherResult) -> Description {
378 format!(
379 "{} elements matching in any order:\n{}",
380 if matcher_result.into() { "contains" } else { "doesn't contain" },
381 self.elements
382 .iter()
383 .map(|matcher| matcher.describe(MatcherResult::Match))
384 .collect::<Description>()
385 .enumerate()
386 .indent()
387 )
388 .into()
389 }
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use crate as googletest;
396 use crate::matcher::MatcherResult;
397 use crate::prelude::*;
398 use indoc::indoc;
399 use std::collections::HashMap;
400
401 #[test]
402 fn has_correct_description_for_map() -> googletest::Result<()> {
403 // UnorderedElementsAreMatcher maintains references to the matchers, so the
404 // constituent matchers must live longer. Inside a verify_that! macro, the
405 // compiler takes care of that, but when the matcher is created separately,
406 // we must create the constitute matchers separately so that they
407 // aren't dropped too early.
408 let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
409 let matcher = unordered_elements_are![
410 (matchers.0 .0, matchers.0 .1),
411 (matchers.1 .0, matchers.1 .1),
412 (matchers.2 .0, matchers.2 .1)
413 ];
414 verify_that!(
415 Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
416 displays_as(eq(indoc!(
417 "
418 contains elements matching in any order:
419 0. is a tuple whose values respectively match:
420 is equal to 2
421 is equal to \"Two\"
422 1. is a tuple whose values respectively match:
423 is equal to 1
424 is equal to \"One\"
425 2. is a tuple whose values respectively match:
426 is equal to 3
427 is equal to \"Three\""
428 )))
429 )
430 }
431
432 #[test]
433 fn unordered_elements_are_description_no_full_match_with_map() -> googletest::Result<()> {
434 // UnorderedElementsAreMatcher maintains references to the matchers, so the
435 // constituent matchers must live longer. Inside a verify_that! macro, the
436 // compiler takes care of that, but when the matcher is created separately,
437 // we must create the constitute matchers separately so that they
438 // aren't dropped too early.
439 let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
440 let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
441 let matcher = unordered_elements_are![
442 (matchers.0 .0, matchers.0 .1),
443 (matchers.1 .0, matchers.1 .1),
444 (matchers.2 .0, matchers.2 .1),
445 ];
446 verify_that!(
447 matcher.explain_match(&value),
448 all![
449 displays_as(contains_regex(
450 "Actual element \\(2, 2\\) at index [0-2] matched expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index [0-2]."
451 )),
452 displays_as(contains_regex(
453 "Actual element \\(\n [0-1],\n [0-1],\n \\) at index [0-2] did not match any remaining expected element."
454 )),
455 displays_as(contains_substring(
456 "Expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index 2 did not match any remaining actual element."
457 ))
458 ]
459 )
460 }
461}