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