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