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