From 7f8125dc6874da8aacefe602c23e5272a3b83eb4 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 13:43:22 +0200 Subject: [PATCH 01/13] Enable logs in tests --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 07e8143..f3de681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ miette = { version = "5.10.0", features = ["fancy"] } # Logging tracing-subscriber = "0.3.17" -tracing-test = "0.2.4" +tracing-test = { version = "0.2.4", features = ["no-env-filter"] } # Tokio tokio = { version = "1.32.0", features = ["full"] } From 2e0e050ca80cb96cc4bbb1e4baedd060bccc706d Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 14:01:05 +0200 Subject: [PATCH 02/13] Increase coverage of alive_guard --- src/runner/alive_guard.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/runner/alive_guard.rs b/src/runner/alive_guard.rs index 00cfddf..4d22fe9 100644 --- a/src/runner/alive_guard.rs +++ b/src/runner/alive_guard.rs @@ -62,10 +62,12 @@ impl Drop for Inner { mod tests { use std::sync::atomic::{AtomicU32, Ordering}; + use tracing_test::traced_test; use super::*; #[test] + #[traced_test] fn finished_callback() { let alive_guard = AliveGuard::new(); @@ -82,6 +84,7 @@ mod tests { } #[test] + #[traced_test] fn cancel_callback() { let alive_guard = AliveGuard::new(); @@ -99,6 +102,7 @@ mod tests { } #[test] + #[traced_test] fn both_callbacks() { let alive_guard = AliveGuard::new(); @@ -117,4 +121,13 @@ mod tests { assert_eq!(counter.load(Ordering::Relaxed), 2); } + + #[test] + #[traced_test] + fn no_callback() { + let alive_guard = AliveGuard::new(); + drop(alive_guard); + + assert!(logs_contain("No `finished` callback was registered in AliveGuard! This should not happen, please report this at https://github.com/Finomnis/tokio-graceful-shutdown/issues.")); + } } From 1df838a49647295e3c470586e210897b487aa5d1 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 14:25:06 +0200 Subject: [PATCH 03/13] Add tests for error_collector --- src/subsystem/error_collector.rs | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/subsystem/error_collector.rs b/src/subsystem/error_collector.rs index 93208b0..b7da95c 100644 --- a/src/subsystem/error_collector.rs +++ b/src/subsystem/error_collector.rs @@ -41,3 +41,76 @@ impl Drop for ErrorCollector { } } } + +#[cfg(test)] +mod tests { + + use tracing_test::traced_test; + + use super::*; + + #[test] + #[traced_test] + fn normal() { + let (sender, receiver) = mpsc::unbounded_channel(); + let mut error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); + } + + #[test] + #[traced_test] + fn double_finish() { + let (sender, receiver) = mpsc::unbounded_channel(); + let mut error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); + } + + #[test] + #[traced_test] + fn no_finish() { + let (sender, receiver) = mpsc::unbounded_channel(); + let error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + drop(error_collector); + + assert!(logs_contain("An error got dropped: Panicked(\"ABC\")")); + assert!(logs_contain("An error got dropped: Panicked(\"def\")")); + } +} From 33de75b776442ebe6ddfcddd3c0fab1d2fcb05c4 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 14:35:53 +0200 Subject: [PATCH 04/13] Attempt to fix traced_test incorrect coverage error --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f3de681..8f0d00c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ miette = { version = "5.10.0", features = ["fancy"] } # Logging tracing-subscriber = "0.3.17" -tracing-test = { version = "0.2.4", features = ["no-env-filter"] } +tracing-test = { version = "0.2.4" } # Tokio tokio = { version = "1.32.0", features = ["full"] } From 0c254f159b16564ce9292e25c887a618826afd64 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 15:04:56 +0200 Subject: [PATCH 05/13] Attempt to fix tracing-test coverage --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8f0d00c..0a05c2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,9 @@ miette = { version = "5.10.0", features = ["fancy"] } # Logging tracing-subscriber = "0.3.17" -tracing-test = { version = "0.2.4" } +tracing-test = { version = "0.2.4", features = [ + "no-env-filter", +], git = "https://github.com/Finomnis/tracing-test.git", branch = "fix_coverage" } # Tokio tokio = { version = "1.32.0", features = ["full"] } From d5a33eedc644b920d2423e3e76f89549421a44ad Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 15:53:28 +0200 Subject: [PATCH 06/13] Retrigger converage CI --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0a05c2a..eee7d9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ miette = { version = "5.10.0", features = ["fancy"] } tracing-subscriber = "0.3.17" tracing-test = { version = "0.2.4", features = [ "no-env-filter", -], git = "https://github.com/Finomnis/tracing-test.git", branch = "fix_coverage" } +], branch = "fix_coverage", git = "https://github.com/Finomnis/tracing-test.git" } # Tokio tokio = { version = "1.32.0", features = ["full"] } From 42cf60170b2bb3e4f4cdfb9966af799a851ef7f9 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 23:25:11 +0200 Subject: [PATCH 07/13] Another attempt to fix traced_tests --- Cargo.toml | 1 + src/lib.rs | 2 ++ tests/cancel_on_shutdown.rs | 3 +++ tests/integration_test.rs | 3 +++ 4 files changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index eee7d9a..4bae2d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ miette = { version = "5.10.0", features = ["fancy"] } tracing-subscriber = "0.3.17" tracing-test = { version = "0.2.4", features = [ "no-env-filter", + "llvm-cov-compat", ], branch = "fix_coverage", git = "https://github.com/Finomnis/tracing-test.git" } # Tokio diff --git a/src/lib.rs b/src/lib.rs index e1d4a89..f05385b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,8 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code))) )] +// Required for test coverage +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] type BoxedError = Box; diff --git a/tests/cancel_on_shutdown.rs b/tests/cancel_on_shutdown.rs index 43dbbb7..dc1de08 100644 --- a/tests/cancel_on_shutdown.rs +++ b/tests/cancel_on_shutdown.rs @@ -1,3 +1,6 @@ +// Required for test coverage +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] + use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{ errors::CancelledByShutdown, FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel, diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 463d463..75f408a 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,3 +1,6 @@ +// Required for test coverage +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] + use anyhow::anyhow; use tokio::time::{sleep, timeout, Duration}; use tokio_graceful_shutdown::{ From fb77dedc4da08b0155cb4d946db79ddda3adea18 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Fri, 20 Oct 2023 23:58:40 +0200 Subject: [PATCH 08/13] Ignore the tests themselves for coverage --- src/error_action.rs | 1 + src/errors.rs | 4 ++++ src/runner/alive_guard.rs | 4 ++++ src/subsystem/error_collector.rs | 3 +++ src/subsystem/subsystem_handle.rs | 2 ++ src/utils/joiner_token.rs | 6 ++++++ src/utils/remote_drop_collection.rs | 2 ++ 7 files changed, 22 insertions(+) diff --git a/src/error_action.rs b/src/error_action.rs index 63a37ba..b5ec976 100644 --- a/src/error_action.rs +++ b/src/error_action.rs @@ -29,6 +29,7 @@ pub enum ErrorAction { mod tests { use super::*; #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn derive_traits() { let x = ErrorAction::CatchAndLocalShutdown; #[allow(clippy::clone_on_copy)] diff --git a/src/errors.rs b/src/errors.rs index 65871d7..f8dd808 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -132,6 +132,7 @@ mod tests { use super::*; + #[cfg_attr(coverage_nightly, coverage(off))] fn examine_report(report: miette::Report) { println!("{}", report); println!("{:?}", report); @@ -142,6 +143,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn errors_can_be_converted_to_diagnostic() { examine_report(GracefulShutdownError::ShutdownTimeout::(Box::new([])).into()); examine_report(GracefulShutdownError::SubsystemsFailed::(Box::new([])).into()); @@ -154,6 +156,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn extract_related_from_graceful_shutdown_error() { let related = || { Box::new([ @@ -185,6 +188,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn extract_contained_error_from_convert_subsystem_failure() { let msg = "MyFailure".to_string(); let failure = SubsystemFailure(msg.clone()); diff --git a/src/runner/alive_guard.rs b/src/runner/alive_guard.rs index 4d22fe9..908ddeb 100644 --- a/src/runner/alive_guard.rs +++ b/src/runner/alive_guard.rs @@ -68,6 +68,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn finished_callback() { let alive_guard = AliveGuard::new(); @@ -85,6 +86,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn cancel_callback() { let alive_guard = AliveGuard::new(); @@ -103,6 +105,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn both_callbacks() { let alive_guard = AliveGuard::new(); @@ -124,6 +127,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn no_callback() { let alive_guard = AliveGuard::new(); drop(alive_guard); diff --git a/src/subsystem/error_collector.rs b/src/subsystem/error_collector.rs index b7da95c..015ec22 100644 --- a/src/subsystem/error_collector.rs +++ b/src/subsystem/error_collector.rs @@ -51,6 +51,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn normal() { let (sender, receiver) = mpsc::unbounded_channel(); let mut error_collector = ErrorCollector::::new(receiver); @@ -71,6 +72,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn double_finish() { let (sender, receiver) = mpsc::unbounded_channel(); let mut error_collector = ErrorCollector::::new(receiver); @@ -97,6 +99,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn no_finish() { let (sender, receiver) = mpsc::unbounded_channel(); let error_collector = ErrorCollector::::new(receiver); diff --git a/src/subsystem/subsystem_handle.rs b/src/subsystem/subsystem_handle.rs index ede0fbf..f2e7b71 100644 --- a/src/subsystem/subsystem_handle.rs +++ b/src/subsystem/subsystem_handle.rs @@ -362,6 +362,7 @@ mod tests { use crate::subsystem::SubsystemBuilder; #[tokio::test] + #[cfg_attr(coverage_nightly, coverage(off))] async fn recursive_cancellation() { let root_handle = root_handle::(|_| {}); @@ -388,6 +389,7 @@ mod tests { } #[tokio::test] + #[cfg_attr(coverage_nightly, coverage(off))] async fn recursive_cancellation_2() { let root_handle = root_handle(|_| {}); diff --git a/src/utils/joiner_token.rs b/src/utils/joiner_token.rs index e792d8e..73c13c1 100644 --- a/src/utils/joiner_token.rs +++ b/src/utils/joiner_token.rs @@ -187,6 +187,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn counters() { let (root, _) = JoinerToken::::new(|_| None); assert_eq!(0, root.count()); @@ -221,6 +222,7 @@ mod tests { #[test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] fn counters_weak() { let (root, weak_root) = JoinerToken::::new(|_| None); assert_eq!(0, weak_root.count()); @@ -293,6 +295,7 @@ mod tests { #[tokio::test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] async fn join() { let (superroot, _) = JoinerToken::::new(|_| None); @@ -335,6 +338,7 @@ mod tests { #[tokio::test] #[traced_test] + #[cfg_attr(coverage_nightly, coverage(off))] async fn join_through_ref() { let (root, joiner) = JoinerToken::::new(|_| None); @@ -372,6 +376,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn debug_print() { let (root, _) = JoinerToken::::new(|_| None); assert_eq!(format!("{:?}", root), "JoinerToken(children = 0)"); @@ -384,6 +389,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn debug_print_ref() { let (root, root_ref) = JoinerToken::::new(|_| None); assert_eq!( diff --git a/src/utils/remote_drop_collection.rs b/src/utils/remote_drop_collection.rs index 56cfc5a..c2547f9 100644 --- a/src/utils/remote_drop_collection.rs +++ b/src/utils/remote_drop_collection.rs @@ -86,6 +86,7 @@ mod tests { use crate::{utils::JoinerToken, BoxedError}; #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn insert_and_drop() { let items = RemotelyDroppableItems::new(); @@ -109,6 +110,7 @@ mod tests { } #[test] + #[cfg_attr(coverage_nightly, coverage(off))] fn drop_token() { let items = RemotelyDroppableItems::new(); From 8e00717b71de704a02469b333a0c65080e846d52 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 21 Oct 2023 00:30:11 +0200 Subject: [PATCH 09/13] Attempt to switch to codecov.io --- tests/integration_test_2.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/integration_test_2.rs diff --git a/tests/integration_test_2.rs b/tests/integration_test_2.rs new file mode 100644 index 0000000..daf0625 --- /dev/null +++ b/tests/integration_test_2.rs @@ -0,0 +1,23 @@ +// Required for test coverage +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] + +use anyhow::anyhow; +use tokio::time::{sleep, timeout, Duration}; +use tokio_graceful_shutdown::{ + errors::{GracefulShutdownError, SubsystemError, SubsystemJoinError}, + ErrorAction, IntoSubsystem, SubsystemBuilder, SubsystemHandle, Toplevel, +}; +use tracing_test::traced_test; + +pub mod common; +use common::Event; + +use std::error::Error; + +/// Wrapper function to simplify lambdas +type BoxedError = Box; +type BoxedResult = Result<(), BoxedError>; + +#[tokio::test] +#[traced_test] +async fn dummy() {} From 4b816be70825c415775f8462e78fe2a250f80cb3 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 21 Oct 2023 00:43:38 +0200 Subject: [PATCH 10/13] Switch to codecov.io, now for real --- .github/workflows/coverage.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 60cb73f..7a5350f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -8,8 +8,8 @@ concurrency: group: coverage-${{ github.ref }} cancel-in-progress: true jobs: - coveralls: - name: Coveralls + coverage: + name: Codecov.io continue-on-error: true runs-on: ubuntu-latest env: @@ -25,14 +25,10 @@ jobs: uses: taiki-e/install-action@cargo-llvm-cov #- uses: Swatinem/rust-cache@v1 - name: Compute Coverage - env: - RUST_LOG: "debug" run: - cargo llvm-cov - --all-features --workspace - --lcov --output-path lcov.info - - name: Upload to Coveralls - uses: coverallsapp/github-action@master + cargo llvm-cov --all-features --workspace --ignore-filename-regex tests --codecov --output-path codecov.info + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 with: - path-to-lcov: lcov.info - github-token: ${{ secrets.github_token }} + files: codecov.json + fail_ci_if_error: true From 36ac387a8067a190141b41843a56f271945af060 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 21 Oct 2023 00:49:51 +0200 Subject: [PATCH 11/13] Upload to codecov, again --- .github/workflows/coverage.yml | 2 +- tests/integration_test_2.rs | 23 ----------------------- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 tests/integration_test_2.rs diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7a5350f..dbe429e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -26,7 +26,7 @@ jobs: #- uses: Swatinem/rust-cache@v1 - name: Compute Coverage run: - cargo llvm-cov --all-features --workspace --ignore-filename-regex tests --codecov --output-path codecov.info + cargo llvm-cov --all-features --workspace --ignore-filename-regex tests --codecov --output-path codecov.json - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: diff --git a/tests/integration_test_2.rs b/tests/integration_test_2.rs deleted file mode 100644 index daf0625..0000000 --- a/tests/integration_test_2.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Required for test coverage -#![cfg_attr(coverage_nightly, feature(coverage_attribute))] - -use anyhow::anyhow; -use tokio::time::{sleep, timeout, Duration}; -use tokio_graceful_shutdown::{ - errors::{GracefulShutdownError, SubsystemError, SubsystemJoinError}, - ErrorAction, IntoSubsystem, SubsystemBuilder, SubsystemHandle, Toplevel, -}; -use tracing_test::traced_test; - -pub mod common; -use common::Event; - -use std::error::Error; - -/// Wrapper function to simplify lambdas -type BoxedError = Box; -type BoxedResult = Result<(), BoxedError>; - -#[tokio::test] -#[traced_test] -async fn dummy() {} From f5d36dee6764e0808c9c9014cd92653785784602 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 21 Oct 2023 01:05:46 +0200 Subject: [PATCH 12/13] Move tests to separate files, to fix coverage --- .github/workflows/coverage.yml | 2 +- Cargo.toml | 5 +- src/error_action.rs | 13 -- src/errors.rs | 72 +------ src/errors/tests.rs | 63 ++++++ src/lib.rs | 2 - src/runner/alive_guard.rs | 77 +------ src/runner/alive_guard/tests.rs | 69 +++++++ src/subsystem/error_collector.rs | 75 +------ src/subsystem/error_collector/tests.rs | 68 ++++++ src/subsystem/subsystem_handle.rs | 77 +------ src/subsystem/subsystem_handle/tests.rs | 71 +++++++ src/utils/joiner_token.rs | 240 +--------------------- src/utils/joiner_token/tests.rs | 231 +++++++++++++++++++++ src/utils/remote_drop_collection.rs | 78 +------ src/utils/remote_drop_collection/tests.rs | 72 +++++++ tests/cancel_on_shutdown.rs | 3 - tests/integration_test.rs | 3 - 18 files changed, 582 insertions(+), 639 deletions(-) create mode 100644 src/errors/tests.rs create mode 100644 src/runner/alive_guard/tests.rs create mode 100644 src/subsystem/error_collector/tests.rs create mode 100644 src/subsystem/subsystem_handle/tests.rs create mode 100644 src/utils/joiner_token/tests.rs create mode 100644 src/utils/remote_drop_collection/tests.rs diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index dbe429e..e2b0f11 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -26,7 +26,7 @@ jobs: #- uses: Swatinem/rust-cache@v1 - name: Compute Coverage run: - cargo llvm-cov --all-features --workspace --ignore-filename-regex tests --codecov --output-path codecov.json + cargo llvm-cov --all-features --workspace --ignore-filename-regex tests.rs --codecov --output-path codecov.json - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: diff --git a/Cargo.toml b/Cargo.toml index 4bae2d8..f3de681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,10 +46,7 @@ miette = { version = "5.10.0", features = ["fancy"] } # Logging tracing-subscriber = "0.3.17" -tracing-test = { version = "0.2.4", features = [ - "no-env-filter", - "llvm-cov-compat", -], branch = "fix_coverage", git = "https://github.com/Finomnis/tracing-test.git" } +tracing-test = { version = "0.2.4", features = ["no-env-filter"] } # Tokio tokio = { version = "1.32.0", features = ["full"] } diff --git a/src/error_action.rs b/src/error_action.rs index b5ec976..5155087 100644 --- a/src/error_action.rs +++ b/src/error_action.rs @@ -24,16 +24,3 @@ pub enum ErrorAction { /// Do not forward the error to the parent subsystem. CatchAndLocalShutdown, } - -#[cfg(test)] -mod tests { - use super::*; - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn derive_traits() { - let x = ErrorAction::CatchAndLocalShutdown; - #[allow(clippy::clone_on_copy)] - let y = x.clone(); - assert!(y == x); - } -} diff --git a/src/errors.rs b/src/errors.rs index f8dd808..693ec98 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -127,74 +127,4 @@ impl SubsystemError { pub struct CancelledByShutdown; #[cfg(test)] -mod tests { - use crate::BoxedError; - - use super::*; - - #[cfg_attr(coverage_nightly, coverage(off))] - fn examine_report(report: miette::Report) { - println!("{}", report); - println!("{:?}", report); - // Convert to std::error::Error - let boxed_error: BoxedError = report.into(); - println!("{}", boxed_error); - println!("{:?}", boxed_error); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn errors_can_be_converted_to_diagnostic() { - examine_report(GracefulShutdownError::ShutdownTimeout::(Box::new([])).into()); - examine_report(GracefulShutdownError::SubsystemsFailed::(Box::new([])).into()); - examine_report(SubsystemJoinError::SubsystemsFailed::(Arc::new([])).into()); - examine_report(SubsystemError::Panicked::("".into()).into()); - examine_report( - SubsystemError::Failed::("".into(), SubsystemFailure("".into())).into(), - ); - examine_report(CancelledByShutdown.into()); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn extract_related_from_graceful_shutdown_error() { - let related = || { - Box::new([ - SubsystemError::Failed("a".into(), SubsystemFailure(String::from("A").into())), - SubsystemError::Panicked("b".into()), - ]) - }; - - let matches_related = |data: &[SubsystemError]| { - let mut iter = data.iter(); - - let elem = iter.next().unwrap(); - assert_eq!(elem.name(), "a"); - assert!(matches!(elem, SubsystemError::Failed(_, _))); - - let elem = iter.next().unwrap(); - assert_eq!(elem.name(), "b"); - assert!(matches!(elem, SubsystemError::Panicked(_))); - - assert!(iter.next().is_none()); - }; - - matches_related(GracefulShutdownError::ShutdownTimeout(related()).get_subsystem_errors()); - matches_related(GracefulShutdownError::SubsystemsFailed(related()).get_subsystem_errors()); - matches_related(&GracefulShutdownError::ShutdownTimeout(related()).into_subsystem_errors()); - matches_related( - &GracefulShutdownError::SubsystemsFailed(related()).into_subsystem_errors(), - ); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn extract_contained_error_from_convert_subsystem_failure() { - let msg = "MyFailure".to_string(); - let failure = SubsystemFailure(msg.clone()); - - assert_eq!(&msg, failure.get_error()); - assert_eq!(msg, *failure); - assert_eq!(msg, failure.into_error()); - } -} +mod tests; diff --git a/src/errors/tests.rs b/src/errors/tests.rs new file mode 100644 index 0000000..ac86457 --- /dev/null +++ b/src/errors/tests.rs @@ -0,0 +1,63 @@ +use crate::BoxedError; + +use super::*; + +fn examine_report(report: miette::Report) { + println!("{}", report); + println!("{:?}", report); + // Convert to std::error::Error + let boxed_error: BoxedError = report.into(); + println!("{}", boxed_error); + println!("{:?}", boxed_error); +} + +#[test] +fn errors_can_be_converted_to_diagnostic() { + examine_report(GracefulShutdownError::ShutdownTimeout::(Box::new([])).into()); + examine_report(GracefulShutdownError::SubsystemsFailed::(Box::new([])).into()); + examine_report(SubsystemJoinError::SubsystemsFailed::(Arc::new([])).into()); + examine_report(SubsystemError::Panicked::("".into()).into()); + examine_report( + SubsystemError::Failed::("".into(), SubsystemFailure("".into())).into(), + ); + examine_report(CancelledByShutdown.into()); +} + +#[test] +fn extract_related_from_graceful_shutdown_error() { + let related = || { + Box::new([ + SubsystemError::Failed("a".into(), SubsystemFailure(String::from("A").into())), + SubsystemError::Panicked("b".into()), + ]) + }; + + let matches_related = |data: &[SubsystemError]| { + let mut iter = data.iter(); + + let elem = iter.next().unwrap(); + assert_eq!(elem.name(), "a"); + assert!(matches!(elem, SubsystemError::Failed(_, _))); + + let elem = iter.next().unwrap(); + assert_eq!(elem.name(), "b"); + assert!(matches!(elem, SubsystemError::Panicked(_))); + + assert!(iter.next().is_none()); + }; + + matches_related(GracefulShutdownError::ShutdownTimeout(related()).get_subsystem_errors()); + matches_related(GracefulShutdownError::SubsystemsFailed(related()).get_subsystem_errors()); + matches_related(&GracefulShutdownError::ShutdownTimeout(related()).into_subsystem_errors()); + matches_related(&GracefulShutdownError::SubsystemsFailed(related()).into_subsystem_errors()); +} + +#[test] +fn extract_contained_error_from_convert_subsystem_failure() { + let msg = "MyFailure".to_string(); + let failure = SubsystemFailure(msg.clone()); + + assert_eq!(&msg, failure.get_error()); + assert_eq!(msg, *failure); + assert_eq!(msg, failure.into_error()); +} diff --git a/src/lib.rs b/src/lib.rs index f05385b..e1d4a89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,8 +91,6 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code))) )] -// Required for test coverage -#![cfg_attr(coverage_nightly, feature(coverage_attribute))] type BoxedError = Box; diff --git a/src/runner/alive_guard.rs b/src/runner/alive_guard.rs index 908ddeb..0efbca5 100644 --- a/src/runner/alive_guard.rs +++ b/src/runner/alive_guard.rs @@ -59,79 +59,4 @@ impl Drop for Inner { } #[cfg(test)] -mod tests { - - use std::sync::atomic::{AtomicU32, Ordering}; - use tracing_test::traced_test; - - use super::*; - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn finished_callback() { - let alive_guard = AliveGuard::new(); - - let counter = Arc::new(AtomicU32::new(0)); - let counter2 = Arc::clone(&counter); - - alive_guard.on_finished(move || { - counter2.fetch_add(1, Ordering::Relaxed); - }); - - drop(alive_guard); - - assert_eq!(counter.load(Ordering::Relaxed), 1); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn cancel_callback() { - let alive_guard = AliveGuard::new(); - - let counter = Arc::new(AtomicU32::new(0)); - let counter2 = Arc::clone(&counter); - - alive_guard.on_finished(|| {}); - alive_guard.on_cancel(move || { - counter2.fetch_add(1, Ordering::Relaxed); - }); - - drop(alive_guard); - - assert_eq!(counter.load(Ordering::Relaxed), 1); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn both_callbacks() { - let alive_guard = AliveGuard::new(); - - let counter = Arc::new(AtomicU32::new(0)); - let counter2 = Arc::clone(&counter); - let counter3 = Arc::clone(&counter); - - alive_guard.on_finished(move || { - counter2.fetch_add(1, Ordering::Relaxed); - }); - alive_guard.on_cancel(move || { - counter3.fetch_add(1, Ordering::Relaxed); - }); - - drop(alive_guard); - - assert_eq!(counter.load(Ordering::Relaxed), 2); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn no_callback() { - let alive_guard = AliveGuard::new(); - drop(alive_guard); - - assert!(logs_contain("No `finished` callback was registered in AliveGuard! This should not happen, please report this at https://github.com/Finomnis/tokio-graceful-shutdown/issues.")); - } -} +mod tests; diff --git a/src/runner/alive_guard/tests.rs b/src/runner/alive_guard/tests.rs new file mode 100644 index 0000000..4542951 --- /dev/null +++ b/src/runner/alive_guard/tests.rs @@ -0,0 +1,69 @@ +use std::sync::atomic::{AtomicU32, Ordering}; +use tracing_test::traced_test; + +use super::*; + +#[test] +#[traced_test] +fn finished_callback() { + let alive_guard = AliveGuard::new(); + + let counter = Arc::new(AtomicU32::new(0)); + let counter2 = Arc::clone(&counter); + + alive_guard.on_finished(move || { + counter2.fetch_add(1, Ordering::Relaxed); + }); + + drop(alive_guard); + + assert_eq!(counter.load(Ordering::Relaxed), 1); +} + +#[test] +#[traced_test] +fn cancel_callback() { + let alive_guard = AliveGuard::new(); + + let counter = Arc::new(AtomicU32::new(0)); + let counter2 = Arc::clone(&counter); + + alive_guard.on_finished(|| {}); + alive_guard.on_cancel(move || { + counter2.fetch_add(1, Ordering::Relaxed); + }); + + drop(alive_guard); + + assert_eq!(counter.load(Ordering::Relaxed), 1); +} + +#[test] +#[traced_test] +fn both_callbacks() { + let alive_guard = AliveGuard::new(); + + let counter = Arc::new(AtomicU32::new(0)); + let counter2 = Arc::clone(&counter); + let counter3 = Arc::clone(&counter); + + alive_guard.on_finished(move || { + counter2.fetch_add(1, Ordering::Relaxed); + }); + alive_guard.on_cancel(move || { + counter3.fetch_add(1, Ordering::Relaxed); + }); + + drop(alive_guard); + + assert_eq!(counter.load(Ordering::Relaxed), 2); +} + +#[test] +#[traced_test] +fn no_callback() { + let alive_guard = AliveGuard::new(); + drop(alive_guard); + + assert!(logs_contain("No `finished` callback was registered in AliveGuard! This should not happen, please report this at https://github.com/Finomnis/tokio-graceful-shutdown/issues.")); +} diff --git a/src/subsystem/error_collector.rs b/src/subsystem/error_collector.rs index 015ec22..9a16956 100644 --- a/src/subsystem/error_collector.rs +++ b/src/subsystem/error_collector.rs @@ -43,77 +43,4 @@ impl Drop for ErrorCollector { } #[cfg(test)] -mod tests { - - use tracing_test::traced_test; - - use super::*; - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn normal() { - let (sender, receiver) = mpsc::unbounded_channel(); - let mut error_collector = ErrorCollector::::new(receiver); - - sender - .send(SubsystemError::Panicked(Arc::from("ABC"))) - .unwrap(); - sender - .send(SubsystemError::Panicked(Arc::from("def"))) - .unwrap(); - - let received = error_collector.finish(); - assert_eq!( - received.iter().map(|e| e.name()).collect::>(), - vec!["ABC", "def"] - ); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn double_finish() { - let (sender, receiver) = mpsc::unbounded_channel(); - let mut error_collector = ErrorCollector::::new(receiver); - - sender - .send(SubsystemError::Panicked(Arc::from("ABC"))) - .unwrap(); - sender - .send(SubsystemError::Panicked(Arc::from("def"))) - .unwrap(); - - let received = error_collector.finish(); - assert_eq!( - received.iter().map(|e| e.name()).collect::>(), - vec!["ABC", "def"] - ); - - let received = error_collector.finish(); - assert_eq!( - received.iter().map(|e| e.name()).collect::>(), - vec!["ABC", "def"] - ); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn no_finish() { - let (sender, receiver) = mpsc::unbounded_channel(); - let error_collector = ErrorCollector::::new(receiver); - - sender - .send(SubsystemError::Panicked(Arc::from("ABC"))) - .unwrap(); - sender - .send(SubsystemError::Panicked(Arc::from("def"))) - .unwrap(); - - drop(error_collector); - - assert!(logs_contain("An error got dropped: Panicked(\"ABC\")")); - assert!(logs_contain("An error got dropped: Panicked(\"def\")")); - } -} +mod tests; diff --git a/src/subsystem/error_collector/tests.rs b/src/subsystem/error_collector/tests.rs new file mode 100644 index 0000000..1e0e354 --- /dev/null +++ b/src/subsystem/error_collector/tests.rs @@ -0,0 +1,68 @@ +use tracing_test::traced_test; + +use super::*; + +#[test] +#[traced_test] +fn normal() { + let (sender, receiver) = mpsc::unbounded_channel(); + let mut error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); +} + +#[test] +#[traced_test] +fn double_finish() { + let (sender, receiver) = mpsc::unbounded_channel(); + let mut error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); + + let received = error_collector.finish(); + assert_eq!( + received.iter().map(|e| e.name()).collect::>(), + vec!["ABC", "def"] + ); +} + +#[test] +#[traced_test] +fn no_finish() { + let (sender, receiver) = mpsc::unbounded_channel(); + let error_collector = ErrorCollector::::new(receiver); + + sender + .send(SubsystemError::Panicked(Arc::from("ABC"))) + .unwrap(); + sender + .send(SubsystemError::Panicked(Arc::from("def"))) + .unwrap(); + + drop(error_collector); + + assert!(logs_contain("An error got dropped: Panicked(\"ABC\")")); + assert!(logs_contain("An error got dropped: Panicked(\"def\")")); +} diff --git a/src/subsystem/subsystem_handle.rs b/src/subsystem/subsystem_handle.rs index f2e7b71..52a5f2f 100644 --- a/src/subsystem/subsystem_handle.rs +++ b/src/subsystem/subsystem_handle.rs @@ -354,79 +354,4 @@ pub(crate) fn root_handle( } #[cfg(test)] -mod tests { - - use tokio::time::{sleep, timeout, Duration}; - - use super::*; - use crate::subsystem::SubsystemBuilder; - - #[tokio::test] - #[cfg_attr(coverage_nightly, coverage(off))] - async fn recursive_cancellation() { - let root_handle = root_handle::(|_| {}); - - let (drop_sender, mut drop_receiver) = tokio::sync::mpsc::channel::<()>(1); - - root_handle.start(SubsystemBuilder::new("", |_| async move { - drop_sender.send(()).await.unwrap(); - std::future::pending::>().await - })); - - // Make sure we are executing the subsystem - let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) - .await - .unwrap(); - assert!(recv_result.is_some()); - - drop(root_handle); - - // Make sure the subsystem got cancelled - let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) - .await - .unwrap(); - assert!(recv_result.is_none()); - } - - #[tokio::test] - #[cfg_attr(coverage_nightly, coverage(off))] - async fn recursive_cancellation_2() { - let root_handle = root_handle(|_| {}); - - let (drop_sender, mut drop_receiver) = tokio::sync::mpsc::channel::<()>(1); - - let subsys2 = |_| async move { - drop_sender.send(()).await.unwrap(); - std::future::pending::>().await - }; - - let subsys = |x: SubsystemHandle| async move { - x.start(SubsystemBuilder::new("", subsys2)); - - Result::<(), BoxedError>::Ok(()) - }; - - root_handle.start(SubsystemBuilder::new("", subsys)); - - // Make sure we are executing the subsystem - let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) - .await - .unwrap(); - assert!(recv_result.is_some()); - - // Make sure the grandchild is still running - sleep(Duration::from_millis(100)).await; - assert!(matches!( - drop_receiver.try_recv(), - Err(tokio::sync::mpsc::error::TryRecvError::Empty) - )); - - drop(root_handle); - - // Make sure the subsystem got cancelled - let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) - .await - .unwrap(); - assert!(recv_result.is_none()); - } -} +mod tests; diff --git a/src/subsystem/subsystem_handle/tests.rs b/src/subsystem/subsystem_handle/tests.rs new file mode 100644 index 0000000..35acbf4 --- /dev/null +++ b/src/subsystem/subsystem_handle/tests.rs @@ -0,0 +1,71 @@ +use tokio::time::{sleep, timeout, Duration}; + +use super::*; +use crate::subsystem::SubsystemBuilder; + +#[tokio::test] +async fn recursive_cancellation() { + let root_handle = root_handle::(|_| {}); + + let (drop_sender, mut drop_receiver) = tokio::sync::mpsc::channel::<()>(1); + + root_handle.start(SubsystemBuilder::new("", |_| async move { + drop_sender.send(()).await.unwrap(); + std::future::pending::>().await + })); + + // Make sure we are executing the subsystem + let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) + .await + .unwrap(); + assert!(recv_result.is_some()); + + drop(root_handle); + + // Make sure the subsystem got cancelled + let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) + .await + .unwrap(); + assert!(recv_result.is_none()); +} + +#[tokio::test] +async fn recursive_cancellation_2() { + let root_handle = root_handle(|_| {}); + + let (drop_sender, mut drop_receiver) = tokio::sync::mpsc::channel::<()>(1); + + let subsys2 = |_| async move { + drop_sender.send(()).await.unwrap(); + std::future::pending::>().await + }; + + let subsys = |x: SubsystemHandle| async move { + x.start(SubsystemBuilder::new("", subsys2)); + + Result::<(), BoxedError>::Ok(()) + }; + + root_handle.start(SubsystemBuilder::new("", subsys)); + + // Make sure we are executing the subsystem + let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) + .await + .unwrap(); + assert!(recv_result.is_some()); + + // Make sure the grandchild is still running + sleep(Duration::from_millis(100)).await; + assert!(matches!( + drop_receiver.try_recv(), + Err(tokio::sync::mpsc::error::TryRecvError::Empty) + )); + + drop(root_handle); + + // Make sure the subsystem got cancelled + let recv_result = timeout(Duration::from_millis(100), drop_receiver.recv()) + .await + .unwrap(); + assert!(recv_result.is_none()); +} diff --git a/src/utils/joiner_token.rs b/src/utils/joiner_token.rs index 73c13c1..6b672ea 100644 --- a/src/utils/joiner_token.rs +++ b/src/utils/joiner_token.rs @@ -177,242 +177,4 @@ impl Drop for JoinerToken { } #[cfg(test)] -mod tests { - use tokio::time::{sleep, timeout, Duration}; - use tracing_test::traced_test; - - use crate::BoxedError; - - use super::*; - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn counters() { - let (root, _) = JoinerToken::::new(|_| None); - assert_eq!(0, root.count()); - - let (child1, _) = root.child_token(|_| None); - assert_eq!(1, root.count()); - assert_eq!(0, child1.count()); - - let (child2, _) = child1.child_token(|_| None); - assert_eq!(2, root.count()); - assert_eq!(1, child1.count()); - assert_eq!(0, child2.count()); - - let (child3, _) = child1.child_token(|_| None); - assert_eq!(3, root.count()); - assert_eq!(2, child1.count()); - assert_eq!(0, child2.count()); - assert_eq!(0, child3.count()); - - drop(child1); - assert_eq!(2, root.count()); - assert_eq!(0, child2.count()); - assert_eq!(0, child3.count()); - - drop(child2); - assert_eq!(1, root.count()); - assert_eq!(0, child3.count()); - - drop(child3); - assert_eq!(0, root.count()); - } - - #[test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn counters_weak() { - let (root, weak_root) = JoinerToken::::new(|_| None); - assert_eq!(0, weak_root.count()); - assert!(weak_root.alive()); - - let (child1, weak_child1) = root.child_token(|_| None); - assert_eq!(1, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(0, weak_child1.count()); - assert!(weak_child1.alive()); - - let (child2, weak_child2) = child1.child_token(|_| None); - assert_eq!(2, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(1, weak_child1.count()); - assert!(weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(weak_child2.alive()); - - let (child3, weak_child3) = child1.child_token(|_| None); - assert_eq!(3, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(2, weak_child1.count()); - assert!(weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(weak_child2.alive()); - assert_eq!(0, weak_child3.count()); - assert!(weak_child3.alive()); - - drop(child1); - assert_eq!(2, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(2, weak_child1.count()); - assert!(!weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(weak_child2.alive()); - assert_eq!(0, weak_child3.count()); - assert!(weak_child3.alive()); - - drop(child2); - assert_eq!(1, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(1, weak_child1.count()); - assert!(!weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(!weak_child2.alive()); - assert_eq!(0, weak_child3.count()); - assert!(weak_child3.alive()); - - drop(child3); - assert_eq!(0, weak_root.count()); - assert!(weak_root.alive()); - assert_eq!(0, weak_child1.count()); - assert!(!weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(!weak_child2.alive()); - assert_eq!(0, weak_child3.count()); - assert!(!weak_child3.alive()); - - drop(root); - assert_eq!(0, weak_root.count()); - assert!(!weak_root.alive()); - assert_eq!(0, weak_child1.count()); - assert!(!weak_child1.alive()); - assert_eq!(0, weak_child2.count()); - assert!(!weak_child2.alive()); - assert_eq!(0, weak_child3.count()); - assert!(!weak_child3.alive()); - } - - #[tokio::test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - async fn join() { - let (superroot, _) = JoinerToken::::new(|_| None); - - let (mut root, _) = superroot.child_token(|_| None); - - let (child1, _) = root.child_token(|_| None); - let (child2, _) = child1.child_token(|_| None); - let (child3, _) = child1.child_token(|_| None); - - let (set_finished, mut finished) = tokio::sync::oneshot::channel(); - tokio::join!( - async { - timeout(Duration::from_millis(500), root.join_children()) - .await - .unwrap(); - set_finished.send(root.count()).unwrap(); - }, - async { - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(child1); - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(child2); - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(child3); - sleep(Duration::from_millis(50)).await; - let count = timeout(Duration::from_millis(50), finished) - .await - .unwrap() - .unwrap(); - assert_eq!(count, 0); - } - ); - } - - #[tokio::test] - #[traced_test] - #[cfg_attr(coverage_nightly, coverage(off))] - async fn join_through_ref() { - let (root, joiner) = JoinerToken::::new(|_| None); - - let (child1, _) = root.child_token(|_| None); - let (child2, _) = child1.child_token(|_| None); - - let (set_finished, mut finished) = tokio::sync::oneshot::channel(); - tokio::join!( - async { - timeout(Duration::from_millis(500), joiner.join()) - .await - .unwrap(); - set_finished.send(()).unwrap(); - }, - async { - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(child1); - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(root); - sleep(Duration::from_millis(50)).await; - assert!(finished.try_recv().is_err()); - - drop(child2); - sleep(Duration::from_millis(50)).await; - timeout(Duration::from_millis(50), finished) - .await - .unwrap() - .unwrap(); - } - ); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn debug_print() { - let (root, _) = JoinerToken::::new(|_| None); - assert_eq!(format!("{:?}", root), "JoinerToken(children = 0)"); - - let (child1, _) = root.child_token(|_| None); - assert_eq!(format!("{:?}", root), "JoinerToken(children = 1)"); - - let (_child2, _) = child1.child_token(|_| None); - assert_eq!(format!("{:?}", root), "JoinerToken(children = 2)"); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn debug_print_ref() { - let (root, root_ref) = JoinerToken::::new(|_| None); - assert_eq!( - format!("{:?}", root_ref), - "JoinerTokenRef(alive = true, children = 0)" - ); - - let (child1, _) = root.child_token(|_| None); - assert_eq!( - format!("{:?}", root_ref), - "JoinerTokenRef(alive = true, children = 1)" - ); - - drop(root); - assert_eq!( - format!("{:?}", root_ref), - "JoinerTokenRef(alive = false, children = 1)" - ); - - drop(child1); - assert_eq!( - format!("{:?}", root_ref), - "JoinerTokenRef(alive = false, children = 0)" - ); - } -} +mod tests; diff --git a/src/utils/joiner_token/tests.rs b/src/utils/joiner_token/tests.rs new file mode 100644 index 0000000..03ba7c9 --- /dev/null +++ b/src/utils/joiner_token/tests.rs @@ -0,0 +1,231 @@ +use tokio::time::{sleep, timeout, Duration}; +use tracing_test::traced_test; + +use crate::BoxedError; + +use super::*; + +#[test] +#[traced_test] +fn counters() { + let (root, _) = JoinerToken::::new(|_| None); + assert_eq!(0, root.count()); + + let (child1, _) = root.child_token(|_| None); + assert_eq!(1, root.count()); + assert_eq!(0, child1.count()); + + let (child2, _) = child1.child_token(|_| None); + assert_eq!(2, root.count()); + assert_eq!(1, child1.count()); + assert_eq!(0, child2.count()); + + let (child3, _) = child1.child_token(|_| None); + assert_eq!(3, root.count()); + assert_eq!(2, child1.count()); + assert_eq!(0, child2.count()); + assert_eq!(0, child3.count()); + + drop(child1); + assert_eq!(2, root.count()); + assert_eq!(0, child2.count()); + assert_eq!(0, child3.count()); + + drop(child2); + assert_eq!(1, root.count()); + assert_eq!(0, child3.count()); + + drop(child3); + assert_eq!(0, root.count()); +} + +#[test] +#[traced_test] +fn counters_weak() { + let (root, weak_root) = JoinerToken::::new(|_| None); + assert_eq!(0, weak_root.count()); + assert!(weak_root.alive()); + + let (child1, weak_child1) = root.child_token(|_| None); + assert_eq!(1, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(0, weak_child1.count()); + assert!(weak_child1.alive()); + + let (child2, weak_child2) = child1.child_token(|_| None); + assert_eq!(2, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(1, weak_child1.count()); + assert!(weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(weak_child2.alive()); + + let (child3, weak_child3) = child1.child_token(|_| None); + assert_eq!(3, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(2, weak_child1.count()); + assert!(weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(weak_child2.alive()); + assert_eq!(0, weak_child3.count()); + assert!(weak_child3.alive()); + + drop(child1); + assert_eq!(2, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(2, weak_child1.count()); + assert!(!weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(weak_child2.alive()); + assert_eq!(0, weak_child3.count()); + assert!(weak_child3.alive()); + + drop(child2); + assert_eq!(1, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(1, weak_child1.count()); + assert!(!weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(!weak_child2.alive()); + assert_eq!(0, weak_child3.count()); + assert!(weak_child3.alive()); + + drop(child3); + assert_eq!(0, weak_root.count()); + assert!(weak_root.alive()); + assert_eq!(0, weak_child1.count()); + assert!(!weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(!weak_child2.alive()); + assert_eq!(0, weak_child3.count()); + assert!(!weak_child3.alive()); + + drop(root); + assert_eq!(0, weak_root.count()); + assert!(!weak_root.alive()); + assert_eq!(0, weak_child1.count()); + assert!(!weak_child1.alive()); + assert_eq!(0, weak_child2.count()); + assert!(!weak_child2.alive()); + assert_eq!(0, weak_child3.count()); + assert!(!weak_child3.alive()); +} + +#[tokio::test] +#[traced_test] +async fn join() { + let (superroot, _) = JoinerToken::::new(|_| None); + + let (mut root, _) = superroot.child_token(|_| None); + + let (child1, _) = root.child_token(|_| None); + let (child2, _) = child1.child_token(|_| None); + let (child3, _) = child1.child_token(|_| None); + + let (set_finished, mut finished) = tokio::sync::oneshot::channel(); + tokio::join!( + async { + timeout(Duration::from_millis(500), root.join_children()) + .await + .unwrap(); + set_finished.send(root.count()).unwrap(); + }, + async { + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(child1); + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(child2); + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(child3); + sleep(Duration::from_millis(50)).await; + let count = timeout(Duration::from_millis(50), finished) + .await + .unwrap() + .unwrap(); + assert_eq!(count, 0); + } + ); +} + +#[tokio::test] +#[traced_test] +async fn join_through_ref() { + let (root, joiner) = JoinerToken::::new(|_| None); + + let (child1, _) = root.child_token(|_| None); + let (child2, _) = child1.child_token(|_| None); + + let (set_finished, mut finished) = tokio::sync::oneshot::channel(); + tokio::join!( + async { + timeout(Duration::from_millis(500), joiner.join()) + .await + .unwrap(); + set_finished.send(()).unwrap(); + }, + async { + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(child1); + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(root); + sleep(Duration::from_millis(50)).await; + assert!(finished.try_recv().is_err()); + + drop(child2); + sleep(Duration::from_millis(50)).await; + timeout(Duration::from_millis(50), finished) + .await + .unwrap() + .unwrap(); + } + ); +} + +#[test] +fn debug_print() { + let (root, _) = JoinerToken::::new(|_| None); + assert_eq!(format!("{:?}", root), "JoinerToken(children = 0)"); + + let (child1, _) = root.child_token(|_| None); + assert_eq!(format!("{:?}", root), "JoinerToken(children = 1)"); + + let (_child2, _) = child1.child_token(|_| None); + assert_eq!(format!("{:?}", root), "JoinerToken(children = 2)"); +} + +#[test] +fn debug_print_ref() { + let (root, root_ref) = JoinerToken::::new(|_| None); + assert_eq!( + format!("{:?}", root_ref), + "JoinerTokenRef(alive = true, children = 0)" + ); + + let (child1, _) = root.child_token(|_| None); + assert_eq!( + format!("{:?}", root_ref), + "JoinerTokenRef(alive = true, children = 1)" + ); + + drop(root); + assert_eq!( + format!("{:?}", root_ref), + "JoinerTokenRef(alive = false, children = 1)" + ); + + drop(child1); + assert_eq!( + format!("{:?}", root_ref), + "JoinerTokenRef(alive = false, children = 0)" + ); +} diff --git a/src/utils/remote_drop_collection.rs b/src/utils/remote_drop_collection.rs index c2547f9..ad88612 100644 --- a/src/utils/remote_drop_collection.rs +++ b/src/utils/remote_drop_collection.rs @@ -80,80 +80,4 @@ impl Drop for RemoteDrop { } #[cfg(test)] -mod tests { - - use super::*; - use crate::{utils::JoinerToken, BoxedError}; - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn insert_and_drop() { - let items = RemotelyDroppableItems::new(); - - let (count1, _) = JoinerToken::::new(|_| None); - let (count2, _) = JoinerToken::::new(|_| None); - - assert_eq!(0, count1.count()); - assert_eq!(0, count2.count()); - - let _token1 = items.insert(count1.child_token(|_| None)); - assert_eq!(1, count1.count()); - assert_eq!(0, count2.count()); - - let _token2 = items.insert(count2.child_token(|_| None)); - assert_eq!(1, count1.count()); - assert_eq!(1, count2.count()); - - drop(items); - assert_eq!(0, count1.count()); - assert_eq!(0, count2.count()); - } - - #[test] - #[cfg_attr(coverage_nightly, coverage(off))] - fn drop_token() { - let items = RemotelyDroppableItems::new(); - - let (count1, _) = JoinerToken::::new(|_| None); - let (count2, _) = JoinerToken::::new(|_| None); - let (count3, _) = JoinerToken::::new(|_| None); - let (count4, _) = JoinerToken::::new(|_| None); - - let token1 = items.insert(count1.child_token(|_| None)); - let token2 = items.insert(count2.child_token(|_| None)); - let token3 = items.insert(count3.child_token(|_| None)); - let token4 = items.insert(count4.child_token(|_| None)); - assert_eq!(1, count1.count()); - assert_eq!(1, count2.count()); - assert_eq!(1, count3.count()); - assert_eq!(1, count4.count()); - - // Last item - drop(token4); - assert_eq!(1, count1.count()); - assert_eq!(1, count2.count()); - assert_eq!(1, count3.count()); - assert_eq!(0, count4.count()); - - // Middle item - drop(token2); - assert_eq!(1, count1.count()); - assert_eq!(0, count2.count()); - assert_eq!(1, count3.count()); - assert_eq!(0, count4.count()); - - // First item - drop(token1); - assert_eq!(0, count1.count()); - assert_eq!(0, count2.count()); - assert_eq!(1, count3.count()); - assert_eq!(0, count4.count()); - - // Only item - drop(token3); - assert_eq!(0, count1.count()); - assert_eq!(0, count2.count()); - assert_eq!(0, count3.count()); - assert_eq!(0, count4.count()); - } -} +mod tests; diff --git a/src/utils/remote_drop_collection/tests.rs b/src/utils/remote_drop_collection/tests.rs new file mode 100644 index 0000000..45a194b --- /dev/null +++ b/src/utils/remote_drop_collection/tests.rs @@ -0,0 +1,72 @@ +use super::*; +use crate::{utils::JoinerToken, BoxedError}; + +#[test] +fn insert_and_drop() { + let items = RemotelyDroppableItems::new(); + + let (count1, _) = JoinerToken::::new(|_| None); + let (count2, _) = JoinerToken::::new(|_| None); + + assert_eq!(0, count1.count()); + assert_eq!(0, count2.count()); + + let _token1 = items.insert(count1.child_token(|_| None)); + assert_eq!(1, count1.count()); + assert_eq!(0, count2.count()); + + let _token2 = items.insert(count2.child_token(|_| None)); + assert_eq!(1, count1.count()); + assert_eq!(1, count2.count()); + + drop(items); + assert_eq!(0, count1.count()); + assert_eq!(0, count2.count()); +} + +#[test] +fn drop_token() { + let items = RemotelyDroppableItems::new(); + + let (count1, _) = JoinerToken::::new(|_| None); + let (count2, _) = JoinerToken::::new(|_| None); + let (count3, _) = JoinerToken::::new(|_| None); + let (count4, _) = JoinerToken::::new(|_| None); + + let token1 = items.insert(count1.child_token(|_| None)); + let token2 = items.insert(count2.child_token(|_| None)); + let token3 = items.insert(count3.child_token(|_| None)); + let token4 = items.insert(count4.child_token(|_| None)); + assert_eq!(1, count1.count()); + assert_eq!(1, count2.count()); + assert_eq!(1, count3.count()); + assert_eq!(1, count4.count()); + + // Last item + drop(token4); + assert_eq!(1, count1.count()); + assert_eq!(1, count2.count()); + assert_eq!(1, count3.count()); + assert_eq!(0, count4.count()); + + // Middle item + drop(token2); + assert_eq!(1, count1.count()); + assert_eq!(0, count2.count()); + assert_eq!(1, count3.count()); + assert_eq!(0, count4.count()); + + // First item + drop(token1); + assert_eq!(0, count1.count()); + assert_eq!(0, count2.count()); + assert_eq!(1, count3.count()); + assert_eq!(0, count4.count()); + + // Only item + drop(token3); + assert_eq!(0, count1.count()); + assert_eq!(0, count2.count()); + assert_eq!(0, count3.count()); + assert_eq!(0, count4.count()); +} diff --git a/tests/cancel_on_shutdown.rs b/tests/cancel_on_shutdown.rs index dc1de08..43dbbb7 100644 --- a/tests/cancel_on_shutdown.rs +++ b/tests/cancel_on_shutdown.rs @@ -1,6 +1,3 @@ -// Required for test coverage -#![cfg_attr(coverage_nightly, feature(coverage_attribute))] - use tokio::time::{sleep, Duration}; use tokio_graceful_shutdown::{ errors::CancelledByShutdown, FutureExt, SubsystemBuilder, SubsystemHandle, Toplevel, diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 75f408a..463d463 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,6 +1,3 @@ -// Required for test coverage -#![cfg_attr(coverage_nightly, feature(coverage_attribute))] - use anyhow::anyhow; use tokio::time::{sleep, timeout, Duration}; use tokio_graceful_shutdown::{ From d1887c8a7b114392ad6235e9af6b289b9ee67407 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Sat, 21 Oct 2023 01:13:54 +0200 Subject: [PATCH 13/13] Update coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3ff462..a1a0d33 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![License](https://img.shields.io/crates/l/tokio-graceful-shutdown)](https://github.com/Finomnis/tokio-graceful-shutdown/blob/main/LICENSE-MIT) [![Build Status](https://img.shields.io/github/actions/workflow/status/Finomnis/tokio-graceful-shutdown/ci.yml?branch=main)](https://github.com/Finomnis/tokio-graceful-shutdown/actions/workflows/ci.yml?query=branch%3Amain) [![docs.rs](https://img.shields.io/docsrs/tokio-graceful-shutdown)](https://docs.rs/tokio-graceful-shutdown) -[![Coverage Status](https://img.shields.io/coveralls/github/Finomnis/tokio-graceful-shutdown/main)](https://coveralls.io/github/Finomnis/tokio-graceful-shutdown?branch=main) +[![Coverage Status](https://img.shields.io/codecov/c/github/Finomnis/tokio-graceful-shutdown)](https://app.codecov.io/github/Finomnis/tokio-graceful-shutdown/tree/main) This crate provides utility functions to perform a graceful shutdown on tokio-rs based services.