1use lazy_static::lazy_static;
4use std::collections::HashMap;
5use std::sync::{Arc, Condvar, Mutex, PoisonError};
6pub use test_deps_if::deps;
7
8lazy_static! {
9 static ref TEST_DEPS_REG: Mutex<(HashMap<String, ()>, HashMap<String, Vec<Arc<Condvar>>>)> = {
10 let completed = HashMap::new();
11 let waitlist = HashMap::new();
12 Mutex::new((completed, waitlist))
13 };
14}
15
16#[doc(hidden)]
17#[derive(Debug)]
18pub enum TestDepsHelperError {
19 ThreadSync(String),
20 CorruptedDeps(String),
21 Generic(String),
22}
23
24impl<T> From<PoisonError<T>> for TestDepsHelperError {
25 fn from(_err: PoisonError<T>) -> TestDepsHelperError {
26 TestDepsHelperError::ThreadSync(String::from("A mutex is poisoned"))
27 }
28}
29
30#[doc(hidden)]
31pub fn target_completed(target: &String) -> Result<(), TestDepsHelperError> {
32 let mut reg = TEST_DEPS_REG.lock()?;
33 if reg.0.insert(target.clone(), ()).is_some() {
34 Err(TestDepsHelperError::CorruptedDeps(format!(
35 "A target duplicated on the completed list: {}",
36 target
37 )))
38 } else {
39 if let Some(cvs) = reg.1.get(target) {
40 for cv in cvs {
41 cv.notify_one();
42 }
43 }
44 Ok(())
45 }
46}
47
48#[doc(hidden)]
49pub fn ensure_prereqs(prereqs: &Vec<String>) -> Result<(), TestDepsHelperError> {
50 let mut reg = TEST_DEPS_REG.lock()?;
51 if !check_readiness(®.0, prereqs) {
52 let cv = Arc::new(Condvar::new());
53 for prereq in prereqs {
54 if let Some(cvs) = reg.1.get_mut(prereq) {
55 cvs.push(cv.clone());
56 } else {
57 reg.1.insert(prereq.clone(), vec![cv.clone()]);
58 }
59 }
60 while !check_readiness(®.0, prereqs) {
61 reg = cv.wait(reg)?;
62 }
63 }
64 Ok(())
65}
66
67fn check_readiness(completed: &HashMap<String, ()>, prereqs: &Vec<String>) -> bool {
68 for prereq in prereqs {
69 if !completed.contains_key(prereq) {
70 return false;
71 }
72 }
73 true
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use std::{thread, time};
80
81 #[test]
82 fn target_completed_no_one_is_waiting_for() {
83 target_completed(&String::from("NO_ONE_IS_WAITING")).unwrap();
84 }
85
86 #[test]
87 fn no_prereq_passes_immediately() {
88 ensure_prereqs(&vec![]).unwrap();
89 }
90
91 #[test]
92 fn wait_one_prereq() {
93 ensure_prereqs(&vec![String::from("FIRST_TARGET")]).unwrap();
94 }
95
96 #[test]
97 fn wait_two_prereqs() {
98 ensure_prereqs(&vec![
99 String::from("FIRST_TARGET"),
100 String::from("SECOND_TARGET"),
101 ])
102 .unwrap();
103 }
104
105 #[test]
106 fn first_target_completed() {
107 thread::sleep(time::Duration::from_secs_f64(0.75));
109 target_completed(&String::from("FIRST_TARGET")).unwrap();
110 }
111
112 #[test]
113 fn second_target_completed() {
114 thread::sleep(time::Duration::from_secs_f64(0.75));
116 target_completed(&String::from("SECOND_TARGET")).unwrap();
117 }
118
119 #[test]
120 fn prereqs_already_satisfied() {
121 thread::sleep(time::Duration::from_secs_f64(0.75));
123 ensure_prereqs(&vec![String::from("TARGET_GOING_FIRST")]).unwrap();
124 }
125
126 #[test]
127 fn target_completed_before_waiter_comes() {
128 target_completed(&String::from("TARGET_GOING_FIRST")).unwrap();
129 }
130
131 #[test]
132 #[should_panic(expected = "A target duplicated on the completed list: COMPLETES_TWICE")]
133 fn target_completed_twice_unexpectedly() {
134 if let Err(_) = target_completed(&String::from("COMPLETES_TWICE")) {
135 panic!("should not panic here");
136 }
137 target_completed(&String::from("COMPLETES_TWICE")).unwrap();
138 }
139}