Просмотр исходного кода

system: add unit tests for CondVar and StoppableTask like a good boi

x 2 лет назад
Родитель
Сommit
902ec7bbf1
2 измененных файлов с 116 добавлено и 8 удалено
  1. 61 0
      src/system/condvar.rs
  2. 55 8
      src/system/stoppable_task.rs

+ 61 - 0
src/system/condvar.rs

@@ -108,3 +108,64 @@ impl<'a> Future for CondVarWait<'a> {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use smol::Executor;
+    use std::sync::Arc;
+
+    #[test]
+    fn condvar_test() {
+        let executor = Arc::new(Executor::new());
+        let executor_ = executor.clone();
+        smol::block_on(executor.run(async move {
+            let cv = Arc::new(CondVar::new());
+
+            let cv_ = cv.clone();
+            executor_
+                .spawn(async move {
+                    // Waits here until notify() is called
+                    cv_.wait().await;
+                })
+                .detach();
+
+            // Allow above code to continue
+            cv.notify();
+        }))
+    }
+
+    #[test]
+    fn condvar_reset() {
+        let executor = Arc::new(Executor::new());
+        let executor_ = executor.clone();
+        smol::block_on(executor.run(async move {
+            let cv = Arc::new(CondVar::new());
+
+            let cv_ = cv.clone();
+            executor_
+                .spawn(async move {
+                    cv_.wait().await;
+                })
+                .detach();
+
+            // #1 send signal
+            cv.notify();
+            // Multiple calls to notify do nothing until we call reset()
+            cv.notify();
+
+            // Without calling reset(), then the wait() will return instantly
+            cv.reset();
+
+            let cv_ = cv.clone();
+            executor_
+                .spawn(async move {
+                    cv_.wait().await;
+                })
+                .detach();
+
+            // #2 send signal again
+            cv.notify();
+        }))
+    }
+}

+ 55 - 8
src/system/stoppable_task.rs

@@ -57,14 +57,6 @@ impl StoppableTask {
         Arc::new(Self { signal: CondVar::new(), barrier: CondVar::new(), task_id: OsRng.gen() })
     }
 
-    /// Stops the task. On completion, guarantees the process has stopped.
-    pub async fn stop(&self) {
-        trace!(target: "system::StoppableTask", "Stopping task {}", self.task_id);
-        self.signal.notify();
-        self.barrier.wait().await;
-        trace!(target: "system::StoppableTask", "Stopped task {}", self.task_id);
-    }
-
     /// Starts the task.
     ///
     /// * `main` is a function of the type `async fn foo() -> ()`
@@ -118,6 +110,14 @@ impl StoppableTask {
             })
             .detach();
     }
+
+    /// Stops the task. On completion, guarantees the process has stopped.
+    pub async fn stop(&self) {
+        trace!(target: "system::StoppableTask", "Stopping task {}", self.task_id);
+        self.signal.notify();
+        self.barrier.wait().await;
+        trace!(target: "system::StoppableTask", "Stopped task {}", self.task_id);
+    }
 }
 
 impl std::hash::Hash for StoppableTask {
@@ -136,3 +136,50 @@ impl std::cmp::PartialEq for StoppableTask {
 }
 
 impl std::cmp::Eq for StoppableTask {}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{error::Error, system::sleep_forever};
+    use smol::Executor;
+    use std::sync::Arc;
+
+    #[test]
+    fn stoppit_mom() {
+        let mut cfg = simplelog::ConfigBuilder::new();
+        cfg.add_filter_ignore("async_io".to_string());
+        cfg.add_filter_ignore("polling".to_string());
+        simplelog::TermLogger::init(
+            simplelog::LevelFilter::Trace,
+            cfg.build(),
+            simplelog::TerminalMode::Mixed,
+            simplelog::ColorChoice::Auto,
+        )
+        .unwrap();
+
+        let executor = Arc::new(Executor::new());
+        let executor_ = executor.clone();
+        smol::block_on(executor.run(async move {
+            let task = StoppableTask::new();
+            task.clone().start(
+                // Main process is an infinite loop
+                async {
+                    sleep_forever().await;
+                    unreachable!()
+                },
+                // Handle stop
+                |result| async move {
+                    assert!(result.is_err());
+                    let is_correct_err = match result {
+                        Err(Error::DetachedTaskStopped) => true,
+                        _ => false,
+                    };
+                    assert!(is_correct_err);
+                },
+                Error::DetachedTaskStopped,
+                executor_,
+            );
+            task.stop().await;
+        }))
+    }
+}