1 #![cfg(test)]
2 #![allow(unused_assignments)]
3 
4 // These tests are primarily targeting "abusive" producers that will
5 // try to drive the "collect consumer" incorrectly. These should
6 // result in panics.
7 
8 use super::collect_with_consumer;
9 use crate::iter::plumbing::*;
10 use rayon_core::join;
11 
12 use std::fmt;
13 use std::panic;
14 use std::sync::atomic::{AtomicUsize, Ordering};
15 use std::thread::Result as ThreadResult;
16 
17 /// Promises to produce 2 items, but then produces 3.  Does not do any
18 /// splits at all.
19 #[test]
20 #[should_panic(expected = "too many values")]
produce_too_many_items()21 fn produce_too_many_items() {
22     let mut v = vec![];
23     collect_with_consumer(&mut v, 2, |consumer| {
24         let mut folder = consumer.into_folder();
25         folder = folder.consume(22);
26         folder = folder.consume(23);
27         folder = folder.consume(24);
28         unreachable!("folder does not complete")
29     });
30 }
31 
32 /// Produces fewer items than promised. Does not do any
33 /// splits at all.
34 #[test]
35 #[should_panic(expected = "expected 5 total writes, but got 2")]
produce_fewer_items()36 fn produce_fewer_items() {
37     let mut v = vec![];
38     collect_with_consumer(&mut v, 5, |consumer| {
39         let mut folder = consumer.into_folder();
40         folder = folder.consume(22);
41         folder = folder.consume(23);
42         folder.complete()
43     });
44 }
45 
46 // Complete is not called by the consumer. Hence,the collection vector is not fully initialized.
47 #[test]
48 #[should_panic(expected = "expected 4 total writes, but got 2")]
left_produces_items_with_no_complete()49 fn left_produces_items_with_no_complete() {
50     let mut v = vec![];
51     collect_with_consumer(&mut v, 4, |consumer| {
52         let (left_consumer, right_consumer, _) = consumer.split_at(2);
53         let mut left_folder = left_consumer.into_folder();
54         let mut right_folder = right_consumer.into_folder();
55         left_folder = left_folder.consume(0).consume(1);
56         right_folder = right_folder.consume(2).consume(3);
57         right_folder.complete()
58     });
59 }
60 
61 // Complete is not called by the right consumer. Hence,the
62 // collection vector is not fully initialized.
63 #[test]
64 #[should_panic(expected = "expected 4 total writes, but got 2")]
right_produces_items_with_no_complete()65 fn right_produces_items_with_no_complete() {
66     let mut v = vec![];
67     collect_with_consumer(&mut v, 4, |consumer| {
68         let (left_consumer, right_consumer, _) = consumer.split_at(2);
69         let mut left_folder = left_consumer.into_folder();
70         let mut right_folder = right_consumer.into_folder();
71         left_folder = left_folder.consume(0).consume(1);
72         right_folder = right_folder.consume(2).consume(3);
73         left_folder.complete()
74     });
75 }
76 
77 // Complete is not called by the consumer. Hence,the collection vector is not fully initialized.
78 #[test]
79 #[cfg_attr(not(panic = "unwind"), ignore)]
produces_items_with_no_complete()80 fn produces_items_with_no_complete() {
81     let counter = DropCounter::default();
82     let mut v = vec![];
83     let panic_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
84         collect_with_consumer(&mut v, 2, |consumer| {
85             let mut folder = consumer.into_folder();
86             folder = folder.consume(counter.element());
87             folder = folder.consume(counter.element());
88             panic!("folder does not complete");
89         });
90     }));
91     assert!(v.is_empty());
92     assert_is_panic_with_message(&panic_result, "folder does not complete");
93     counter.assert_drop_count();
94 }
95 
96 // The left consumer produces too many items while the right
97 // consumer produces correct number.
98 #[test]
99 #[should_panic(expected = "too many values")]
left_produces_too_many_items()100 fn left_produces_too_many_items() {
101     let mut v = vec![];
102     collect_with_consumer(&mut v, 4, |consumer| {
103         let (left_consumer, right_consumer, _) = consumer.split_at(2);
104         let mut left_folder = left_consumer.into_folder();
105         let mut right_folder = right_consumer.into_folder();
106         left_folder = left_folder.consume(0).consume(1).consume(2);
107         right_folder = right_folder.consume(2).consume(3);
108         let _ = right_folder.complete();
109         unreachable!("folder does not complete");
110     });
111 }
112 
113 // The right consumer produces too many items while the left
114 // consumer produces correct number.
115 #[test]
116 #[should_panic(expected = "too many values")]
right_produces_too_many_items()117 fn right_produces_too_many_items() {
118     let mut v = vec![];
119     collect_with_consumer(&mut v, 4, |consumer| {
120         let (left_consumer, right_consumer, _) = consumer.split_at(2);
121         let mut left_folder = left_consumer.into_folder();
122         let mut right_folder = right_consumer.into_folder();
123         left_folder = left_folder.consume(0).consume(1);
124         right_folder = right_folder.consume(2).consume(3).consume(4);
125         let _ = left_folder.complete();
126         unreachable!("folder does not complete");
127     });
128 }
129 
130 // The left consumer produces fewer items while the right
131 // consumer produces correct number.
132 #[test]
133 #[should_panic(expected = "expected 4 total writes, but got 1")]
left_produces_fewer_items()134 fn left_produces_fewer_items() {
135     let mut v = vec![];
136     collect_with_consumer(&mut v, 4, |consumer| {
137         let reducer = consumer.to_reducer();
138         let (left_consumer, right_consumer, _) = consumer.split_at(2);
139         let mut left_folder = left_consumer.into_folder();
140         let mut right_folder = right_consumer.into_folder();
141         left_folder = left_folder.consume(0);
142         right_folder = right_folder.consume(2).consume(3);
143         let left_result = left_folder.complete();
144         let right_result = right_folder.complete();
145         reducer.reduce(left_result, right_result)
146     });
147 }
148 
149 // The left and right consumer produce the correct number but
150 // only left result is returned
151 #[test]
152 #[should_panic(expected = "expected 4 total writes, but got 2")]
only_left_result()153 fn only_left_result() {
154     let mut v = vec![];
155     collect_with_consumer(&mut v, 4, |consumer| {
156         let (left_consumer, right_consumer, _) = consumer.split_at(2);
157         let mut left_folder = left_consumer.into_folder();
158         let mut right_folder = right_consumer.into_folder();
159         left_folder = left_folder.consume(0).consume(1);
160         right_folder = right_folder.consume(2).consume(3);
161         let left_result = left_folder.complete();
162         let _ = right_folder.complete();
163         left_result
164     });
165 }
166 
167 // The left and right consumer produce the correct number but
168 // only right result is returned
169 #[test]
170 #[should_panic(expected = "expected 4 total writes, but got 2")]
only_right_result()171 fn only_right_result() {
172     let mut v = vec![];
173     collect_with_consumer(&mut v, 4, |consumer| {
174         let (left_consumer, right_consumer, _) = consumer.split_at(2);
175         let mut left_folder = left_consumer.into_folder();
176         let mut right_folder = right_consumer.into_folder();
177         left_folder = left_folder.consume(0).consume(1);
178         right_folder = right_folder.consume(2).consume(3);
179         let _ = left_folder.complete();
180         right_folder.complete()
181     });
182 }
183 
184 // The left and right consumer produce the correct number but reduce
185 // in the wrong order.
186 #[test]
187 #[should_panic(expected = "expected 4 total writes, but got 2")]
reducer_does_not_preserve_order()188 fn reducer_does_not_preserve_order() {
189     let mut v = vec![];
190     collect_with_consumer(&mut v, 4, |consumer| {
191         let reducer = consumer.to_reducer();
192         let (left_consumer, right_consumer, _) = consumer.split_at(2);
193         let mut left_folder = left_consumer.into_folder();
194         let mut right_folder = right_consumer.into_folder();
195         left_folder = left_folder.consume(0).consume(1);
196         right_folder = right_folder.consume(2).consume(3);
197         let left_result = left_folder.complete();
198         let right_result = right_folder.complete();
199         reducer.reduce(right_result, left_result)
200     });
201 }
202 
203 // The right consumer produces fewer items while the left
204 // consumer produces correct number.
205 #[test]
206 #[should_panic(expected = "expected 4 total writes, but got 3")]
right_produces_fewer_items()207 fn right_produces_fewer_items() {
208     let mut v = vec![];
209     collect_with_consumer(&mut v, 4, |consumer| {
210         let reducer = consumer.to_reducer();
211         let (left_consumer, right_consumer, _) = consumer.split_at(2);
212         let mut left_folder = left_consumer.into_folder();
213         let mut right_folder = right_consumer.into_folder();
214         left_folder = left_folder.consume(0).consume(1);
215         right_folder = right_folder.consume(2);
216         let left_result = left_folder.complete();
217         let right_result = right_folder.complete();
218         reducer.reduce(left_result, right_result)
219     });
220 }
221 
222 // The left consumer panics and the right stops short, like `panic_fuse()`.
223 // We should get the left panic without finishing `collect_with_consumer`.
224 #[test]
225 #[should_panic(expected = "left consumer panic")]
left_panics()226 fn left_panics() {
227     let mut v = vec![];
228     collect_with_consumer(&mut v, 4, |consumer| {
229         let reducer = consumer.to_reducer();
230         let (left_consumer, right_consumer, _) = consumer.split_at(2);
231         let (left_result, right_result) = join(
232             || {
233                 let mut left_folder = left_consumer.into_folder();
234                 left_folder = left_folder.consume(0);
235                 panic!("left consumer panic");
236             },
237             || {
238                 let mut right_folder = right_consumer.into_folder();
239                 right_folder = right_folder.consume(2);
240                 right_folder.complete() // early return
241             },
242         );
243         reducer.reduce(left_result, right_result)
244     });
245     unreachable!();
246 }
247 
248 // The right consumer panics and the left stops short, like `panic_fuse()`.
249 // We should get the right panic without finishing `collect_with_consumer`.
250 #[test]
251 #[should_panic(expected = "right consumer panic")]
right_panics()252 fn right_panics() {
253     let mut v = vec![];
254     collect_with_consumer(&mut v, 4, |consumer| {
255         let reducer = consumer.to_reducer();
256         let (left_consumer, right_consumer, _) = consumer.split_at(2);
257         let (left_result, right_result) = join(
258             || {
259                 let mut left_folder = left_consumer.into_folder();
260                 left_folder = left_folder.consume(0);
261                 left_folder.complete() // early return
262             },
263             || {
264                 let mut right_folder = right_consumer.into_folder();
265                 right_folder = right_folder.consume(2);
266                 panic!("right consumer panic");
267             },
268         );
269         reducer.reduce(left_result, right_result)
270     });
271     unreachable!();
272 }
273 
274 // The left consumer produces fewer items while the right
275 // consumer produces correct number; check that created elements are dropped
276 #[test]
277 #[cfg_attr(not(panic = "unwind"), ignore)]
left_produces_fewer_items_drops()278 fn left_produces_fewer_items_drops() {
279     let counter = DropCounter::default();
280     let mut v = vec![];
281     let panic_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
282         collect_with_consumer(&mut v, 4, |consumer| {
283             let reducer = consumer.to_reducer();
284             let (left_consumer, right_consumer, _) = consumer.split_at(2);
285             let mut left_folder = left_consumer.into_folder();
286             let mut right_folder = right_consumer.into_folder();
287             left_folder = left_folder.consume(counter.element());
288             right_folder = right_folder
289                 .consume(counter.element())
290                 .consume(counter.element());
291             let left_result = left_folder.complete();
292             let right_result = right_folder.complete();
293             reducer.reduce(left_result, right_result)
294         });
295     }));
296     assert!(v.is_empty());
297     assert_is_panic_with_message(&panic_result, "expected 4 total writes, but got 1");
298     counter.assert_drop_count();
299 }
300 
301 /// This counter can create elements, and then count and verify
302 /// the number of which have actually been dropped again.
303 #[derive(Default)]
304 struct DropCounter {
305     created: AtomicUsize,
306     dropped: AtomicUsize,
307 }
308 
309 struct Element<'a>(&'a AtomicUsize);
310 
311 impl DropCounter {
created(&self) -> usize312     fn created(&self) -> usize {
313         self.created.load(Ordering::SeqCst)
314     }
315 
dropped(&self) -> usize316     fn dropped(&self) -> usize {
317         self.dropped.load(Ordering::SeqCst)
318     }
319 
element(&self) -> Element<'_>320     fn element(&self) -> Element<'_> {
321         self.created.fetch_add(1, Ordering::SeqCst);
322         Element(&self.dropped)
323     }
324 
assert_drop_count(&self)325     fn assert_drop_count(&self) {
326         assert_eq!(
327             self.created(),
328             self.dropped(),
329             "Expected {} dropped elements, but found {}",
330             self.created(),
331             self.dropped()
332         );
333     }
334 }
335 
336 impl<'a> Drop for Element<'a> {
drop(&mut self)337     fn drop(&mut self) {
338         self.0.fetch_add(1, Ordering::SeqCst);
339     }
340 }
341 
342 /// Assert that the result from catch_unwind is a panic that contains expected message
assert_is_panic_with_message<T>(result: &ThreadResult<T>, expected: &str) where T: fmt::Debug,343 fn assert_is_panic_with_message<T>(result: &ThreadResult<T>, expected: &str)
344 where
345     T: fmt::Debug,
346 {
347     match result {
348         Ok(value) => {
349             panic!(
350                 "assertion failure: Expected panic, got successful {:?}",
351                 value
352             );
353         }
354         Err(error) => {
355             let message_str = error.downcast_ref::<&'static str>().cloned();
356             let message_string = error.downcast_ref::<String>().map(String::as_str);
357             if let Some(message) = message_str.or(message_string) {
358                 if !message.contains(expected) {
359                     panic!(
360                         "assertion failure: Expected {:?}, but found panic with {:?}",
361                         expected, message
362                     );
363                 }
364             // assertion passes
365             } else {
366                 panic!(
367                     "assertion failure: Expected {:?}, but found panic with unknown value",
368                     expected
369                 );
370             }
371         }
372     }
373 }
374