• 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 use crate::{
16     description::Description,
17     matcher::{Matcher, MatcherBase, MatcherResult},
18 };
19 use std::fmt::Debug;
20 
21 /// Matches a container containing all of the items in the given container
22 /// `subset`.
23 ///
24 /// The element type `ElementT` must implement `PartialEq` to allow element
25 /// comparison.
26 ///
27 /// `ActualT` and `ExpectedT` can each be any container which implements
28 /// `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
29 /// common container like `&Vec`, arrays or slices. They need not be the same
30 /// container type.
31 ///
32 /// ```
33 /// # use googletest::prelude::*;
34 /// # use std::collections::HashSet;
35 /// # fn should_pass_1() -> Result<()> {
36 /// let value = vec![1, 2, 3];
37 /// verify_that!(value, superset_of([&1, &2]))?;  // Passes
38 /// let array_value = [1, 2, 3];
39 /// verify_that!(array_value, superset_of([1, 2]))?;  // Passes
40 /// #     Ok(())
41 /// # }
42 /// # fn should_fail() -> Result<()> {
43 /// # let value = vec![1, 2, 3];
44 /// verify_that!(value, superset_of([&1, &2, &4]))?;  // Fails: 4 is not in the subset
45 /// #     Ok(())
46 /// # }
47 /// # should_pass_1().unwrap();
48 /// # should_fail().unwrap_err();
49 ///
50 /// # fn should_pass_2() -> Result<()> {
51 /// let value: HashSet<i32> = [1, 2, 3].into();
52 /// verify_that!(value, superset_of([&1, &2, &3]))?;  // Passes
53 /// #     Ok(())
54 /// # }
55 /// # should_pass_2().unwrap();
56 /// ```
57 ///
58 /// Item multiplicity in both the actual and expected containers is ignored:
59 ///
60 /// ```
61 /// # use googletest::prelude::*;
62 /// # fn should_pass() -> Result<()> {
63 /// let value: Vec<i32> = vec![0, 0, 1];
64 /// verify_that!(value, superset_of([&0, &1]))?;  // Passes
65 /// verify_that!(value, superset_of([&0, &1, &1]))?;  // Passes
66 /// #     Ok(())
67 /// # }
68 /// # should_pass().unwrap();
69 /// ```
70 ///
71 /// A note on performance: This matcher uses a naive algorithm with a worst-case
72 /// runtime proportional to the *product* of the sizes of the actual and
73 /// expected containers as well as the time to check equality of each pair of
74 /// items. It should not be used on especially large containers.
superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT>75 pub fn superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT> {
76     SupersetOfMatcher { subset }
77 }
78 
79 #[derive(MatcherBase)]
80 pub struct SupersetOfMatcher<ExpectedT> {
81     subset: ExpectedT,
82 }
83 
84 impl<ElementT: Debug + Copy + PartialEq, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
85     for SupersetOfMatcher<ExpectedT>
86 where
87     ActualT: IntoIterator<Item = ElementT>,
88     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
89 {
matches(&self, actual: ActualT) -> MatcherResult90     fn matches(&self, actual: ActualT) -> MatcherResult {
91         for expected_item in &self.subset {
92             if actual_is_missing(actual, expected_item) {
93                 return MatcherResult::NoMatch;
94             }
95         }
96         MatcherResult::Match
97     }
98 
explain_match(&self, actual: ActualT) -> Description99     fn explain_match(&self, actual: ActualT) -> Description {
100         let missing_items: Vec<_> = self
101             .subset
102             .into_iter()
103             .filter(|expected_item| actual_is_missing(actual, *expected_item))
104             .map(|expected_item| format!("{expected_item:#?}"))
105             .collect();
106         match missing_items.len() {
107             0 => "whose no element is missing".into(),
108             1 => format!("whose element {} is missing", &missing_items[0]).into(),
109             _ => format!("whose elements {} are missing", missing_items.join(", ")).into(),
110         }
111     }
112 
describe(&self, matcher_result: MatcherResult) -> Description113     fn describe(&self, matcher_result: MatcherResult) -> Description {
114         match matcher_result {
115             MatcherResult::Match => format!("is a superset of {:#?}", self.subset).into(),
116             MatcherResult::NoMatch => format!("isn't a superset of {:#?}", self.subset).into(),
117         }
118     }
119 }
120 
actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool where ActualT: IntoIterator<Item = ElementT>,121 fn actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool
122 where
123     ActualT: IntoIterator<Item = ElementT>,
124 {
125     !actual.into_iter().any(|item| &item == needle)
126 }
127 #[cfg(test)]
128 mod tests {
129     use crate::prelude::*;
130     use indoc::indoc;
131     use std::collections::HashSet;
132 
133     #[test]
superset_of_matches_empty_vec() -> Result<()>134     fn superset_of_matches_empty_vec() -> Result<()> {
135         let value: Vec<i32> = vec![];
136         verify_that!(value, superset_of([]))
137     }
138 
139     #[test]
superset_of_matches_vec_with_one_element() -> Result<()>140     fn superset_of_matches_vec_with_one_element() -> Result<()> {
141         let value = vec![1];
142         verify_that!(value, superset_of([&1]))
143     }
144 
145     #[test]
superset_of_matches_vec_with_two_items() -> Result<()>146     fn superset_of_matches_vec_with_two_items() -> Result<()> {
147         let value = vec![1, 2];
148         verify_that!(value, superset_of([&1, &2]))
149     }
150 
151     #[test]
superset_of_matches_vec_when_actual_has_excess_element() -> Result<()>152     fn superset_of_matches_vec_when_actual_has_excess_element() -> Result<()> {
153         let value = vec![1, 2, 3];
154         verify_that!(value, superset_of([&1, &2]))
155     }
156 
157     #[test]
superset_of_matches_vec_when_actual_has_excess_element_first() -> Result<()>158     fn superset_of_matches_vec_when_actual_has_excess_element_first() -> Result<()> {
159         let value = vec![3, 1, 2];
160         verify_that!(value, superset_of([&1, &2]))
161     }
162 
163     #[test]
superset_of_matches_slice_with_one_element() -> Result<()>164     fn superset_of_matches_slice_with_one_element() -> Result<()> {
165         let value = &[1];
166         verify_that!(value, superset_of([&1]))
167     }
168 
169     #[test]
superset_of_matches_hash_set_with_one_element() -> Result<()>170     fn superset_of_matches_hash_set_with_one_element() -> Result<()> {
171         let value: HashSet<i32> = [1].into();
172         verify_that!(value, superset_of([&1]))
173     }
174 
175     #[test]
superset_of_does_not_match_when_first_element_does_not_match() -> Result<()>176     fn superset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
177         let value = vec![0];
178         verify_that!(value, not(superset_of([&1])))
179     }
180 
181     #[test]
superset_of_does_not_match_when_second_element_does_not_match() -> Result<()>182     fn superset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
183         let value = vec![2];
184         verify_that!(value, not(superset_of([&2, &0])))
185     }
186 
187     #[test]
superset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()>188     fn superset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
189         let result = verify_that!(vec![0, 2, 3], superset_of([&1, &2, &3]));
190 
191         verify_that!(
192             result,
193             err(displays_as(contains_substring(indoc!(
194                 "
195                     Value of: vec![0, 2, 3]
196                     Expected: is a superset of [
197                         1,
198                         2,
199                         3,
200                     ]
201                     Actual: [0, 2, 3],
202                       whose element 1 is missing
203                 "
204             ))))
205         )
206     }
207 
208     #[test]
superset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()>209     fn superset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
210         let result = verify_that!(vec![1, 0, 3], superset_of([&1, &2, &3]));
211 
212         verify_that!(
213             result,
214             err(displays_as(contains_substring(indoc!(
215                 "
216                     Value of: vec![1, 0, 3]
217                     Expected: is a superset of [
218                         1,
219                         2,
220                         3,
221                     ]
222                     Actual: [1, 0, 3],
223                       whose element 2 is missing
224                 "
225             ))))
226         )
227     }
228 
229     #[test]
superset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()>230     fn superset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
231         let result = verify_that!(vec![0, 0, 3], superset_of([&1, &2, &3]));
232 
233         verify_that!(
234             result,
235             err(displays_as(contains_substring(indoc!(
236                 "
237                     Value of: vec![0, 0, 3]
238                     Expected: is a superset of [
239                         1,
240                         2,
241                         3,
242                     ]
243                     Actual: [0, 0, 3],
244                       whose elements 1, 2 are missing
245                 "
246             ))))
247         )
248     }
249 }
250