From ed32cd194c47c9ad674c65cee5a5a901b387cb79 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Tue, 31 Oct 2023 10:02:00 +0100 Subject: [PATCH] task: add tests for tracing instrumentation of tasks (#6112) Tokio is instrumented with traces which can be used to analyze the behavior of the runtime during execution or post-mortem. The instrumentation is optional. This is where tokio-console collections information. There are currently no tests for the instrumentation. In order to provide more stability to the instrumentation and prepare for future changes, tests are added to verify the current behavior. The tests are written using the `tracing-mock` crate. As this crate is still unreleased, a separate test create has been added under `tokio/tests` which is outside the workspace. This allows us to pull in both `tracing` and `tracing-mock` from the tracing repository on GitHub without affecting the rest of the tokio repository. This change adds initial tests for the task instrumentation. Further tests will be added in subsequent commits. Once `tracing-mock` is published on crates.io (tokio-rs/tracing#539), these tests can be moved in with the "normal" tokio integration tests. The decision to add these tests now is due to the release of `tracing-mock` taking a while, so it would be better to have tests while we wait. --- .github/workflows/ci.yml | 29 +++++ .../tests/tracing-instrumentation/Cargo.toml | 17 +++ .../tracing-instrumentation/tests/task.rs | 107 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 tokio/tests/tracing-instrumentation/Cargo.toml create mode 100644 tokio/tests/tracing-instrumentation/tests/task.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f18093454..35d202dd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: - test-workspace-all-features - test-integration-tests-per-feature - test-parking_lot + - test-tracing-instrumentation - valgrind - test-unstable - miri @@ -213,6 +214,34 @@ jobs: - name: Check tests with all features enabled run: cargo check --workspace --all-features --tests + test-tracing-instrumentation: + # These tests use the as-yet unpublished `tracing-mock` crate to test the + # tracing instrumentation present in Tokio. As such they are placed in + # their own test crate outside of the workspace. + needs: basics + name: test tokio instrumentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Rust ${{ env.rust_stable }} + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.rust_stable }} + - name: Install cargo-nextest + uses: taiki-e/install-action@v2 + with: + tool: cargo-nextest + + - uses: Swatinem/rust-cache@v2 + + - name: test tracing-instrumentation + run: | + set -euxo pipefail + cargo nextest run + working-directory: tokio/tests/tracing-instrumentation + env: + RUSTFLAGS: --cfg tokio_unstable -Dwarnings + valgrind: name: valgrind needs: basics diff --git a/tokio/tests/tracing-instrumentation/Cargo.toml b/tokio/tests/tracing-instrumentation/Cargo.toml new file mode 100644 index 000000000..94c88dc28 --- /dev/null +++ b/tokio/tests/tracing-instrumentation/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tracing-instrumentation" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[dev-dependencies] +futures = { version = "0.3.0", features = ["async-await"] } +tokio = { version = "1.33.0", path = "../..", features = ["full", "tracing"] } +tracing = { version = "0.1.40", git = "https://github.com/tokio-rs/tracing.git", tag = "tracing-0.1.40" } +tracing-mock = { version = "0.1.0", git = "https://github.com/tokio-rs/tracing.git", tag = "tracing-0.1.40" } + +[patch.crates-io] +tracing = { git = "https://github.com/tokio-rs/tracing.git", tag = "tracing-0.1.40" } + +[workspace] diff --git a/tokio/tests/tracing-instrumentation/tests/task.rs b/tokio/tests/tracing-instrumentation/tests/task.rs new file mode 100644 index 000000000..7bdb078e3 --- /dev/null +++ b/tokio/tests/tracing-instrumentation/tests/task.rs @@ -0,0 +1,107 @@ +//! Tests for task instrumentation. +//! +//! These tests ensure that the instrumentation for task spawning and task +//! lifecycles is correct. + +use tokio::task; +use tracing_mock::{expect, span::NewSpan, subscriber}; + +#[tokio::test] +async fn task_spawn_creates_span() { + let task_span = expect::span() + .named("runtime.spawn") + .with_target("tokio::task"); + + let (subscriber, handle) = subscriber::mock() + .new_span(task_span.clone()) + .enter(task_span.clone()) + .exit(task_span.clone()) + // The task span is entered once more when it gets dropped + .enter(task_span.clone()) + .exit(task_span.clone()) + .drop_span(task_span) + .run_with_handle(); + + { + let _guard = tracing::subscriber::set_default(subscriber); + tokio::spawn(futures::future::ready(())) + .await + .expect("failed to await join handle"); + } + + handle.assert_finished(); +} + +#[tokio::test] +async fn task_spawn_loc_file_recorded() { + let task_span = expect::span() + .named("runtime.spawn") + .with_target("tokio::task") + .with_field(expect::field("loc.file").with_value(&file!())); + + let (subscriber, handle) = subscriber::mock().new_span(task_span).run_with_handle(); + + { + let _guard = tracing::subscriber::set_default(subscriber); + + tokio::spawn(futures::future::ready(())) + .await + .expect("failed to await join handle"); + } + + handle.assert_finished(); +} + +#[tokio::test] +async fn task_builder_name_recorded() { + let task_span = expect_task_named("test-task"); + + let (subscriber, handle) = subscriber::mock().new_span(task_span).run_with_handle(); + + { + let _guard = tracing::subscriber::set_default(subscriber); + task::Builder::new() + .name("test-task") + .spawn(futures::future::ready(())) + .unwrap() + .await + .expect("failed to await join handle"); + } + + handle.assert_finished(); +} + +#[tokio::test] +async fn task_builder_loc_file_recorded() { + let task_span = expect::span() + .named("runtime.spawn") + .with_target("tokio::task") + .with_field(expect::field("loc.file").with_value(&file!())); + + let (subscriber, handle) = subscriber::mock().new_span(task_span).run_with_handle(); + + { + let _guard = tracing::subscriber::set_default(subscriber); + + task::Builder::new() + .spawn(futures::future::ready(())) + .unwrap() + .await + .expect("failed to await join handle"); + } + + handle.assert_finished(); +} + +/// Expect a task with name +/// +/// This is a convenience function to create the expectation for a new task +/// with the `task.name` field set to the provided name. +fn expect_task_named(name: &str) -> NewSpan { + expect::span() + .named("runtime.spawn") + .with_target("tokio::task") + .with_field( + expect::field("task.name").with_value(&tracing::field::debug(format_args!("{}", name))), + ) +}