1use crate::{
16 description::Description,
17 matcher::{Matcher, MatcherBase, MatcherResult},
18};
19use std::fmt::Debug;
20
21pub fn superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT> {
76 SupersetOfMatcher { subset }
77}
78
79#[derive(MatcherBase)]
80pub struct SupersetOfMatcher<ExpectedT> {
81 subset: ExpectedT,
82}
83
84impl<ElementT: Debug + Copy + PartialEq, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
85 for SupersetOfMatcher<ExpectedT>
86where
87 ActualT: IntoIterator<Item = ElementT>,
88 for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
89{
90 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
99 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
113 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
121fn actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool
122where
123 ActualT: IntoIterator<Item = ElementT>,
124{
125 !actual.into_iter().any(|item| &item == needle)
126}
127#[cfg(test)]
128mod tests {
129 use crate as googletest;
130 use crate::prelude::*;
131 use indoc::indoc;
132 use std::collections::HashSet;
133
134 #[test]
135 fn superset_of_matches_empty_vec() -> googletest::Result<()> {
136 let value: Vec<i32> = vec![];
137 verify_that!(value, superset_of([]))
138 }
139
140 #[test]
141 fn superset_of_matches_vec_with_one_element() -> googletest::Result<()> {
142 let value = vec![1];
143 verify_that!(value, superset_of([&1]))
144 }
145
146 #[test]
147 fn superset_of_matches_vec_with_two_items() -> googletest::Result<()> {
148 let value = vec![1, 2];
149 verify_that!(value, superset_of([&1, &2]))
150 }
151
152 #[test]
153 fn superset_of_matches_vec_when_actual_has_excess_element() -> googletest::Result<()> {
154 let value = vec![1, 2, 3];
155 verify_that!(value, superset_of([&1, &2]))
156 }
157
158 #[test]
159 fn superset_of_matches_vec_when_actual_has_excess_element_first() -> googletest::Result<()> {
160 let value = vec![3, 1, 2];
161 verify_that!(value, superset_of([&1, &2]))
162 }
163
164 #[test]
165 fn superset_of_matches_slice_with_one_element() -> googletest::Result<()> {
166 let value = &[1];
167 verify_that!(value, superset_of([&1]))
168 }
169
170 #[test]
171 fn superset_of_matches_hash_set_with_one_element() -> googletest::Result<()> {
172 let value: HashSet<i32> = [1].into();
173 verify_that!(value, superset_of([&1]))
174 }
175
176 #[test]
177 fn superset_of_does_not_match_when_first_element_does_not_match() -> googletest::Result<()> {
178 let value = vec![0];
179 verify_that!(value, not(superset_of([&1])))
180 }
181
182 #[test]
183 fn superset_of_does_not_match_when_second_element_does_not_match() -> googletest::Result<()> {
184 let value = vec![2];
185 verify_that!(value, not(superset_of([&2, &0])))
186 }
187
188 #[test]
189 fn superset_of_shows_correct_message_when_first_item_does_not_match() -> googletest::Result<()>
190 {
191 let result = verify_that!(vec![0, 2, 3], superset_of([&1, &2, &3]));
192
193 verify_that!(
194 result,
195 err(displays_as(contains_substring(indoc!(
196 "
197 Value of: vec![0, 2, 3]
198 Expected: is a superset of [
199 1,
200 2,
201 3,
202 ]
203 Actual: [0, 2, 3],
204 whose element 1 is missing
205 "
206 ))))
207 )
208 }
209
210 #[test]
211 fn superset_of_shows_correct_message_when_second_item_does_not_match() -> googletest::Result<()>
212 {
213 let result = verify_that!(vec![1, 0, 3], superset_of([&1, &2, &3]));
214
215 verify_that!(
216 result,
217 err(displays_as(contains_substring(indoc!(
218 "
219 Value of: vec![1, 0, 3]
220 Expected: is a superset of [
221 1,
222 2,
223 3,
224 ]
225 Actual: [1, 0, 3],
226 whose element 2 is missing
227 "
228 ))))
229 )
230 }
231
232 #[test]
233 fn superset_of_shows_correct_message_when_first_two_items_do_not_match(
234 ) -> googletest::Result<()> {
235 let result = verify_that!(vec![0, 0, 3], superset_of([&1, &2, &3]));
236
237 verify_that!(
238 result,
239 err(displays_as(contains_substring(indoc!(
240 "
241 Value of: vec![0, 0, 3]
242 Expected: is a superset of [
243 1,
244 2,
245 3,
246 ]
247 Actual: [0, 0, 3],
248 whose elements 1, 2 are missing
249 "
250 ))))
251 )
252 }
253}