fluent_test/backend/matchers/
collection.rs1use crate::backend::Assertion;
2use crate::backend::assertions::sentence::AssertionSentence;
3use std::fmt::Debug;
4
5pub trait CollectionMatchers<T> {
7 fn to_be_empty(self) -> Self;
8 fn to_have_length(self, expected: usize) -> Self;
9 fn to_contain<U: PartialEq<T> + Debug>(self, expected: U) -> Self;
10 fn to_contain_all_of<U: PartialEq<T> + Debug>(self, expected: &[U]) -> Self;
11 fn to_equal_collection<U: PartialEq<T> + Debug>(self, expected: &[U]) -> Self;
12}
13
14trait AsCollection {
16 type Item;
17
18 fn is_empty(&self) -> bool;
19 fn length(&self) -> usize;
20 fn contains_item<U>(&self, item: &U) -> bool
21 where
22 U: PartialEq<Self::Item>;
23 fn contains_all_items<U>(&self, items: &[U]) -> bool
24 where
25 U: PartialEq<Self::Item>;
26 fn equals_items<U>(&self, other: &[U]) -> bool
27 where
28 U: PartialEq<Self::Item>;
29}
30
31impl<T: PartialEq> AsCollection for &[T] {
33 type Item = T;
34
35 fn is_empty(&self) -> bool {
36 <[T]>::is_empty(self)
37 }
38
39 fn length(&self) -> usize {
40 self.len()
41 }
42
43 fn contains_item<U>(&self, item: &U) -> bool
44 where
45 U: PartialEq<Self::Item>,
46 {
47 self.iter().any(|x| item == x)
48 }
49
50 fn contains_all_items<U>(&self, items: &[U]) -> bool
51 where
52 U: PartialEq<Self::Item>,
53 {
54 items.iter().all(|item| self.contains_item(item))
55 }
56
57 fn equals_items<U>(&self, other: &[U]) -> bool
58 where
59 U: PartialEq<Self::Item>,
60 {
61 if self.len() != other.len() {
62 return false;
63 }
64
65 self.iter().zip(other.iter()).all(|(a, b)| b == a)
66 }
67}
68
69impl<T: PartialEq> AsCollection for &Vec<T> {
71 type Item = T;
72
73 fn is_empty(&self) -> bool {
74 Vec::is_empty(self)
75 }
76
77 fn length(&self) -> usize {
78 self.len()
79 }
80
81 fn contains_item<U>(&self, item: &U) -> bool
82 where
83 U: PartialEq<Self::Item>,
84 {
85 self.iter().any(|x| item == x)
86 }
87
88 fn contains_all_items<U>(&self, items: &[U]) -> bool
89 where
90 U: PartialEq<Self::Item>,
91 {
92 items.iter().all(|item| self.contains_item(item))
93 }
94
95 fn equals_items<U>(&self, other: &[U]) -> bool
96 where
97 U: PartialEq<Self::Item>,
98 {
99 if self.len() != other.len() {
100 return false;
101 }
102
103 self.iter().zip(other.iter()).all(|(a, b)| b == a)
104 }
105}
106
107impl<T: PartialEq> AsCollection for Vec<T> {
109 type Item = T;
110
111 fn is_empty(&self) -> bool {
112 Vec::is_empty(self)
113 }
114
115 fn length(&self) -> usize {
116 self.len()
117 }
118
119 fn contains_item<U>(&self, item: &U) -> bool
120 where
121 U: PartialEq<Self::Item>,
122 {
123 self.iter().any(|x| item == x)
124 }
125
126 fn contains_all_items<U>(&self, items: &[U]) -> bool
127 where
128 U: PartialEq<Self::Item>,
129 {
130 items.iter().all(|item| self.contains_item(item))
131 }
132
133 fn equals_items<U>(&self, other: &[U]) -> bool
134 where
135 U: PartialEq<Self::Item>,
136 {
137 if self.len() != other.len() {
138 return false;
139 }
140
141 self.iter().zip(other.iter()).all(|(a, b)| b == a)
142 }
143}
144
145impl<T: PartialEq, const N: usize> AsCollection for &[T; N] {
147 type Item = T;
148
149 fn is_empty(&self) -> bool {
150 N == 0
151 }
152
153 fn length(&self) -> usize {
154 N
155 }
156
157 fn contains_item<U>(&self, item: &U) -> bool
158 where
159 U: PartialEq<Self::Item>,
160 {
161 self.iter().any(|x| item == x)
162 }
163
164 fn contains_all_items<U>(&self, items: &[U]) -> bool
165 where
166 U: PartialEq<Self::Item>,
167 {
168 items.iter().all(|item| self.contains_item(item))
169 }
170
171 fn equals_items<U>(&self, other: &[U]) -> bool
172 where
173 U: PartialEq<Self::Item>,
174 {
175 if N != other.len() {
176 return false;
177 }
178
179 self.iter().zip(other.iter()).all(|(a, b)| b == a)
180 }
181}
182
183impl<T, V> CollectionMatchers<T> for Assertion<V>
185where
186 T: Debug + Clone + PartialEq,
187 V: AsCollection<Item = T> + Debug + Clone,
188{
189 fn to_be_empty(self) -> Self {
190 let result = self.value.is_empty();
191 let sentence = AssertionSentence::new("be", "empty");
192
193 return self.add_step(sentence, result);
194 }
195
196 fn to_have_length(self, expected: usize) -> Self {
197 let actual_length = self.value.length();
198 let result = actual_length == expected;
199 let sentence = AssertionSentence::new("have", format!("length {}", expected));
200
201 return self.add_step(sentence, result);
202 }
203
204 fn to_contain<U: PartialEq<T> + Debug>(self, expected: U) -> Self {
205 let result = self.value.contains_item(&expected);
206 let sentence = AssertionSentence::new("contain", format!("{:?}", expected));
207
208 return self.add_step(sentence, result);
209 }
210
211 fn to_contain_all_of<U: PartialEq<T> + Debug>(self, expected: &[U]) -> Self {
212 let result = self.value.contains_all_items(expected);
213 let sentence = AssertionSentence::new("contain", format!("all of {:?}", expected));
214
215 return self.add_step(sentence, result);
216 }
217
218 fn to_equal_collection<U: PartialEq<T> + Debug>(self, expected: &[U]) -> Self {
219 let result = self.value.equals_items(expected);
220
221 if self.value.length() != expected.len() {
223 let sentence = AssertionSentence::new("equal", format!("collection {:?} (different lengths)", expected));
224 return self.add_step(sentence, result);
225 }
226
227 let sentence = AssertionSentence::new("equal", format!("collection {:?}", expected));
228 return self.add_step(sentence, result);
229 }
230}
231
232pub trait CollectionExtensions<T> {
234 fn first(&self) -> Option<&T>;
235 fn last(&self) -> Option<&T>;
236}
237
238impl<T> CollectionExtensions<T> for Vec<T> {
239 fn first(&self) -> Option<&T> {
240 return <[T]>::first(self);
241 }
242
243 fn last(&self) -> Option<&T> {
244 return <[T]>::last(self);
245 }
246}
247
248impl<T> CollectionExtensions<T> for &[T] {
249 fn first(&self) -> Option<&T> {
250 return <[T]>::first(self);
251 }
252
253 fn last(&self) -> Option<&T> {
254 return <[T]>::last(self);
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use crate::prelude::*;
261
262 #[test]
263 fn test_collection_length() {
264 crate::Reporter::disable_deduplication();
266
267 let collection = vec![1, 2, 3, 4, 5];
268 let slice = collection.as_slice();
269
270 expect!(slice).to_have_length(5);
271 expect!(&collection).to_have_length(5);
272 }
273
274 #[test]
275 #[should_panic(expected = "have length 6")]
276 fn test_wrong_length_fails() {
277 let collection = vec![1, 2, 3, 4, 5];
279 let slice = collection.as_slice();
280 expect!(slice).to_have_length(6);
281 }
282
283 #[test]
284 #[should_panic(expected = "not have length 5")]
285 fn test_right_length_not_fails() {
286 let collection = vec![1, 2, 3, 4, 5];
288 let slice = collection.as_slice();
289 expect!(slice).not().to_have_length(5);
290 }
291
292 #[test]
293 fn test_collection_contains() {
294 crate::Reporter::disable_deduplication();
296
297 let collection = vec![1, 2, 3, 4, 5];
298 let slice = collection.as_slice();
299
300 expect!(slice).to_contain(3);
301 expect!(&collection).to_contain(3);
302 }
303
304 #[test]
305 #[should_panic(expected = "contain 6")]
306 fn test_missing_value_fails() {
307 let collection = vec![1, 2, 3, 4, 5];
308 let slice = collection.as_slice();
309 expect!(slice).to_contain(6);
310 }
311
312 #[test]
313 #[should_panic(expected = "not contain 3")]
314 fn test_present_value_not_fails() {
315 let collection = vec![1, 2, 3, 4, 5];
316 let slice = collection.as_slice();
317 expect!(slice).not().to_contain(3);
318 }
319
320 #[test]
321 fn test_collection_contains_all() {
322 crate::Reporter::disable_deduplication();
324
325 let collection = vec![1, 2, 3, 4, 5];
326 let slice = collection.as_slice();
327
328 expect!(slice).to_contain_all_of(&[1, 3, 5]);
329 expect!(&collection).to_contain_all_of(&[1, 3, 5]);
330 }
331
332 #[test]
333 #[should_panic(expected = "contain all of")]
334 fn test_missing_values_fails() {
335 let collection = vec![1, 2, 3, 4, 5];
336 let slice = collection.as_slice();
337 expect!(slice).to_contain_all_of(&[1, 6, 7]);
338 }
339
340 #[test]
341 #[should_panic(expected = "not contain all of")]
342 fn test_present_values_not_fails() {
343 let collection = vec![1, 2, 3, 4, 5];
344 let slice = collection.as_slice();
345 expect!(slice).not().to_contain_all_of(&[1, 3, 5]);
346 }
347
348 #[test]
349 fn test_collection_equality() {
350 crate::Reporter::disable_deduplication();
352
353 let collection = vec![1, 2, 3, 4, 5];
354 let slice = collection.as_slice();
355
356 expect!(slice).to_equal_collection(&[1, 2, 3, 4, 5]);
357 expect!(&collection).to_equal_collection(&[1, 2, 3, 4, 5]);
358 }
359
360 #[test]
361 #[should_panic(expected = "equal collection")]
362 fn test_different_collection_fails() {
363 let collection = vec![1, 2, 3, 4, 5];
364 let slice = collection.as_slice();
365 expect!(slice).to_equal_collection(&[5, 4, 3, 2, 1]);
366 }
367
368 #[test]
369 #[should_panic(expected = "different lengths")]
370 fn test_shorter_collection_fails() {
371 let collection = vec![1, 2, 3, 4, 5];
372 let slice = collection.as_slice();
373 expect!(slice).to_equal_collection(&[1, 2, 3]);
374 }
375
376 #[test]
377 #[should_panic(expected = "not equal collection")]
378 fn test_same_collection_not_fails() {
379 let collection = vec![1, 2, 3, 4, 5];
380 let slice = collection.as_slice();
381 expect!(slice).not().to_equal_collection(&[1, 2, 3, 4, 5]);
382 }
383
384 #[test]
385 fn test_empty_collection() {
386 crate::Reporter::disable_deduplication();
388
389 let empty: Vec<i32> = vec![];
390 let slice = empty.as_slice();
391
392 expect!(slice).to_be_empty();
393 expect!(&empty).to_be_empty();
394 }
395
396 #[test]
397 #[should_panic(expected = "be empty")]
398 fn test_non_empty_to_be_empty_fails() {
399 let collection = vec![1, 2, 3];
400 let slice = collection.as_slice();
401 expect!(slice).to_be_empty();
402 }
403
404 #[test]
405 #[should_panic(expected = "not be empty")]
406 fn test_empty_not_to_be_empty_fails() {
407 let empty: Vec<i32> = vec![];
408 let slice = empty.as_slice();
409 expect!(slice).not().to_be_empty();
410 }
411}