sabi/
async_group.rs

1// Copyright (C) 2024-2025 Takayuki Sato. All Rights Reserved.
2// This program is free software under MIT License.
3// See the file LICENSE in this distribution for more details.
4
5use std::collections::{HashMap, VecDeque};
6use std::future::Future;
7use std::pin::Pin;
8
9use errs::Err;
10use futures::future;
11use tokio::runtime;
12
13/// The enum type representing the reasons for errors that can occur within an `AsyncGroup`.
14#[derive(Debug)]
15pub enum AsyncGroupError {
16    /// Indicates a failure to create a new asynchronous runtime, which is necessary
17    /// for executing the tasks within the `AsyncGroup`.
18    FailToCreateAsyncRuntime,
19}
20
21/// The structure that allows for the asynchronous execution of multiple functions
22/// and waits for all of them to complete.
23///
24/// Functions are added using the `add` method and are then run concurrently.
25/// The `AsyncGroup` ensures that all tasks finish before proceeding,
26/// and can collect any errors that occur.
27pub struct AsyncGroup<'a> {
28    task_vec: VecDeque<Pin<Box<dyn Future<Output = Result<(), Err>> + Send + 'static>>>,
29    name_vec: VecDeque<String>,
30    pub(crate) name: &'a str,
31}
32
33impl<'a> AsyncGroup<'_> {
34    pub(crate) fn new() -> Self {
35        Self {
36            task_vec: VecDeque::new(),
37            name_vec: VecDeque::new(),
38            name: "",
39        }
40    }
41
42    /// Adds an asynchronous task (a future) to the group.
43    ///
44    /// This provided future is executed asynchronously with other added tasks,
45    /// awaiting completion and collecting errors internally.
46    ///
47    /// # Type Parameters
48    ///
49    /// * `Fut`: The type of the future, which must be
50    ///   `Future<Output = Result<(), Err>> + Send + 'static`.
51    ///
52    /// # Parameters
53    ///
54    /// * `future`: The future to be executed.
55    pub fn add<Fut>(&mut self, future: Fut)
56    where
57        Fut: Future<Output = Result<(), Err>> + Send + 'static,
58    {
59        self.task_vec.push_back(Box::pin(future));
60        self.name_vec.push_back(self.name.to_string());
61    }
62
63    pub(crate) fn join_and_collect_errors(mut self, err_map: &mut HashMap<String, Err>) {
64        match runtime::Runtime::new() {
65            Ok(rt) => {
66                rt.block_on(async {
67                    let result_all = future::join_all(self.task_vec).await;
68                    for result in result_all.into_iter() {
69                        if let Some(name) = self.name_vec.pop_front() {
70                            if let Err(err) = result {
71                                err_map.insert(name, err);
72                            }
73                        }
74                    }
75                });
76            }
77            Err(err) => {
78                err_map.insert(
79                    "sabi::AsyncGroup".to_string(),
80                    Err::with_source(AsyncGroupError::FailToCreateAsyncRuntime, err),
81                );
82            }
83        }
84    }
85
86    pub(crate) fn join_and_ignore_errors(self) {
87        match runtime::Runtime::new() {
88            Ok(rt) => {
89                rt.block_on(async {
90                    let _ = future::join_all(self.task_vec).await;
91                });
92            }
93            Err(err) => {
94                let _ = Err::with_source(AsyncGroupError::FailToCreateAsyncRuntime, err);
95            }
96        }
97    }
98
99    pub(crate) async fn join_and_collect_errors_async(
100        mut self,
101        err_map: &mut HashMap<String, Err>,
102    ) {
103        let result_all = future::join_all(self.task_vec).await;
104        for result in result_all.into_iter() {
105            if let Some(name) = self.name_vec.pop_front() {
106                if let Err(err) = result {
107                    err_map.insert(name, err);
108                }
109            }
110        }
111    }
112
113    pub(crate) async fn join_and_ignore_errors_async(self) {
114        let _ = future::join_all(self.task_vec).await;
115    }
116}
117
118#[cfg(test)]
119mod tests_of_async_group {
120    use super::*;
121    use std::sync::{Arc, Mutex};
122    use tokio::time;
123
124    const BASE_LINE: u32 = line!();
125
126    #[derive(Debug, PartialEq)]
127    enum Reasons {
128        BadFlag(bool),
129        BadString(String),
130        BadNumber(i64),
131    }
132
133    struct StructA {
134        flag: Arc<Mutex<bool>>,
135        will_fail: bool,
136    }
137
138    impl StructA {
139        fn new(will_fail: bool) -> Self {
140            Self {
141                flag: Arc::new(Mutex::new(false)),
142                will_fail,
143            }
144        }
145
146        fn process(&self, ag: &mut AsyncGroup) {
147            let flag_clone = self.flag.clone();
148            let will_fail = self.will_fail;
149            ag.add(async move {
150                // The `.await` must be executed outside the Mutex lock.
151                let _ = time::sleep(time::Duration::from_millis(100)).await;
152
153                {
154                    let mut flag = flag_clone.lock().unwrap();
155                    if will_fail {
156                        return Err(Err::new(Reasons::BadFlag(*flag)));
157                    }
158                    *flag = true;
159                }
160
161                Ok(())
162            });
163        }
164    }
165
166    struct StructB {
167        string: Arc<Mutex<String>>,
168        flag: Arc<Mutex<bool>>,
169        will_fail: bool,
170    }
171
172    impl StructB {
173        fn new(will_fail: bool) -> Self {
174            Self {
175                string: Arc::new(Mutex::new("-".to_string())),
176                flag: Arc::new(Mutex::new(false)),
177                will_fail,
178            }
179        }
180
181        fn process(&self, ag: &mut AsyncGroup) {
182            let string_clone = self.string.clone();
183            let flag_clone = self.flag.clone();
184            let will_fail = self.will_fail;
185            ag.add(async move {
186                // The `.await` must be executed outside the Mutex lock.
187                let _ = time::sleep(time::Duration::from_millis(300)).await;
188
189                {
190                    let mut string = string_clone.lock().unwrap();
191                    let mut flag = flag_clone.lock().unwrap();
192                    if will_fail {
193                        return Err(Err::new(Reasons::BadString(string.to_string())));
194                    }
195                    *flag = true;
196                    *string = "hello".to_string();
197                }
198                Ok(())
199            });
200        }
201    }
202
203    struct StructC {
204        number: Arc<Mutex<i64>>,
205        will_fail: bool,
206    }
207
208    impl StructC {
209        fn new(will_fail: bool) -> Self {
210            Self {
211                number: Arc::new(Mutex::new(123)),
212                will_fail,
213            }
214        }
215
216        fn process(&self, ag: &mut AsyncGroup) {
217            let number_clone = self.number.clone();
218            let will_fail = self.will_fail;
219            ag.add(async move {
220                // The `.await` must be executed outside the Mutex lock.
221                let _ = time::sleep(time::Duration::from_millis(50)).await;
222
223                {
224                    let mut number = number_clone.lock().unwrap();
225                    if will_fail {
226                        return Err(Err::new(Reasons::BadNumber(*number)));
227                    }
228                    *number = 987;
229                }
230                Ok(())
231            });
232        }
233    }
234
235    mod test_join_and_collect_errors {
236        use super::*;
237
238        #[test]
239        fn zero() {
240            let ag = AsyncGroup::new();
241            let mut m = HashMap::<String, Err>::new();
242
243            ag.join_and_collect_errors(&mut m);
244            assert_eq!(m.len(), 0);
245        }
246
247        #[test]
248        fn single_ok() {
249            let mut ag = AsyncGroup::new();
250            let mut m = HashMap::<String, Err>::new();
251
252            let struct_a = StructA::new(false);
253            assert_eq!(*struct_a.flag.lock().unwrap(), false);
254
255            ag.name = "foo";
256            struct_a.process(&mut ag);
257
258            ag.join_and_collect_errors(&mut m);
259            assert_eq!(m.len(), 0);
260            assert_eq!(*struct_a.flag.lock().unwrap(), true);
261        }
262
263        #[test]
264        fn single_fail() {
265            let mut ag = AsyncGroup::new();
266            let mut m = HashMap::<String, Err>::new();
267
268            let struct_a = StructA::new(true);
269            assert_eq!(*struct_a.flag.lock().unwrap(), false);
270
271            ag.name = "foo";
272            struct_a.process(&mut ag);
273
274            ag.join_and_collect_errors(&mut m);
275            assert_eq!(m.len(), 1);
276            assert_eq!(*struct_a.flag.lock().unwrap(), false);
277
278            #[cfg(unix)]
279            assert_eq!(
280                format!("{:?}", *(m.get("foo").unwrap())),
281                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }"
282            );
283            #[cfg(windows)]
284            assert_eq!(
285                format!("{:?}", *(m.get("foo").unwrap())),
286                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }"
287            );
288        }
289
290        #[test]
291        fn multiple_ok() {
292            let mut ag = AsyncGroup::new();
293            let mut m = HashMap::<String, Err>::new();
294
295            let struct_a = StructA::new(false);
296            assert_eq!(*struct_a.flag.lock().unwrap(), false);
297
298            let struct_b = StructB::new(false);
299            assert_eq!(*struct_b.flag.lock().unwrap(), false);
300            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
301
302            let struct_c = StructC::new(false);
303            assert_eq!(*struct_c.number.lock().unwrap(), 123);
304
305            ag.name = "foo";
306            struct_a.process(&mut ag);
307
308            ag.name = "bar";
309            struct_b.process(&mut ag);
310
311            ag.name = "baz";
312            struct_c.process(&mut ag);
313
314            ag.join_and_collect_errors(&mut m);
315            assert_eq!(m.len(), 0);
316            assert_eq!(*struct_a.flag.lock().unwrap(), true);
317            assert_eq!(*struct_b.flag.lock().unwrap(), true);
318            assert_eq!(*struct_b.string.lock().unwrap(), "hello".to_string());
319            assert_eq!(*struct_c.number.lock().unwrap(), 987);
320        }
321
322        #[test]
323        fn multiple_processes_and_single_fail() {
324            let mut ag = AsyncGroup::new();
325            let mut m = HashMap::<String, Err>::new();
326
327            let struct_a = StructA::new(false);
328            assert_eq!(*struct_a.flag.lock().unwrap(), false);
329
330            let struct_b = StructB::new(true);
331            assert_eq!(*struct_b.flag.lock().unwrap(), false);
332            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
333
334            let struct_c = StructC::new(false);
335            assert_eq!(*struct_c.number.lock().unwrap(), 123);
336
337            ag.name = "foo";
338            struct_a.process(&mut ag);
339
340            ag.name = "bar";
341            struct_b.process(&mut ag);
342
343            ag.name = "baz";
344            struct_c.process(&mut ag);
345
346            ag.join_and_collect_errors(&mut m);
347            assert_eq!(m.len(), 1);
348
349            #[cfg(unix)]
350            assert_eq!(
351                format!("{:?}", *(m.get("bar").unwrap())),
352                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
353            );
354            #[cfg(windows)]
355            assert_eq!(
356                format!("{:?}", *(m.get("bar").unwrap())),
357                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
358            );
359
360            assert_eq!(*struct_a.flag.lock().unwrap(), true);
361            assert_eq!(*struct_b.flag.lock().unwrap(), false);
362            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
363            assert_eq!(*struct_c.number.lock().unwrap(), 987);
364        }
365
366        #[test]
367        fn multiple_fail() {
368            let mut ag = AsyncGroup::new();
369            let mut m = HashMap::<String, Err>::new();
370
371            let struct_a = StructA::new(true);
372            assert_eq!(*struct_a.flag.lock().unwrap(), false);
373
374            let struct_b = StructB::new(true);
375            assert_eq!(*struct_b.flag.lock().unwrap(), false);
376            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
377
378            let struct_c = StructC::new(true);
379            assert_eq!(*struct_c.number.lock().unwrap(), 123);
380
381            ag.name = "foo";
382            struct_a.process(&mut ag);
383
384            ag.name = "bar";
385            struct_b.process(&mut ag);
386
387            ag.name = "baz";
388            struct_c.process(&mut ag);
389
390            ag.join_and_collect_errors(&mut m);
391            assert_eq!(m.len(), 3);
392
393            #[cfg(unix)]
394            assert_eq!(
395                format!("{:?}", *(m.get("foo").unwrap())),
396                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }",
397            );
398            #[cfg(windows)]
399            assert_eq!(
400                format!("{:?}", *(m.get("foo").unwrap())),
401                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }",
402            );
403            #[cfg(unix)]
404            assert_eq!(
405                format!("{:?}", *(m.get("bar").unwrap())),
406                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
407            );
408            #[cfg(windows)]
409            assert_eq!(
410                format!("{:?}", *(m.get("bar").unwrap())),
411                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
412            );
413            #[cfg(unix)]
414            assert_eq!(
415                format!("{:?}", *(m.get("baz").unwrap())),
416                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadNumber(123), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 102).to_string() + " }",
417            );
418            #[cfg(windows)]
419            assert_eq!(
420                format!("{:?}", *(m.get("baz").unwrap())),
421                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadNumber(123), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 102).to_string() + " }",
422            );
423
424            assert_eq!(*struct_a.flag.lock().unwrap(), false);
425            assert_eq!(*struct_b.flag.lock().unwrap(), false);
426            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
427            assert_eq!(*struct_c.number.lock().unwrap(), 123);
428        }
429    }
430
431    mod test_join_and_ignore_errors {
432        use super::*;
433
434        #[test]
435        fn zero() {
436            let ag = AsyncGroup::new();
437
438            ag.join_and_ignore_errors();
439        }
440
441        #[test]
442        fn single_ok() {
443            let mut ag = AsyncGroup::new();
444
445            let struct_a = StructA::new(false);
446            assert_eq!(*struct_a.flag.lock().unwrap(), false);
447
448            ag.name = "foo";
449            struct_a.process(&mut ag);
450
451            ag.join_and_ignore_errors();
452            assert_eq!(*struct_a.flag.lock().unwrap(), true);
453        }
454
455        #[test]
456        fn single_fail() {
457            let mut ag = AsyncGroup::new();
458
459            let struct_a = StructA::new(true);
460            assert_eq!(*struct_a.flag.lock().unwrap(), false);
461
462            ag.name = "foo";
463            struct_a.process(&mut ag);
464
465            ag.join_and_ignore_errors();
466            assert_eq!(*struct_a.flag.lock().unwrap(), false);
467        }
468
469        #[test]
470        fn multiple_ok() {
471            let mut ag = AsyncGroup::new();
472
473            let struct_a = StructA::new(false);
474            assert_eq!(*struct_a.flag.lock().unwrap(), false);
475
476            let struct_b = StructB::new(false);
477            assert_eq!(*struct_b.flag.lock().unwrap(), false);
478            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
479
480            let struct_c = StructC::new(false);
481            assert_eq!(*struct_c.number.lock().unwrap(), 123);
482
483            ag.name = "foo";
484            struct_a.process(&mut ag);
485
486            ag.name = "bar";
487            struct_b.process(&mut ag);
488
489            ag.name = "baz";
490            struct_c.process(&mut ag);
491
492            ag.join_and_ignore_errors();
493
494            assert_eq!(*struct_a.flag.lock().unwrap(), true);
495            assert_eq!(*struct_b.flag.lock().unwrap(), true);
496            assert_eq!(*struct_b.string.lock().unwrap(), "hello".to_string());
497            assert_eq!(*struct_c.number.lock().unwrap(), 987);
498        }
499
500        #[test]
501        fn multiple_processes_and_single_fail() {
502            let mut ag = AsyncGroup::new();
503
504            let struct_a = StructA::new(false);
505            assert_eq!(*struct_a.flag.lock().unwrap(), false);
506
507            let struct_b = StructB::new(true);
508            assert_eq!(*struct_b.flag.lock().unwrap(), false);
509            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
510
511            let struct_c = StructC::new(false);
512            assert_eq!(*struct_c.number.lock().unwrap(), 123);
513
514            ag.name = "foo";
515            struct_a.process(&mut ag);
516
517            ag.name = "bar";
518            struct_b.process(&mut ag);
519
520            ag.name = "baz";
521            struct_c.process(&mut ag);
522
523            ag.join_and_ignore_errors();
524
525            assert_eq!(*struct_a.flag.lock().unwrap(), true);
526            assert_eq!(*struct_b.flag.lock().unwrap(), false);
527            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
528            assert_eq!(*struct_c.number.lock().unwrap(), 987);
529        }
530
531        #[test]
532        fn multiple_fail() {
533            let mut ag = AsyncGroup::new();
534
535            let struct_a = StructA::new(true);
536            assert_eq!(*struct_a.flag.lock().unwrap(), false);
537
538            let struct_b = StructB::new(true);
539            assert_eq!(*struct_b.flag.lock().unwrap(), false);
540            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
541
542            let struct_c = StructC::new(true);
543            assert_eq!(*struct_c.number.lock().unwrap(), 123);
544
545            ag.name = "foo";
546            struct_a.process(&mut ag);
547
548            ag.name = "bar";
549            struct_b.process(&mut ag);
550
551            ag.name = "baz";
552            struct_c.process(&mut ag);
553
554            ag.join_and_ignore_errors();
555
556            assert_eq!(*struct_a.flag.lock().unwrap(), false);
557            assert_eq!(*struct_b.flag.lock().unwrap(), false);
558            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
559            assert_eq!(*struct_c.number.lock().unwrap(), 123);
560        }
561    }
562
563    mod test_join_and_collect_errors_async {
564        use super::*;
565
566        #[tokio::test]
567        async fn zero() {
568            let ag = AsyncGroup::new();
569            let mut m = HashMap::<String, Err>::new();
570
571            ag.join_and_collect_errors_async(&mut m).await;
572            assert_eq!(m.len(), 0);
573        }
574
575        #[tokio::test]
576        async fn single_ok() {
577            let mut ag = AsyncGroup::new();
578            let mut m = HashMap::<String, Err>::new();
579
580            let struct_a = StructA::new(false);
581            assert_eq!(*struct_a.flag.lock().unwrap(), false);
582
583            ag.name = "foo";
584            struct_a.process(&mut ag);
585
586            ag.join_and_collect_errors_async(&mut m).await;
587            assert_eq!(m.len(), 0);
588            assert_eq!(*struct_a.flag.lock().unwrap(), true);
589        }
590
591        #[tokio::test]
592        async fn single_fail() {
593            let mut ag = AsyncGroup::new();
594            let mut m = HashMap::<String, Err>::new();
595
596            let struct_a = StructA::new(true);
597            assert_eq!(*struct_a.flag.lock().unwrap(), false);
598
599            ag.name = "foo";
600            struct_a.process(&mut ag);
601
602            ag.join_and_collect_errors_async(&mut m).await;
603            assert_eq!(m.len(), 1);
604            assert_eq!(*struct_a.flag.lock().unwrap(), false);
605
606            #[cfg(unix)]
607            assert_eq!(
608                format!("{:?}", *(m.get("foo").unwrap())),
609                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }"
610            );
611            #[cfg(windows)]
612            assert_eq!(
613                format!("{:?}", *(m.get("foo").unwrap())),
614                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }"
615            );
616        }
617
618        #[tokio::test]
619        async fn multiple_ok() {
620            let mut ag = AsyncGroup::new();
621            let mut m = HashMap::<String, Err>::new();
622
623            let struct_a = StructA::new(false);
624            assert_eq!(*struct_a.flag.lock().unwrap(), false);
625
626            let struct_b = StructB::new(false);
627            assert_eq!(*struct_b.flag.lock().unwrap(), false);
628            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
629
630            let struct_c = StructC::new(false);
631            assert_eq!(*struct_c.number.lock().unwrap(), 123);
632
633            ag.name = "foo";
634            struct_a.process(&mut ag);
635
636            ag.name = "bar";
637            struct_b.process(&mut ag);
638
639            ag.name = "baz";
640            struct_c.process(&mut ag);
641
642            ag.join_and_collect_errors_async(&mut m).await;
643            assert_eq!(m.len(), 0);
644            assert_eq!(*struct_a.flag.lock().unwrap(), true);
645            assert_eq!(*struct_b.flag.lock().unwrap(), true);
646            assert_eq!(*struct_b.string.lock().unwrap(), "hello".to_string());
647            assert_eq!(*struct_c.number.lock().unwrap(), 987);
648        }
649
650        #[tokio::test]
651        async fn multiple_processes_and_single_fail() {
652            let mut ag = AsyncGroup::new();
653            let mut m = HashMap::<String, Err>::new();
654
655            let struct_a = StructA::new(false);
656            assert_eq!(*struct_a.flag.lock().unwrap(), false);
657
658            let struct_b = StructB::new(true);
659            assert_eq!(*struct_b.flag.lock().unwrap(), false);
660            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
661
662            let struct_c = StructC::new(false);
663            assert_eq!(*struct_c.number.lock().unwrap(), 123);
664
665            ag.name = "foo";
666            struct_a.process(&mut ag);
667
668            ag.name = "bar";
669            struct_b.process(&mut ag);
670
671            ag.name = "baz";
672            struct_c.process(&mut ag);
673
674            ag.join_and_collect_errors_async(&mut m).await;
675            assert_eq!(m.len(), 1);
676
677            #[cfg(unix)]
678            assert_eq!(
679                format!("{:?}", *(m.get("bar").unwrap())),
680                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
681            );
682            #[cfg(windows)]
683            assert_eq!(
684                format!("{:?}", *(m.get("bar").unwrap())),
685                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
686            );
687
688            assert_eq!(*struct_a.flag.lock().unwrap(), true);
689            assert_eq!(*struct_b.flag.lock().unwrap(), false);
690            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
691            assert_eq!(*struct_c.number.lock().unwrap(), 987);
692        }
693
694        #[tokio::test]
695        async fn multiple_fail() {
696            let mut ag = AsyncGroup::new();
697            let mut m = HashMap::<String, Err>::new();
698
699            let struct_a = StructA::new(true);
700            assert_eq!(*struct_a.flag.lock().unwrap(), false);
701
702            let struct_b = StructB::new(true);
703            assert_eq!(*struct_b.flag.lock().unwrap(), false);
704            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
705
706            let struct_c = StructC::new(true);
707            assert_eq!(*struct_c.number.lock().unwrap(), 123);
708
709            ag.name = "foo";
710            struct_a.process(&mut ag);
711
712            ag.name = "bar";
713            struct_b.process(&mut ag);
714
715            ag.name = "baz";
716            struct_c.process(&mut ag);
717
718            ag.join_and_collect_errors_async(&mut m).await;
719            assert_eq!(m.len(), 3);
720
721            #[cfg(unix)]
722            assert_eq!(
723                format!("{:?}", *(m.get("foo").unwrap())),
724                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }",
725            );
726            #[cfg(windows)]
727            assert_eq!(
728                format!("{:?}", *(m.get("foo").unwrap())),
729                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadFlag(false), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 32).to_string() + " }",
730            );
731            #[cfg(unix)]
732            assert_eq!(
733                format!("{:?}", *(m.get("bar").unwrap())),
734                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
735            );
736            #[cfg(windows)]
737            assert_eq!(
738                format!("{:?}", *(m.get("bar").unwrap())),
739                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadString(\"-\"), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 69).to_string() + " }",
740            );
741            #[cfg(unix)]
742            assert_eq!(
743                format!("{:?}", *(m.get("baz").unwrap())),
744                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadNumber(123), file = src/async_group.rs, line = ".to_string() + &(BASE_LINE + 102).to_string() + " }",
745            );
746            #[cfg(windows)]
747            assert_eq!(
748                format!("{:?}", *(m.get("baz").unwrap())),
749                "errs::Err { reason = sabi::async_group::tests_of_async_group::Reasons BadNumber(123), file = src\\async_group.rs, line = ".to_string() + &(BASE_LINE + 102).to_string() + " }",
750            );
751
752            assert_eq!(*struct_a.flag.lock().unwrap(), false);
753            assert_eq!(*struct_b.flag.lock().unwrap(), false);
754            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
755            assert_eq!(*struct_c.number.lock().unwrap(), 123);
756        }
757    }
758
759    mod test_join_and_ignore_errors_async {
760        use super::*;
761
762        #[tokio::test]
763        async fn zero() {
764            let ag = AsyncGroup::new();
765
766            ag.join_and_ignore_errors_async().await;
767        }
768
769        #[tokio::test]
770        async fn single_ok() {
771            let mut ag = AsyncGroup::new();
772
773            let struct_a = StructA::new(false);
774            assert_eq!(*struct_a.flag.lock().unwrap(), false);
775
776            ag.name = "foo";
777            struct_a.process(&mut ag);
778
779            ag.join_and_ignore_errors_async().await;
780            assert_eq!(*struct_a.flag.lock().unwrap(), true);
781        }
782
783        #[tokio::test]
784        async fn single_fail() {
785            let mut ag = AsyncGroup::new();
786
787            let struct_a = StructA::new(true);
788            assert_eq!(*struct_a.flag.lock().unwrap(), false);
789
790            ag.name = "foo";
791            struct_a.process(&mut ag);
792
793            ag.join_and_ignore_errors_async().await;
794            assert_eq!(*struct_a.flag.lock().unwrap(), false);
795        }
796
797        #[tokio::test]
798        async fn multiple_ok() {
799            let mut ag = AsyncGroup::new();
800
801            let struct_a = StructA::new(false);
802            assert_eq!(*struct_a.flag.lock().unwrap(), false);
803
804            let struct_b = StructB::new(false);
805            assert_eq!(*struct_b.flag.lock().unwrap(), false);
806            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
807
808            let struct_c = StructC::new(false);
809            assert_eq!(*struct_c.number.lock().unwrap(), 123);
810
811            ag.name = "foo";
812            struct_a.process(&mut ag);
813
814            ag.name = "bar";
815            struct_b.process(&mut ag);
816
817            ag.name = "baz";
818            struct_c.process(&mut ag);
819
820            ag.join_and_ignore_errors_async().await;
821
822            assert_eq!(*struct_a.flag.lock().unwrap(), true);
823            assert_eq!(*struct_b.flag.lock().unwrap(), true);
824            assert_eq!(*struct_b.string.lock().unwrap(), "hello".to_string());
825            assert_eq!(*struct_c.number.lock().unwrap(), 987);
826        }
827
828        #[tokio::test]
829        async fn multiple_processes_and_single_fail() {
830            let mut ag = AsyncGroup::new();
831
832            let struct_a = StructA::new(false);
833            assert_eq!(*struct_a.flag.lock().unwrap(), false);
834
835            let struct_b = StructB::new(true);
836            assert_eq!(*struct_b.flag.lock().unwrap(), false);
837            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
838
839            let struct_c = StructC::new(false);
840            assert_eq!(*struct_c.number.lock().unwrap(), 123);
841
842            ag.name = "foo";
843            struct_a.process(&mut ag);
844
845            ag.name = "bar";
846            struct_b.process(&mut ag);
847
848            ag.name = "baz";
849            struct_c.process(&mut ag);
850
851            ag.join_and_ignore_errors_async().await;
852
853            assert_eq!(*struct_a.flag.lock().unwrap(), true);
854            assert_eq!(*struct_b.flag.lock().unwrap(), false);
855            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
856            assert_eq!(*struct_c.number.lock().unwrap(), 987);
857        }
858
859        #[tokio::test]
860        async fn multiple_fail() {
861            let mut ag = AsyncGroup::new();
862
863            let struct_a = StructA::new(true);
864            assert_eq!(*struct_a.flag.lock().unwrap(), false);
865
866            let struct_b = StructB::new(true);
867            assert_eq!(*struct_b.flag.lock().unwrap(), false);
868            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
869
870            let struct_c = StructC::new(true);
871            assert_eq!(*struct_c.number.lock().unwrap(), 123);
872
873            ag.name = "foo";
874            struct_a.process(&mut ag);
875
876            ag.name = "bar";
877            struct_b.process(&mut ag);
878
879            ag.name = "baz";
880            struct_c.process(&mut ag);
881
882            ag.join_and_ignore_errors_async().await;
883
884            assert_eq!(*struct_a.flag.lock().unwrap(), false);
885            assert_eq!(*struct_b.flag.lock().unwrap(), false);
886            assert_eq!(*struct_b.string.lock().unwrap(), "-".to_string());
887            assert_eq!(*struct_c.number.lock().unwrap(), 123);
888        }
889    }
890}