• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)]
88 macro_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)]
103 pub 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)]
new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self123         pub fn new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self {
124             Self { elements }
125         }
126     }
127 
128     impl<'a, T: Debug + Copy, ContainerT: Debug + Copy> Matcher<ContainerT> for ElementsAre<'a, T>
129     where
130         ContainerT: IntoIterator<Item = T>,
131     {
matches(&self, actual: ContainerT) -> MatcherResult132         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 
explain_match(&self, actual: ContainerT) -> Description146         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 
describe(&self, matcher_result: MatcherResult) -> Description170         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 }
186