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 ($(
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 ($(
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 ($(
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 /// A trait for a tuple of matchers.
322 pub trait MatcherTuple<T: Debug + Copy> {
323 /// Turn the tuple into a vector of matchers.
324 fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>>;
325 }
326
327 // Base case, no matchers.
328 impl<T: Debug + Copy> MatcherTuple<T> for () {
329 fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
330 vec![]
331 }
332 }
333
334 // A macro for generating a MatcherTuple implementation for a given number
335 // of matchers.
336 macro_rules! matcher_tuple_n {
337 // Given list of (field_number, matcher) pairs, generate a MatcherTuple
338 // implementation.
339 ($([$field_number:tt, $matcher_type:ident]),*) => {
340 impl<T: Debug + Copy, $($matcher_type: Matcher<T>),*> MatcherTuple<T>
341 for ($($matcher_type,)*) {
342 // Our implementation of to_matchers just converts our tuple into a vector of
343 // matchers by expanding self.0, self.1, etc.
344 fn to_matchers(&self) -> Vec<Box<dyn Matcher<T> + '_>> {
345 vec![$(Box::new(&self.$field_number)),*]
346 }
347 }
348 };
349 }
350
351 matcher_tuple_n!([0, M0]);
352 matcher_tuple_n!([0, M0], [1, M1]);
353 matcher_tuple_n!([0, M0], [1, M1], [2, M2]);
354 matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3]);
355 matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4]);
356 matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5]);
357 matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6]);
358 matcher_tuple_n!([0, M0], [1, M1], [2, M2], [3, M3], [4, M4], [5, M5], [6, M6], [7, M7]);
359 matcher_tuple_n!(
360 [0, M0],
361 [1, M1],
362 [2, M2],
363 [3, M3],
364 [4, M4],
365 [5, M5],
366 [6, M6],
367 [7, M7],
368 [8, M8]
369 );
370 matcher_tuple_n!(
371 [0, M0],
372 [1, M1],
373 [2, M2],
374 [3, M3],
375 [4, M4],
376 [5, M5],
377 [6, M6],
378 [7, M7],
379 [8, M8],
380 [9, M9]
381 );
382 matcher_tuple_n!(
383 [0, M0],
384 [1, M1],
385 [2, M2],
386 [3, M3],
387 [4, M4],
388 [5, M5],
389 [6, M6],
390 [7, M7],
391 [8, M8],
392 [9, M9],
393 [10, M10]
394 );
395 matcher_tuple_n!(
396 [0, M0],
397 [1, M1],
398 [2, M2],
399 [3, M3],
400 [4, M4],
401 [5, M5],
402 [6, M6],
403 [7, M7],
404 [8, M8],
405 [9, M9],
406 [10, M10],
407 [11, M11]
408 );
409
410 /// This struct is meant to be used only through the
411 /// `unordered_elements_are![...]` macro.
412 ///
413 /// **For internal use only. API stablility is not guaranteed!**
414 #[doc(hidden)]
415 #[derive(MatcherBase)]
416 pub struct UnorderedElementsAreMatcher<M> {
417 matchers: M,
418 requirements: Requirements,
419 }
420
421 impl<M> UnorderedElementsAreMatcher<M> {
422 pub fn new(matchers: M, requirements: Requirements) -> Self {
423 Self { matchers, requirements }
424 }
425 }
426
427 // This matcher performs the checks in three different steps in both `matches`
428 // and `explain_match`. This is useful for performance but also to produce
429 // an actionable error message.
430 // 1. `UnorderedElementsAreMatcher` verifies that both collections have the same
431 // size
432 // 2. `UnorderedElementsAreMatcher` verifies that each actual element matches at
433 // least one expected element and vice versa.
434 // 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists
435 // using Ford-Fulkerson.
436 impl<T: Debug + Copy, ContainerT: Debug + Copy, M> Matcher<ContainerT>
437 for UnorderedElementsAreMatcher<M>
438 where
439 ContainerT: IntoIterator<Item = T>,
440 M: MatcherTuple<T>,
441 {
442 fn matches(&self, actual: ContainerT) -> MatcherResult {
443 let matchers = self.matchers.to_matchers();
444 let match_matrix = MatchMatrix::generate(actual, &matchers);
445 match_matrix.is_match_for(self.requirements).into()
446 }
447
448 fn explain_match(&self, actual: ContainerT) -> Description {
449 let matchers = self.matchers.to_matchers();
450 if let Some(size_mismatch_explanation) =
451 self.requirements.explain_size_mismatch(actual, matchers.len())
452 {
453 return size_mismatch_explanation;
454 }
455
456 let match_matrix = MatchMatrix::generate(actual, &matchers);
457 if let Some(unmatchable_explanation) =
458 match_matrix.explain_unmatchable(self.requirements)
459 {
460 return unmatchable_explanation;
461 }
462
463 let best_match = match_matrix.find_best_match();
464 best_match
465 .get_explanation(actual, &matchers, self.requirements)
466 .unwrap_or("whose elements all match".into())
467 }
468
469 fn describe(&self, matcher_result: MatcherResult) -> Description {
470 let matchers = self.matchers.to_matchers();
471 format!(
472 "{} elements matching in any order:\n{}",
473 if matcher_result.into() { "contains" } else { "doesn't contain" },
474 matchers
475 .iter()
476 .map(|matcher| matcher.describe(MatcherResult::Match))
477 .collect::<Description>()
478 .enumerate()
479 .indent()
480 )
481 .into()
482 }
483 }
484}
485
486#[cfg(test)]
487mod tests {
488 use crate as googletest;
489 use crate::description::Description;
490 use crate::matcher::MatcherResult;
491 use crate::prelude::*;
492 use indoc::indoc;
493 use std::collections::{HashMap, HashSet};
494
495 #[test]
496 fn has_correct_description_for_map() -> googletest::Result<()> {
497 // UnorderedElementsAreMatcher maintains references to the matchers, so the
498 // constituent matchers must live longer. Inside a verify_that! macro, the
499 // compiler takes care of that, but when the matcher is created separately,
500 // we must create the constitute matchers separately so that they
501 // aren't dropped too early.
502 let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
503 let matcher = unordered_elements_are![
504 (matchers.0 .0, matchers.0 .1),
505 (matchers.1 .0, matchers.1 .1),
506 (matchers.2 .0, matchers.2 .1)
507 ];
508 verify_that!(
509 Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
510 displays_as(eq(indoc!(
511 "
512 contains elements matching in any order:
513 0. is a tuple whose values respectively match:
514 is equal to 2
515 is equal to \"Two\"
516 1. is a tuple whose values respectively match:
517 is equal to 1
518 is equal to \"One\"
519 2. is a tuple whose values respectively match:
520 is equal to 3
521 is equal to \"Three\""
522 )))
523 )
524 }
525
526 #[test]
527 fn unordered_elements_are_description_no_full_match_with_map() -> googletest::Result<()> {
528 // UnorderedElementsAreMatcher maintains references to the matchers, so the
529 // constituent matchers must live longer. Inside a verify_that! macro, the
530 // compiler takes care of that, but when the matcher is created separately,
531 // we must create the constitute matchers separately so that they
532 // aren't dropped too early.
533 let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
534 let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
535 let matcher = unordered_elements_are![
536 (matchers.0 .0, matchers.0 .1),
537 (matchers.1 .0, matchers.1 .1),
538 (matchers.2 .0, matchers.2 .1),
539 ];
540 verify_that!(
541 matcher.explain_match(&value),
542 all![
543 displays_as(contains_regex(
544 "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]."
545 )),
546 displays_as(contains_regex(
547 "Actual element \\(\n [0-1],\n [0-1],\n \\) at index [0-2] did not match any remaining expected element."
548 )),
549 displays_as(contains_substring(
550 "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."
551 ))
552 ]
553 )
554 }
555
556 // This test verifies that unordered_elements_are can be used as the inner
557 // matcher of another matcher, where the inner matcher is given a reference
558 // to a local variable. If unordered_elements_are stores its matchers in a
559 // Vec with the same type and lifetimes then this fails to compile.
560 #[test]
561 fn works_with_inner_matcher_that_modifies_the_input() -> googletest::Result<()> {
562 // A custom matcher that applies an operation to the input hashmap (filtering
563 // out any keys present in `to_filter`) and then applies the inner
564 // matcher to the modified input.
565 fn matches_filtered<'a, M>(
566 to_filter: &'a HashSet<i32>,
567 inner: M,
568 ) -> impl Matcher<&'a HashSet<i32>>
569 where
570 M: for<'b> Matcher<&'b HashSet<i32>>,
571 {
572 #[derive(MatcherBase)]
573 struct FilteredMatcher<'a, M> {
574 to_filter: &'a HashSet<i32>,
575 inner: M,
576 }
577
578 impl<'a, M> Matcher<&'a HashSet<i32>> for FilteredMatcher<'a, M>
579 where
580 M: for<'b> Matcher<&'b HashSet<i32>>,
581 {
582 fn matches(&self, actual: &'a HashSet<i32>) -> MatcherResult {
583 let filtered: HashSet<i32> =
584 actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
585 self.inner.matches(&filtered)
586 }
587
588 fn describe(&self, matcher_result: MatcherResult) -> Description {
589 self.inner.describe(matcher_result)
590 }
591
592 fn explain_match(&self, actual: &'a HashSet<i32>) -> Description {
593 let filtered: HashSet<i32> =
594 actual.iter().filter(|k| !self.to_filter.contains(k)).cloned().collect();
595 self.inner.explain_match(&filtered)
596 }
597 }
598
599 FilteredMatcher { to_filter, inner }
600 }
601
602 let before: HashSet<i32> = HashSet::from([1]);
603 let after: HashSet<i32> = HashSet::from([1, 2]);
604
605 verify_that!(&after, matches_filtered(&before, unordered_elements_are![eq(&2)]))?;
606
607 Ok(())
608 }
609}