Skip to main content

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}