Skip to main content

googletest/matchers/
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's elements to each matcher in order.
20///
21/// This macro produces a matcher against a container. It takes as arguments a
22/// sequence of matchers each of which should respectively match the
23/// corresponding element of the actual value.
24///
25/// ```
26/// # use googletest::prelude::*;
27/// verify_that!(vec![1, 2, 3], elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
28/// #    .unwrap();
29/// ```
30///
31/// The actual value must be a container such as a `&Vec`, an array, or a slice.
32/// More precisely, the actual value must implement [`IntoIterator`].
33///
34/// ```
35/// # use googletest::prelude::*;
36/// let vector = vec![1, 2, 3];
37/// let slice = vector.as_slice();
38/// verify_that!(slice, elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
39/// #    .unwrap();
40/// ```
41///
42/// This can also be omitted in [`verify_that!`] macros and replaced with square
43/// brackets.
44///
45/// ```
46/// # use googletest::prelude::*;
47///  verify_that!(vec![1, 2], [eq(&1), eq(&2)])
48/// #     .unwrap();
49/// ```
50///
51/// Note: This behavior is only possible in [`verify_that!`] macros. In any
52/// other cases, it is still necessary to use the
53/// [`elements_are!`][crate::matchers::elements_are] macro.
54///
55/// ```compile_fail
56/// # use googletest::prelude::*;
57/// verify_that!(vec![vec![1,2], vec![3]], [[eq(&1), eq(&2)], [eq(&3)]])
58/// # .unwrap();
59/// ```
60///
61/// Use this instead:
62/// ```
63/// # use googletest::prelude::*;
64/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(&1), eq(&2)], elements_are![eq(&3)]])
65/// # .unwrap();
66/// ```
67///
68///  If an inner matcher is `eq(...)`, it can be omitted:
69///
70/// ```
71/// # use googletest::prelude::*;
72///
73/// verify_that!(vec![1,2,3], elements_are![&1, lt(&1000), gt(&1)])
74/// #     .unwrap();
75/// ```
76///
77/// Do not use this with unordered containers, since that will lead to flaky
78/// tests. Use
79/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are]
80/// instead.
81///
82/// [`IntoIterator`]: std::iter::IntoIterator
83/// [`Iterator`]: std::iter::Iterator
84/// [`Iterator::collect`]: std::iter::Iterator::collect
85/// [`Vec`]: std::vec::Vec
86#[macro_export]
87#[doc(hidden)]
88macro_rules! __elements_are {
89    ($($matcher:expr),* $(,)?) => {{
90        $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre::new(
91            vec![$(Box::new(
92                $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
93                    $matcher
94                )
95            )),*])
96    }}
97}
98
99/// Module for use only by the procedural macros in this module.
100///
101/// **For internal use only. API stablility is not guaranteed!**
102#[doc(hidden)]
103pub mod internal {
104    use crate::description::Description;
105    use crate::matcher::{Matcher, MatcherBase, MatcherResult};
106    use crate::matcher_support::zipped_iterator::zip;
107    use std::fmt::Debug;
108
109    /// This struct is meant to be used only by the macro `elements_are!`.
110    ///
111    /// **For internal use only. API stablility is not guaranteed!**
112    #[doc(hidden)]
113    #[derive(MatcherBase)]
114    pub struct ElementsAre<'a, T: Debug + Copy> {
115        elements: Vec<Box<dyn Matcher<T> + 'a>>,
116    }
117
118    impl<'a, T: Debug + Copy> ElementsAre<'a, T> {
119        /// Factory only intended for use in the macro `elements_are!`.
120        ///
121        /// **For internal use only. API stablility is not guaranteed!**
122        #[doc(hidden)]
123        pub fn new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self {
124            Self { elements }
125        }
126    }
127
128    impl<T: Debug + Copy, ContainerT: Debug + Copy> Matcher<ContainerT> for ElementsAre<'_, T>
129    where
130        ContainerT: IntoIterator<Item = T>,
131    {
132        fn matches(&self, actual: ContainerT) -> MatcherResult {
133            let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter());
134            for (a, e) in zipped_iterator.by_ref() {
135                if e.matches(a).is_no_match() {
136                    return MatcherResult::NoMatch;
137                }
138            }
139            if !zipped_iterator.has_size_mismatch() {
140                MatcherResult::Match
141            } else {
142                MatcherResult::NoMatch
143            }
144        }
145
146        fn explain_match(&self, actual: ContainerT) -> Description {
147            let actual_iterator = actual.into_iter();
148            let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
149            let mut mismatches = Vec::new();
150            for (idx, (a, e)) in zipped_iterator.by_ref().enumerate() {
151                if e.matches(a).is_no_match() {
152                    mismatches.push(format!("element #{idx} is {a:?}, {}", e.explain_match(a)));
153                }
154            }
155            if mismatches.is_empty() {
156                if !zipped_iterator.has_size_mismatch() {
157                    "whose elements all match".into()
158                } else {
159                    format!("whose size is {}", zipped_iterator.left_size()).into()
160                }
161            } else if mismatches.len() == 1 {
162                let mismatches = mismatches.into_iter().collect::<Description>();
163                format!("where {mismatches}").into()
164            } else {
165                let mismatches = mismatches.into_iter().collect::<Description>();
166                format!("where:\n{}", mismatches.bullet_list().indent()).into()
167            }
168        }
169
170        fn describe(&self, matcher_result: MatcherResult) -> Description {
171            format!(
172                "{} elements:\n{}",
173                if matcher_result.into() { "has" } else { "doesn't have" },
174                &self
175                    .elements
176                    .iter()
177                    .map(|matcher| matcher.describe(MatcherResult::Match))
178                    .collect::<Description>()
179                    .enumerate()
180                    .indent()
181            )
182            .into()
183        }
184    }
185}