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