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, MatcherResult},
18 };
19 use std::{fmt::Debug, marker::PhantomData};
20 
21 /// Matches a container all of whose items are in the given container
22 /// `superset`.
23 ///
24 /// The element type `ElementT` must implement `PartialEq` to allow element
25 /// comparison.
26 ///
27 /// `ActualT` and `ExpectedT` can each be any container a reference to which
28 /// implements `IntoIterator`. For instance, `T` can be a common container like
29 /// `Vec` or arrays. They need not be the same container type.
30 ///
31 /// ```
32 /// # use googletest::prelude::*;
33 /// # use std::collections::HashSet;
34 /// # fn should_pass_1() -> Result<()> {
35 /// let value = vec![1, 2, 3];
36 /// verify_that!(value, subset_of([1, 2, 3, 4]))?;  // Passes
37 /// let array_value = [1, 2, 3];
38 /// verify_that!(array_value, subset_of([1, 2, 3, 4]))?;  // Passes
39 /// #     Ok(())
40 /// # }
41 /// # fn should_fail() -> Result<()> {
42 /// # let value = vec![1, 2, 3];
43 /// verify_that!(value, subset_of([1, 2]))?;  // Fails: 3 is not in the superset
44 /// #     Ok(())
45 /// # }
46 /// # should_pass_1().unwrap();
47 /// # should_fail().unwrap_err();
48 ///
49 /// # fn should_pass_2() -> Result<()> {
50 /// let value: HashSet<i32> = [1, 2, 3].into();
51 /// verify_that!(value, subset_of([1, 2, 3]))?;  // Passes
52 /// #     Ok(())
53 /// # }
54 /// # should_pass_2().unwrap();
55 /// ```
56 ///
57 /// Item multiplicity in both the actual and expected containers is ignored:
58 ///
59 /// ```
60 /// # use googletest::prelude::*;
61 /// # fn should_pass() -> Result<()> {
62 /// let value: Vec<i32> = vec![0, 0, 1];
63 /// verify_that!(value, subset_of([0, 1]))?;  // Passes
64 /// verify_that!(value, subset_of([0, 1, 1]))?;  // Passes
65 /// #     Ok(())
66 /// # }
67 /// # should_pass().unwrap();
68 /// ```
69 ///
70 /// One can also verify the contents of a slice by dereferencing it:
71 ///
72 /// ```
73 /// # use googletest::prelude::*;
74 /// # fn should_pass() -> Result<()> {
75 /// let value = &[1, 2, 3];
76 /// verify_that!(*value, subset_of([1, 2, 3]))?;
77 /// #     Ok(())
78 /// # }
79 /// # should_pass().unwrap();
80 /// ```
81 ///
82 /// A note on performance: This matcher uses a naive algorithm with a worst-case
83 /// runtime proportional to the *product* of the sizes of the actual and
84 /// expected containers as well as the time to check equality of each pair of
85 /// items. It should not be used on especially large containers.
subset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>( superset: ExpectedT, ) -> impl Matcher<ActualT = ActualT> where for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>, for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,86 pub fn subset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>(
87     superset: ExpectedT,
88 ) -> impl Matcher<ActualT = ActualT>
89 where
90     for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
91     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
92 {
93     SubsetOfMatcher::<ActualT, _> { superset, phantom: Default::default() }
94 }
95 
96 struct SubsetOfMatcher<ActualT: ?Sized, ExpectedT> {
97     superset: ExpectedT,
98     phantom: PhantomData<ActualT>,
99 }
100 
101 impl<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher
102     for SubsetOfMatcher<ActualT, ExpectedT>
103 where
104     for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
105     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
106 {
107     type ActualT = ActualT;
108 
matches(&self, actual: &ActualT) -> MatcherResult109     fn matches(&self, actual: &ActualT) -> MatcherResult {
110         for actual_item in actual {
111             if self.expected_is_missing(actual_item) {
112                 return MatcherResult::NoMatch;
113             }
114         }
115         MatcherResult::Match
116     }
117 
explain_match(&self, actual: &ActualT) -> Description118     fn explain_match(&self, actual: &ActualT) -> Description {
119         let unexpected_elements = actual
120             .into_iter()
121             .enumerate()
122             .filter(|&(_, actual_item)| self.expected_is_missing(actual_item))
123             .map(|(idx, actual_item)| format!("{actual_item:#?} at #{idx}"))
124             .collect::<Vec<_>>();
125 
126         match unexpected_elements.len() {
127             0 => "which no element is unexpected".into(),
128             1 => format!("whose element {} is unexpected", &unexpected_elements[0]).into(),
129             _ => format!("whose elements {} are unexpected", unexpected_elements.join(", ")).into(),
130         }
131     }
132 
describe(&self, matcher_result: MatcherResult) -> Description133     fn describe(&self, matcher_result: MatcherResult) -> Description {
134         match matcher_result {
135             MatcherResult::Match => format!("is a subset of {:#?}", self.superset).into(),
136             MatcherResult::NoMatch => format!("isn't a subset of {:#?}", self.superset).into(),
137         }
138     }
139 }
140 
141 impl<ActualT: ?Sized, ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ActualT, ExpectedT>
142 where
143     for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
144 {
expected_is_missing(&self, needle: &ElementT) -> bool145     fn expected_is_missing(&self, needle: &ElementT) -> bool {
146         !self.superset.into_iter().any(|item| *item == *needle)
147     }
148 }
149 
150 #[cfg(test)]
151 mod tests {
152     use super::subset_of;
153     use crate::prelude::*;
154     use indoc::indoc;
155     use std::collections::HashSet;
156 
157     #[test]
subset_of_matches_empty_vec() -> Result<()>158     fn subset_of_matches_empty_vec() -> Result<()> {
159         let value: Vec<i32> = vec![];
160         verify_that!(value, subset_of([]))
161     }
162 
163     #[test]
subset_of_matches_vec_with_one_element() -> Result<()>164     fn subset_of_matches_vec_with_one_element() -> Result<()> {
165         let value = vec![1];
166         verify_that!(value, subset_of([1]))
167     }
168 
169     #[test]
subset_of_matches_vec_with_two_elements() -> Result<()>170     fn subset_of_matches_vec_with_two_elements() -> Result<()> {
171         let value = vec![1, 2];
172         verify_that!(value, subset_of([1, 2]))
173     }
174 
175     #[test]
subset_of_matches_vec_when_expected_has_excess_element() -> Result<()>176     fn subset_of_matches_vec_when_expected_has_excess_element() -> Result<()> {
177         let value = vec![1, 2];
178         verify_that!(value, subset_of([1, 2, 3]))
179     }
180 
181     #[test]
subset_of_matches_vec_when_expected_has_excess_element_first() -> Result<()>182     fn subset_of_matches_vec_when_expected_has_excess_element_first() -> Result<()> {
183         let value = vec![1, 2];
184         verify_that!(value, subset_of([3, 1, 2]))
185     }
186 
187     #[test]
subset_of_matches_slice_with_one_element() -> Result<()>188     fn subset_of_matches_slice_with_one_element() -> Result<()> {
189         let value = &[1];
190         verify_that!(*value, subset_of([1]))
191     }
192 
193     #[test]
subset_of_matches_hash_set_with_one_element() -> Result<()>194     fn subset_of_matches_hash_set_with_one_element() -> Result<()> {
195         let value: HashSet<i32> = [1].into();
196         verify_that!(value, subset_of([1]))
197     }
198 
199     #[test]
subset_of_does_not_match_when_first_element_does_not_match() -> Result<()>200     fn subset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
201         let value = vec![0];
202         verify_that!(value, not(subset_of([1])))
203     }
204 
205     #[test]
subset_of_does_not_match_when_second_element_does_not_match() -> Result<()>206     fn subset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
207         let value = vec![2, 0];
208         verify_that!(value, not(subset_of([2])))
209     }
210 
211     #[test]
subset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()>212     fn subset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
213         let result = verify_that!(vec![0, 2, 3], subset_of([1, 2, 3]));
214 
215         verify_that!(
216             result,
217             err(displays_as(contains_substring(indoc!(
218                 "
219                     Value of: vec![0, 2, 3]
220                     Expected: is a subset of [
221                         1,
222                         2,
223                         3,
224                     ]
225                     Actual: [0, 2, 3],
226                       whose element 0 at #0 is unexpected
227                 "
228             ))))
229         )
230     }
231 
232     #[test]
subset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()>233     fn subset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
234         let result = verify_that!(vec![1, 0, 3], subset_of([1, 2, 3]));
235 
236         verify_that!(
237             result,
238             err(displays_as(contains_substring(indoc!(
239                 "
240                     Value of: vec![1, 0, 3]
241                     Expected: is a subset of [
242                         1,
243                         2,
244                         3,
245                     ]
246                     Actual: [1, 0, 3],
247                       whose element 0 at #1 is unexpected
248                 "
249             ))))
250         )
251     }
252 
253     #[test]
subset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()>254     fn subset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
255         let result = verify_that!(vec![0, 0, 3], subset_of([1, 2, 3]));
256 
257         verify_that!(
258             result,
259             err(displays_as(contains_substring(indoc!(
260                 "
261                     Value of: vec![0, 0, 3]
262                     Expected: is a subset of [
263                         1,
264                         2,
265                         3,
266                     ]
267                     Actual: [0, 0, 3],
268                       whose elements 0 at #0, 0 at #1 are unexpected
269                 "
270             ))))
271         )
272     }
273 }
274