1use alloc::borrow::ToOwned;
2use alloc::format;
3use alloc::vec::Vec;
4use core::fmt::Debug;
5
6use crate::{AssertThat, AssertrPartialEq, Mode, tracking::AssertionTracking};
7
8pub trait SliceAssertions<'t, T> {
9 fn contains<E>(self, expected: E) -> Self
10 where
11 E: Debug,
12 T: AssertrPartialEq<E> + Debug;
13
14 fn contains_exactly<E, EE>(self, expected: EE) -> Self
20 where
21 E: Debug + 't,
22 EE: AsRef<[E]>,
23 T: AssertrPartialEq<E> + Debug;
24
25 fn contains_exactly_in_any_order<E: AsRef<[T]>>(self, expected: E) -> Self
26 where
27 T: PartialEq + Debug;
28
29 fn contains_exactly_matching_in_any_order<P>(self, expected: impl AsRef<[P]>) -> Self
31 where
32 T: Debug,
33 P: Fn(&T) -> bool;
34}
35
36impl<'t, T, M: Mode> SliceAssertions<'t, T> for AssertThat<'t, &[T], M> {
37 #[track_caller]
38 fn contains<E>(self, expected: E) -> Self
39 where
40 E: Debug,
41 T: Debug + AssertrPartialEq<E>,
42 {
43 self.track_assertion();
44 let actual = self.actual().iter().collect::<Vec<_>>();
45 let expected = expected;
46 if !self
47 .actual()
48 .iter()
49 .any(|it| AssertrPartialEq::eq(it, &expected, None))
50 {
51 self.fail(format_args!(
52 "Actual: {actual:#?}\n\ndoes not contain expected: {expected:#?}\n",
53 ));
54 }
55 self
56 }
57
58 #[track_caller]
59 fn contains_exactly<E, EE>(self, expected: EE) -> Self
60 where
61 E: Debug + 't,
62 EE: AsRef<[E]>,
63 T: AssertrPartialEq<E> + Debug,
64 {
65 self.track_assertion();
66 let actual = *self.actual();
67 let expected = expected.as_ref();
68
69 let result = crate::util::slice::compare(actual, expected);
70
71 if !result.strictly_equal {
72 if !result.not_in_b.is_empty() {
73 self.add_detail_message(format!("Elements not expected: {:#?}", result.not_in_b));
74 }
75 if !result.not_in_a.is_empty() {
76 self.add_detail_message(format!("Elements not found: {:#?}", result.not_in_a));
77 }
78 if result.only_differing_in_order() {
79 self.add_detail_message("The order of elements does not match!".to_owned());
80 }
81
82 let actual = self.actual();
83
84 self.fail(format_args!(
85 "Actual: {actual:#?},\n\ndid not exactly match\n\nExpected: {expected:#?}\n",
86 ));
87 }
88 self
89 }
90
91 #[track_caller]
92 fn contains_exactly_in_any_order<E: AsRef<[T]>>(self, expected: E) -> Self
93 where
94 T: PartialEq + Debug,
95 {
96 self.track_assertion();
97 let actual: &[T] = self.actual();
98 let expected: &[T] = expected.as_ref();
99
100 let mut elements_found = Vec::new();
101 let mut elements_not_found: Vec<&T> = Vec::new();
102 let mut elements_not_expected = Vec::new();
103
104 for e in expected.iter() {
105 let found = actual.as_ref().iter().find(|it| *it == e);
106
107 match found {
108 Some(_e) => elements_found.push(e),
109 None => elements_not_found.push(e),
110 }
111 }
112
113 for e in actual.iter() {
114 match elements_found.iter().find(|it| **it == e) {
115 Some(_) => {}
116 None => elements_not_expected.push(e),
117 }
118 }
119
120 if !elements_not_found.is_empty() || !elements_not_expected.is_empty() {
121 self.fail(format_args!(
122 "Actual: {actual:#?},\n\nElements expected: {expected:#?}\n\nElements not found: {elements_not_found:#?}\n\nElements not expected: {elements_not_expected:#?}\n",
123 actual = actual,
124 expected = expected
125 ));
126 }
127 self
128 }
129
130 #[track_caller]
131 fn contains_exactly_matching_in_any_order<P>(self, expected: impl AsRef<[P]>) -> Self
132 where
133 T: Debug,
134 P: Fn(&T) -> bool,
135 {
136 self.track_assertion();
137 let actual = *self.actual();
138 let expected = expected.as_ref();
139
140 let result = crate::util::slice::test_matching_any(actual, expected);
141
142 if !result.not_matched.is_empty() {
143 if !result.not_matched.is_empty() {
144 self.add_detail_message(format!("Elements not matched: {:#?}", result.not_matched));
145 }
146
147 let actual = self.actual();
148
149 self.fail(format_args!(
150 "Actual: {actual:#?},\n\ndid not exactly match predicates in any order.\n",
151 ));
152 }
153 self
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 mod contains_exactly {
160 use indoc::formatdoc;
161
162 use crate::prelude::*;
163
164 #[test]
165 fn succeeds_when_exact_match() {
166 assert_that([1, 2, 3].as_slice()).contains_exactly([1, 2, 3]);
167 }
168
169 #[test]
170 fn compiles_for_different_type_combinations() {
171 assert_that(["foo".to_owned()].as_slice()).contains_exactly(["foo"]);
172 assert_that(["foo"].as_slice()).contains_exactly(["foo"]);
173 assert_that(["foo"].as_slice()).contains_exactly(["foo".to_owned()]);
174 assert_that(["foo"].as_slice()).contains_exactly(vec!["foo".to_owned()]);
175 assert_that(vec!["foo"].as_slice())
176 .contains_exactly(vec!["foo".to_owned()].into_iter());
177 }
178
179 #[test]
180 fn succeeds_when_exact_match_provided_as_slice() {
181 assert_that([1, 2, 3].as_slice()).contains_exactly(&[1, 2, 3]);
182 }
183
184 #[test]
185 fn panics_when_not_exact_match() {
186 assert_that_panic_by(|| {
187 assert_that([1, 2, 3].as_slice())
188 .with_location(false)
189 .contains_exactly([2, 3, 4])
190 })
191 .has_type::<String>()
192 .is_equal_to(formatdoc! {r#"
193 -------- assertr --------
194 Actual: [
195 1,
196 2,
197 3,
198 ],
199
200 did not exactly match
201
202 Expected: [
203 2,
204 3,
205 4,
206 ]
207
208 Details: [
209 Elements not expected: [
210 1,
211 ],
212 Elements not found: [
213 4,
214 ],
215 ]
216 -------- assertr --------
217 "#});
218 }
219
220 #[test]
221 fn panics_with_detail_message_when_only_differing_in_order() {
222 assert_that_panic_by(|| {
223 assert_that([1, 2, 3].as_slice())
224 .with_location(false)
225 .contains_exactly([3, 2, 1])
226 })
227 .has_type::<String>()
228 .is_equal_to(formatdoc! {r#"
229 -------- assertr --------
230 Actual: [
231 1,
232 2,
233 3,
234 ],
235
236 did not exactly match
237
238 Expected: [
239 3,
240 2,
241 1,
242 ]
243
244 Details: [
245 The order of elements does not match!,
246 ]
247 -------- assertr --------
248 "#});
249 }
250 }
251
252 mod contains_exactly_in_any_order {
253 use indoc::formatdoc;
254
255 use crate::prelude::*;
256
257 #[test]
258 fn succeeds_when_slices_match() {
259 assert_that([1, 2, 3].as_slice()).contains_exactly_in_any_order([2, 3, 1]);
260 }
261
262 #[test]
263 fn panics_when_slice_contains_unknown_data() {
264 assert_that_panic_by(|| {
265 assert_that([1, 2, 3].as_slice())
266 .with_location(false)
267 .contains_exactly_in_any_order([2, 3, 4])
268 })
269 .has_type::<String>()
270 .is_equal_to(formatdoc! {"
271 -------- assertr --------
272 Actual: [
273 1,
274 2,
275 3,
276 ],
277
278 Elements expected: [
279 2,
280 3,
281 4,
282 ]
283
284 Elements not found: [
285 4,
286 ]
287
288 Elements not expected: [
289 1,
290 ]
291 -------- assertr --------
292 "});
293 }
294 }
295
296 mod contains_exactly_matching_in_any_order {
297 use indoc::formatdoc;
298
299 use crate::prelude::*;
300
301 #[test]
302 fn succeeds_when_slices_match() {
303 assert_that([1, 2, 3].as_slice()).contains_exactly_matching_in_any_order(
304 [
305 move |it: &i32| *it == 1,
306 move |it: &i32| *it == 2,
307 move |it: &i32| *it == 3,
308 ]
309 .as_slice(),
310 );
311 }
312
313 #[test]
314 fn succeeds_when_slices_match_in_different_order() {
315 assert_that([1, 2, 3].as_slice()).contains_exactly_matching_in_any_order(
316 [
317 move |it: &i32| *it == 3,
318 move |it: &i32| *it == 1,
319 move |it: &i32| *it == 2,
320 ]
321 .as_slice(),
322 );
323 }
324
325 #[test]
326 fn panics_when_slice_contains_non_matching_data() {
327 assert_that_panic_by(|| {
328 assert_that([1, 2, 3].as_slice())
329 .with_location(false)
330 .contains_exactly_matching_in_any_order(
331 [
332 move |it: &i32| *it == 2,
333 move |it: &i32| *it == 3,
334 move |it: &i32| *it == 4,
335 ]
336 .as_slice(),
337 )
338 })
339 .has_type::<String>()
340 .is_equal_to(formatdoc! {"
341 -------- assertr --------
342 Actual: [
343 1,
344 2,
345 3,
346 ],
347
348 did not exactly match predicates in any order.
349
350 Details: [
351 Elements not matched: [
352 1,
353 ],
354 ]
355 -------- assertr --------
356 "});
357 }
358 }
359}