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" )] |
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" )] |
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" )] |
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" )] |
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)] |
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" )] |
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" )] |
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" )] |
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" )] |
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" )] |
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" )] |
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" )] |
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" )] |
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" )] |
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)] |
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 { |
312 | fn created(&self) -> usize { |
313 | self.created.load(Ordering::SeqCst) |
314 | } |
315 | |
316 | fn dropped(&self) -> usize { |
317 | self.dropped.load(Ordering::SeqCst) |
318 | } |
319 | |
320 | fn element(&self) -> Element<'_> { |
321 | self.created.fetch_add(1, Ordering::SeqCst); |
322 | Element(&self.dropped) |
323 | } |
324 | |
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> { |
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 |
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 | |