mirror of
https://github.com/tokio-rs/tracing.git
synced 2025-09-30 14:30:42 +00:00
mock: document public APIs in subscriber
module (#2446)
There has been interest around publishing `tracing-mock` to crates.io for some time. In order to make this possible, documentation and some code clean up is needed. The `subscriber` module needs documentation and examples. This change adds documentation to the `subscriber` module and all the public APIs within it. This includes doctests on all the methods which serve as examples. The `MockSubscriberBuilder::record` method was removed as its functionality is not implemented. Previously, the `MockSubscriber` would verify the scope of an `ExpectedEvent`, even if `in_scope` hadn't been called. In this case, that would check that an event was not in a span if `in_scope` had not been called. `tracing-subscriber` all adhere to this pattern. However it is different to the behavior of all other expectation methods, where an explicit call is needed to expect something, otherwise nothing is checked. As such, the behavior has been modified to align with the rest of the crate. The previous behavior can be achieved by calling `in_scope(None)` to verify that an event has no scope. The documentation for `in_scope` has been updated with an example for this case. The tests in `tracing-subscriber` which previously verified *implicitly* that an event had no scope (by not calling `in_scope` at all) have *not* been modified. It is my opinion that this implicit behavior was never required. Refs: #539
This commit is contained in:
parent
2cb1d63eca
commit
8c74eb8c91
@ -9,7 +9,7 @@
|
||||
//!
|
||||
//! ```
|
||||
//! use tracing::subscriber::with_default;
|
||||
//! use tracing_mock::{subscriber, expect};
|
||||
//! use tracing_mock::{expect, subscriber};
|
||||
//!
|
||||
//! let event = expect::event()
|
||||
//! .at_level(tracing::Level::INFO)
|
||||
@ -43,7 +43,7 @@ use std::fmt;
|
||||
pub struct ExpectedEvent {
|
||||
pub(super) fields: Option<field::ExpectedFields>,
|
||||
pub(super) parent: Option<Parent>,
|
||||
pub(super) in_spans: Vec<span::ExpectedSpan>,
|
||||
pub(super) in_spans: Option<Vec<span::ExpectedSpan>>,
|
||||
pub(super) metadata: ExpectedMetadata,
|
||||
}
|
||||
|
||||
@ -451,30 +451,28 @@ impl ExpectedEvent {
|
||||
/// recorded event, the expectation will fail.
|
||||
///
|
||||
/// **Note**: This validation currently only works with a
|
||||
/// [`MockSubscriber`]. If used with a [`MockCollector`], the
|
||||
/// [`MockLayer`]. If used with a [`MockSubscriber`], the
|
||||
/// expectation will fail directly as it is unimplemented.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, subscriber};
|
||||
/// use tracing_subscriber::{
|
||||
/// filter::filter_fn, registry, subscribe::CollectExt, util::SubscriberInitExt, Subscribe,
|
||||
/// };
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let event = expect::event().in_scope([
|
||||
/// expect::span().named("parent_span"),
|
||||
/// expect::span().named("grandparent_span")
|
||||
/// ]);
|
||||
///
|
||||
/// let (subscriber, handle) = subscriber::mock()
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(expect::span())
|
||||
/// .enter(expect::span())
|
||||
/// .event(event)
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = registry()
|
||||
/// .with(subscriber.with_filter(filter_fn(move |_meta| true)))
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// let grandparent = tracing::info_span!("grandparent_span");
|
||||
@ -489,23 +487,21 @@ impl ExpectedEvent {
|
||||
/// The scope must match exactly, otherwise the expectation will fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, subscriber};
|
||||
/// use tracing_subscriber::{
|
||||
/// filter::filter_fn, registry, subscribe::CollectExt, util::SubscriberInitExt, Subscribe,
|
||||
/// };
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let event = expect::event().in_scope([
|
||||
/// expect::span().named("parent_span"),
|
||||
/// expect::span().named("grandparent_span")
|
||||
/// ]);
|
||||
///
|
||||
/// let (subscriber, handle) = subscriber::mock()
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(expect::span())
|
||||
/// .event(event)
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = registry()
|
||||
/// .with(subscriber.with_filter(filter_fn(move |_meta| true)))
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// let parent = tracing::info_span!("parent_span");
|
||||
@ -514,12 +510,39 @@ impl ExpectedEvent {
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// It is also possible to test that an event has no parent spans
|
||||
/// by passing `None` to `in_scope`. If the event is within a
|
||||
/// span, the test will fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let event = expect::event().in_scope(None);
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(expect::span())
|
||||
/// .event(event)
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// let parent = tracing::info_span!("parent_span");
|
||||
/// let _guard = parent.enter();
|
||||
/// tracing::info!(field = &"value");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`MockLayer`]: struct@crate::layer::MockLayer
|
||||
/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
|
||||
/// [`MockCollector`]: struct@crate::subscriber::MockCollector
|
||||
#[cfg(feature = "tracing-subscriber")]
|
||||
pub fn in_scope(self, spans: impl IntoIterator<Item = span::ExpectedSpan>) -> Self {
|
||||
Self {
|
||||
in_spans: spans.into_iter().collect(),
|
||||
in_spans: Some(spans.into_iter().collect()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
@ -527,8 +550,8 @@ impl ExpectedEvent {
|
||||
/// Provides access to the expected scope (spans) for this expected
|
||||
/// event.
|
||||
#[cfg(feature = "tracing-subscriber")]
|
||||
pub(crate) fn scope_mut(&mut self) -> &mut [span::ExpectedSpan] {
|
||||
&mut self.in_spans[..]
|
||||
pub(crate) fn scope_mut(&mut self) -> Option<&mut [span::ExpectedSpan]> {
|
||||
self.in_spans.as_mut().map(|s| &mut s[..])
|
||||
}
|
||||
|
||||
pub(crate) fn check(
|
||||
@ -596,8 +619,8 @@ impl fmt::Debug for ExpectedEvent {
|
||||
s.field("parent", &format_args!("{:?}", parent));
|
||||
}
|
||||
|
||||
if !self.in_spans.is_empty() {
|
||||
s.field("in_spans", &self.in_spans);
|
||||
if let Some(in_spans) = &self.in_spans {
|
||||
s.field("in_spans", in_spans);
|
||||
}
|
||||
|
||||
s.finish()
|
||||
|
@ -1,8 +1,123 @@
|
||||
#![allow(missing_docs, dead_code)]
|
||||
//! An implementation of the [`Layer`] trait which validates that
|
||||
//! the `tracing` data it recieves matches the expected output for a test.
|
||||
//!
|
||||
//!
|
||||
//! The [`MockLayer`] is the central component in these tools. The
|
||||
//! `MockLayer` has expectations set on it which are later
|
||||
//! validated as the code under test is run.
|
||||
//!
|
||||
//! ```
|
||||
//! use tracing_mock::{expect, field, layer};
|
||||
//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
//!
|
||||
//! let (layer, handle) = layer::mock()
|
||||
//! // Expect a single event with a specified message
|
||||
//! .event(expect::event().with_fields(field::msg("droids")))
|
||||
//! .run_with_handle();
|
||||
//!
|
||||
//! // Use `set_default` to apply the `MockSubscriber` until the end
|
||||
//! // of the current scope (when the guard `_subscriber` is dropped).
|
||||
//! let _subscriber = tracing_subscriber::registry()
|
||||
//! .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
//! .set_default();
|
||||
//!
|
||||
//! // These *are* the droids we are looking for
|
||||
//! tracing::info!("droids");
|
||||
//!
|
||||
//! // Use the handle to check the assertions. This line will panic if an
|
||||
//! // assertion is not met.
|
||||
//! handle.assert_finished();
|
||||
//! ```
|
||||
//!
|
||||
//! A more complex example may consider multiple spans and events with
|
||||
//! their respective fields:
|
||||
//!
|
||||
//! ```
|
||||
//! use tracing_mock::{expect, field, layer};
|
||||
//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
//!
|
||||
//! let span = expect::span()
|
||||
//! .named("my_span");
|
||||
//! let (layer, handle) = layer::mock()
|
||||
//! // Enter a matching span
|
||||
//! .enter(span.clone())
|
||||
//! // Record an event with message "collect parting message"
|
||||
//! .event(expect::event().with_fields(field::msg("say hello")))
|
||||
//! // Exit a matching span
|
||||
//! .exit(span)
|
||||
//! // Expect no further messages to be recorded
|
||||
//! .only()
|
||||
//! // Return the layer and handle
|
||||
//! .run_with_handle();
|
||||
//!
|
||||
//! // Use `set_default` to apply the `MockLayers` until the end
|
||||
//! // of the current scope (when the guard `_subscriber` is dropped).
|
||||
//! let _subscriber = tracing_subscriber::registry()
|
||||
//! .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
//! .set_default();
|
||||
//!
|
||||
//! {
|
||||
//! let span = tracing::trace_span!(
|
||||
//! "my_span",
|
||||
//! greeting = "hello world",
|
||||
//! );
|
||||
//!
|
||||
//! let _guard = span.enter();
|
||||
//! tracing::info!("say hello");
|
||||
//! }
|
||||
//!
|
||||
//! // Use the handle to check the assertions. This line will panic if an
|
||||
//! // assertion is not met.
|
||||
//! handle.assert_finished();
|
||||
//! ```
|
||||
//!
|
||||
//! If we modify the previous example so that we **don't** enter the
|
||||
//! span before recording an event, the test will fail:
|
||||
//!
|
||||
//! ```should_panic
|
||||
//! use tracing_mock::{expect, field, layer};
|
||||
//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
//!
|
||||
//! let span = expect::span()
|
||||
//! .named("my_span");
|
||||
//! let (layer, handle) = layer::mock()
|
||||
//! // Enter a matching span
|
||||
//! .enter(span.clone())
|
||||
//! // Record an event with message "collect parting message"
|
||||
//! .event(expect::event().with_fields(field::msg("say hello")))
|
||||
//! // Exit a matching span
|
||||
//! .exit(span)
|
||||
//! // Expect no further messages to be recorded
|
||||
//! .only()
|
||||
//! // Return the subscriber and handle
|
||||
//! .run_with_handle();
|
||||
//!
|
||||
//! // Use `set_default` to apply the `MockSubscriber` until the end
|
||||
//! // of the current scope (when the guard `_subscriber` is dropped).
|
||||
//! let _subscriber = tracing_subscriber::registry()
|
||||
//! .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
//! .set_default();
|
||||
//!
|
||||
//! {
|
||||
//! let span = tracing::trace_span!(
|
||||
//! "my_span",
|
||||
//! greeting = "hello world",
|
||||
//! );
|
||||
//!
|
||||
//! // Don't enter the span.
|
||||
//! // let _guard = span.enter();
|
||||
//! tracing::info!("say hello");
|
||||
//! }
|
||||
//!
|
||||
//! // Use the handle to check the assertions. This line will panic if an
|
||||
//! // assertion is not met.
|
||||
//! handle.assert_finished();
|
||||
//! ```
|
||||
//!
|
||||
//! [`Layer`]: trait@tracing_subscriber::layer::Layer
|
||||
use crate::{
|
||||
event::ExpectedEvent,
|
||||
expect::Expect,
|
||||
field::ExpectedFields,
|
||||
span::{ExpectedSpan, NewSpan},
|
||||
subscriber::MockHandle,
|
||||
};
|
||||
@ -21,6 +136,55 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
/// Create a [`MockLayerBuilder`] used to construct a
|
||||
/// [`MockLayer`].
|
||||
///
|
||||
/// For additional information and examples, see the [`layer`]
|
||||
/// module and [`MockLayerBuilder`] documentation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, field, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .named("my_span");
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// // Enter a matching span
|
||||
/// .enter(span.clone())
|
||||
/// // Record an event with message "collect parting message"
|
||||
/// .event(expect::event().with_fields(field::msg("say hello")))
|
||||
/// // Exit a matching span
|
||||
/// .exit(span)
|
||||
/// // Expect no further messages to be recorded
|
||||
/// .only()
|
||||
/// // Return the subscriber and handle
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// // Use `set_default` to apply the `MockSubscriber` until the end
|
||||
/// // of the current scope (when the guard `_subscriber` is dropped).
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// {
|
||||
/// let span = tracing::trace_span!(
|
||||
/// "my_span",
|
||||
/// greeting = "hello world",
|
||||
/// );
|
||||
///
|
||||
/// let _guard = span.enter();
|
||||
/// tracing::info!("say hello");
|
||||
/// }
|
||||
///
|
||||
/// // Use the handle to check the assertions. This line will panic if an
|
||||
/// // assertion is not met.
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`layer`]: mod@crate::layer
|
||||
#[must_use]
|
||||
pub fn mock() -> MockLayerBuilder {
|
||||
MockLayerBuilder {
|
||||
expected: Default::default(),
|
||||
@ -31,15 +195,81 @@ pub fn mock() -> MockLayerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`MockLayerBuilder`] with a name already set.
|
||||
///
|
||||
/// This constructor is equivalent to calling
|
||||
/// [`MockLayerBuilder::named`] in the following way"
|
||||
/// `layer::mock().named(name)`.
|
||||
///
|
||||
/// For additional information and examples, see the [`layer`]
|
||||
/// module and [`MockLayerBuilder`] documentation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The example from [`named`] could be rewritten as:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer_1, handle_1) = layer::named("subscriber-1")
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let (layer_2, handle_2) = layer::named("subscriber-2")
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(
|
||||
/// layer_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
|
||||
/// )
|
||||
/// .set_default();
|
||||
/// {
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(
|
||||
/// layer_1
|
||||
/// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
|
||||
/// )
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("a");
|
||||
/// }
|
||||
///
|
||||
/// handle_1.assert_finished();
|
||||
/// handle_2.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`MockLayerBuilder::named`]: fn@crate::layer::MockLayerBuilder::named
|
||||
/// [`layer`]: mod@crate::layer
|
||||
#[must_use]
|
||||
pub fn named(name: impl std::fmt::Display) -> MockLayerBuilder {
|
||||
mock().named(name)
|
||||
}
|
||||
|
||||
/// A builder for constructing [`MockLayer`]s.
|
||||
///
|
||||
/// The methods on this builder set expectations which are then
|
||||
/// validated by the constructed [`MockLayer`].
|
||||
///
|
||||
/// For a detailed description and examples see the documentation
|
||||
/// for the methods and the [`layer`] module.
|
||||
///
|
||||
/// [`layer`]: mod@crate::layer
|
||||
|
||||
pub struct MockLayerBuilder {
|
||||
expected: VecDeque<Expect>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// A layer which validates the traces it receives.
|
||||
///
|
||||
/// A `MockLayer` is constructed with a
|
||||
/// [`MockLayerBuilder`]. For a detailed description and examples,
|
||||
/// see the documentation for that struct and for the [`layer`]
|
||||
/// module.
|
||||
///
|
||||
/// [`layer`]: mod@crate::layer
|
||||
pub struct MockLayer {
|
||||
expected: Arc<Mutex<VecDeque<Expect>>>,
|
||||
current: Mutex<Vec<Id>>,
|
||||
@ -58,8 +288,61 @@ impl MockLayerBuilder {
|
||||
/// When a test has only one mock layer, this is sufficient. However,
|
||||
/// some tests may include multiple layers, in order to test
|
||||
/// interactions between multiple layers. In that case, it can be
|
||||
/// helpful to give each layer a separate name to distinguish where the
|
||||
/// helpful to give each layers a separate name to distinguish where the
|
||||
/// debugging output comes from.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// In the following example, we create two layers, both
|
||||
/// expecting to receive an event. As we only record a single
|
||||
/// event, the test will fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{layer, expect};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer_1, handle_1) = layer::mock()
|
||||
/// .named("layer-1")
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let (layer_2, handle_2) = layer::mock()
|
||||
/// .named("layer-2")
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(
|
||||
/// layer_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
|
||||
/// )
|
||||
/// .set_default();
|
||||
/// {
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(
|
||||
/// layer_1
|
||||
/// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
|
||||
/// )
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("a");
|
||||
/// }
|
||||
///
|
||||
/// handle_1.assert_finished();
|
||||
/// handle_2.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// In the test output, we see that the layer which didn't
|
||||
/// received the event was the one named `layer-2`, which is
|
||||
/// correct as the layer named `layer-1` was the default
|
||||
/// when the event was recorded:
|
||||
///
|
||||
/// ```text
|
||||
/// [main::layer-2] more notifications expected: [
|
||||
/// Event(
|
||||
/// MockEvent,
|
||||
/// ),
|
||||
/// ]', tracing-mock/src/subscriber.rs:472:13
|
||||
/// ```
|
||||
pub fn named(mut self, name: impl fmt::Display) -> Self {
|
||||
use std::fmt::Write;
|
||||
if !self.name.is_empty() {
|
||||
@ -70,34 +353,125 @@ impl MockLayerBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn enter(mut self, span: ExpectedSpan) -> Self {
|
||||
self.expected.push_back(Expect::Enter(span));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an expectation that an event matching the [`ExpectedEvent`]
|
||||
/// will be recorded next.
|
||||
///
|
||||
/// The `event` can be a default mock which will match any event
|
||||
/// (`expect::event()`) or can include additional expectations.
|
||||
/// See the [`ExpectedEvent`] documentation for more details.
|
||||
///
|
||||
/// If an event is recorded that doesn't match the `ExpectedEvent`,
|
||||
/// or if something else (such as entering a span) is recorded
|
||||
/// first, then the expectation will fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("event");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// A span is entered before the event, causing the test to fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// let span = tracing::info_span!("span");
|
||||
/// let _guard = span.enter();
|
||||
/// tracing::info!("event");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
pub fn event(mut self, event: ExpectedEvent) -> Self {
|
||||
self.expected.push_back(Expect::Event(event));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn exit(mut self, span: ExpectedSpan) -> Self {
|
||||
self.expected.push_back(Expect::Exit(span));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn only(mut self) -> Self {
|
||||
self.expected.push_back(Expect::Nothing);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn record<I>(mut self, span: ExpectedSpan, fields: I) -> Self
|
||||
where
|
||||
I: Into<ExpectedFields>,
|
||||
{
|
||||
self.expected.push_back(Expect::Visit(span, fields.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an expectation that the creation of a span will be
|
||||
/// recorded next.
|
||||
///
|
||||
/// This function accepts `Into<NewSpan>` instead of
|
||||
/// [`ExpectedSpan`] directly. [`NewSpan`] can be used to test
|
||||
/// span fields and the span parent.
|
||||
///
|
||||
/// The new span doesn't need to be entered for this expectation
|
||||
/// to succeed.
|
||||
///
|
||||
/// If a span is recorded that doesn't match the `ExpectedSpan`,
|
||||
/// or if something else (such as an event) is recorded first,
|
||||
/// then the expectation will fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing")
|
||||
/// .with_field(expect::field("testing").with_value(&"yes"));
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .new_span(span)
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// _ = tracing::info_span!("the span we're testing", testing = "yes");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// An event is recorded before the span is created, causing the
|
||||
/// test to fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing")
|
||||
/// .with_field(expect::field("testing").with_value(&"yes"));
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .new_span(span)
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("an event");
|
||||
/// _ = tracing::info_span!("the span we're testing", testing = "yes");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`ExpectedSpan`]: struct@crate::span::ExpectedSpan
|
||||
/// [`NewSpan`]: struct@crate::span::NewSpan
|
||||
pub fn new_span<I>(mut self, new_span: I) -> Self
|
||||
where
|
||||
I: Into<NewSpan>,
|
||||
@ -106,6 +480,252 @@ impl MockLayerBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an expectation that entering a span matching the
|
||||
/// [`ExpectedSpan`] will be recorded next.
|
||||
///
|
||||
/// This expectation is generally accompanied by a call to
|
||||
/// [`exit`], since an entered span will typically be exited. If used
|
||||
/// together with [`only`], this is likely necessary, because the span
|
||||
/// will be dropped before the test completes (except in rare cases,
|
||||
/// such as if [`std::mem::forget`] is used).
|
||||
///
|
||||
/// If the span that is entered doesn't match the [`ExpectedSpan`],
|
||||
/// or if something else (such as an event) is recorded first,
|
||||
/// then the expectation will fail.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing");
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(span.clone())
|
||||
/// .exit(span)
|
||||
/// .only()
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// {
|
||||
/// let span = tracing::info_span!("the span we're testing");
|
||||
/// let _entered = span.enter();
|
||||
/// }
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// An event is recorded before the span is entered, causing the
|
||||
/// test to fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing");
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(span.clone())
|
||||
/// .exit(span)
|
||||
/// .only()
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// {
|
||||
/// tracing::info!("an event");
|
||||
/// let span = tracing::info_span!("the span we're testing");
|
||||
/// let _entered = span.enter();
|
||||
/// }
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`exit`]: fn@Self::exit
|
||||
/// [`only`]: fn@Self::only
|
||||
pub fn enter(mut self, span: ExpectedSpan) -> Self {
|
||||
self.expected.push_back(Expect::Enter(span));
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an expectation that exiting a span matching the
|
||||
/// [`ExpectedSpan`] will be recorded next.
|
||||
///
|
||||
/// As a span may be entered and exited multiple times,
|
||||
/// this is different from the span being closed. In
|
||||
/// general [`enter`] and `exit` should be paired.
|
||||
///
|
||||
/// If the span that is exited doesn't match the [`ExpectedSpan`],
|
||||
/// or if something else (such as an event) is recorded first,
|
||||
/// then the expectation will fail.
|
||||
///
|
||||
/// **Note**: Ensure that the guard returned by [`Span::enter`]
|
||||
/// is dropped before calling [`MockHandle::assert_finished`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing");
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(span.clone())
|
||||
/// .exit(span)
|
||||
/// .only()
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
/// {
|
||||
/// let span = tracing::info_span!("the span we're testing");
|
||||
/// let _entered = span.enter();
|
||||
/// }
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// An event is recorded before the span is exited, causing the
|
||||
/// test to fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let span = expect::span()
|
||||
/// .at_level(tracing::Level::INFO)
|
||||
/// .named("the span we're testing");
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .enter(span.clone())
|
||||
/// .exit(span)
|
||||
/// .only()
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// {
|
||||
/// let span = tracing::info_span!("the span we're testing");
|
||||
/// let _entered = span.enter();
|
||||
/// tracing::info!("an event");
|
||||
/// }
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`enter`]: fn@Self::enter
|
||||
/// [`MockHandle::assert_finished`]: fn@crate::subscriber::MockHandle::assert_finished
|
||||
/// [`Span::enter`]: fn@tracing::Span::enter
|
||||
pub fn exit(mut self, span: ExpectedSpan) -> Self {
|
||||
self.expected.push_back(Expect::Exit(span));
|
||||
self
|
||||
}
|
||||
|
||||
/// Expects that no further traces are received.
|
||||
///
|
||||
/// The call to `only` should appear immediately before the final
|
||||
/// call to [`run`] or [`run_with_handle`], as any expectations which
|
||||
/// are added after `only` will not be considered.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Consider this simple test. It passes even though we only
|
||||
/// expect a single event, but receive three:
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("a");
|
||||
/// tracing::info!("b");
|
||||
/// tracing::info!("c");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// After including `only`, the test will fail:
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .event(expect::event())
|
||||
/// .only()
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("a");
|
||||
/// tracing::info!("b");
|
||||
/// tracing::info!("c");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`run`]: fn@Self::run
|
||||
/// [`run_with_handle`]: fn@Self::run_with_handle
|
||||
pub fn only(mut self) -> Self {
|
||||
self.expected.push_back(Expect::Nothing);
|
||||
self
|
||||
}
|
||||
|
||||
/// Consume this builder and return a [`MockLayer`] which can
|
||||
/// be set as the default subscriber.
|
||||
///
|
||||
/// This function is similar to [`run_with_handle`], but it doesn't
|
||||
/// return a [`MockHandle`]. This is useful if the desired
|
||||
/// assertions can be checked externally to the subscriber.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following test is used within the `tracing-subscriber`
|
||||
/// codebase:
|
||||
///
|
||||
/// ```
|
||||
/// use tracing::Subscriber;
|
||||
/// use tracing_mock::layer;
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let unfiltered = layer::named("unfiltered").run().boxed();
|
||||
/// let info = layer::named("info")
|
||||
/// .run()
|
||||
/// .with_filter(tracing_core::LevelFilter::INFO)
|
||||
/// .boxed();
|
||||
/// let debug = layer::named("debug")
|
||||
/// .run()
|
||||
/// .with_filter(tracing_core::LevelFilter::DEBUG)
|
||||
/// .boxed();
|
||||
///
|
||||
/// let subscriber = tracing_subscriber::registry().with(vec![unfiltered, info, debug]);
|
||||
///
|
||||
/// assert_eq!(subscriber.max_level_hint(), None);
|
||||
/// ```
|
||||
///
|
||||
/// [`MockHandle`]: struct@crate::subscriber::MockHandle
|
||||
/// [`run_with_handle`]: fn@Self::run_with_handle
|
||||
pub fn run(self) -> MockLayer {
|
||||
MockLayer {
|
||||
expected: Arc::new(Mutex::new(self.expected)),
|
||||
@ -114,15 +734,40 @@ impl MockLayerBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume this builder and return a [`MockLayer`] which can
|
||||
/// be set as the default subscriber and a [`MockHandle`] which can
|
||||
/// be used to validate the provided expectations.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tracing_mock::{expect, layer};
|
||||
/// use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||
///
|
||||
/// let (layer, handle) = layer::mock()
|
||||
/// .event(expect::event())
|
||||
/// .run_with_handle();
|
||||
///
|
||||
/// let _subscriber = tracing_subscriber::registry()
|
||||
/// .with(layer.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
|
||||
/// .set_default();
|
||||
///
|
||||
/// tracing::info!("event");
|
||||
///
|
||||
/// handle.assert_finished();
|
||||
/// ```
|
||||
///
|
||||
/// [`MockHandle`]: struct@crate::subscriber::MockHandle
|
||||
/// [`MockLayer`]: struct@crate::layer::MockLayer
|
||||
pub fn run_with_handle(self) -> (MockLayer, MockHandle) {
|
||||
let expected = Arc::new(Mutex::new(self.expected));
|
||||
let handle = MockHandle::new(expected.clone(), self.name.clone());
|
||||
let layer = MockLayer {
|
||||
let subscriber = MockLayer {
|
||||
expected,
|
||||
name: self.name,
|
||||
current: Mutex::new(Vec::new()),
|
||||
};
|
||||
(layer, handle)
|
||||
(subscriber, handle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,6 +830,46 @@ impl MockLayer {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_event_scope<C>(
|
||||
&self,
|
||||
current_scope: Option<tracing_subscriber::registry::Scope<'_, C>>,
|
||||
expected_scope: &mut [ExpectedSpan],
|
||||
) where
|
||||
C: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
|
||||
{
|
||||
let mut current_scope = current_scope.into_iter().flatten();
|
||||
let mut i = 0;
|
||||
for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
|
||||
println!(
|
||||
"[{}] event_scope[{}] actual={} ({:?}); expected={}",
|
||||
self.name,
|
||||
i,
|
||||
actual.name(),
|
||||
actual.id(),
|
||||
expected
|
||||
);
|
||||
self.check_span_ref(
|
||||
expected,
|
||||
&actual,
|
||||
format_args!("the {}th span in the event's scope to be", i),
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
let remaining_expected = &expected_scope[i..];
|
||||
assert!(
|
||||
remaining_expected.is_empty(),
|
||||
"\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
|
||||
self.name,
|
||||
self.name,
|
||||
remaining_expected,
|
||||
);
|
||||
assert!(
|
||||
current_scope.next().is_none(),
|
||||
"\n[{}] did not expect all spans in the actual event scope!",
|
||||
self.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Layer<C> for MockLayer
|
||||
@ -221,45 +906,21 @@ where
|
||||
Some(Expect::Event(mut expected)) => {
|
||||
let get_parent_name = || cx.event_span(event).map(|span| span.name().to_string());
|
||||
expected.check(event, get_parent_name, &self.name);
|
||||
let mut current_scope = cx.event_scope(event).into_iter().flatten();
|
||||
let expected_scope = expected.scope_mut();
|
||||
let mut i = 0;
|
||||
for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
|
||||
println!(
|
||||
"[{}] event_scope[{}] actual={} ({:?}); expected={}",
|
||||
self.name,
|
||||
i,
|
||||
actual.name(),
|
||||
actual.id(),
|
||||
expected
|
||||
);
|
||||
self.check_span_ref(
|
||||
expected,
|
||||
&actual,
|
||||
format_args!("the {}th span in the event's scope to be", i),
|
||||
);
|
||||
i += 1;
|
||||
|
||||
if let Some(expected_scope) = expected.scope_mut() {
|
||||
self.check_event_scope(cx.event_scope(event), expected_scope);
|
||||
}
|
||||
let remaining_expected = &expected_scope[i..];
|
||||
assert!(
|
||||
remaining_expected.is_empty(),
|
||||
"\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
|
||||
self.name,
|
||||
self.name,
|
||||
remaining_expected,
|
||||
);
|
||||
assert!(
|
||||
current_scope.next().is_none(),
|
||||
"\n[{}] did not expect all spans in the actual event scope!",
|
||||
self.name,
|
||||
);
|
||||
}
|
||||
Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, C>) {
|
||||
// TODO: it should be possible to expect spans to follow from other spans
|
||||
unimplemented!(
|
||||
"so far, we don't have any tests that need an `on_follows_from` \
|
||||
implementation.\nif you just wrote one that does, feel free to \
|
||||
implement it!"
|
||||
);
|
||||
}
|
||||
|
||||
fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, C>) {
|
||||
@ -377,13 +1038,13 @@ where
|
||||
}
|
||||
|
||||
fn on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, C>) {
|
||||
panic!("well-behaved Layers should never do this to us, lol");
|
||||
panic!("well-behaved subscribers should never do this to us, lol");
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MockLayer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut s = f.debug_struct("Expectlayer");
|
||||
let mut s = f.debug_struct("ExpectSubscriber");
|
||||
s.field("name", &self.name);
|
||||
|
||||
if let Ok(expected) = self.expected.try_lock() {
|
||||
|
@ -155,14 +155,14 @@ where
|
||||
}
|
||||
|
||||
pub fn run(self) -> impl Subscriber {
|
||||
let (subscriber, _) = self.run_with_handle();
|
||||
subscriber
|
||||
let (collector, _) = self.run_with_handle();
|
||||
collector
|
||||
}
|
||||
|
||||
pub fn run_with_handle(self) -> (impl Subscriber, MockHandle) {
|
||||
let expected = Arc::new(Mutex::new(self.expected));
|
||||
let handle = MockHandle(expected.clone(), self.name.clone());
|
||||
let subscriber = Running {
|
||||
let collector = Running {
|
||||
spans: Mutex::new(HashMap::new()),
|
||||
expected,
|
||||
current: Mutex::new(Vec::new()),
|
||||
@ -171,7 +171,7 @@ where
|
||||
max_level: self.max_level,
|
||||
name: self.name,
|
||||
};
|
||||
(subscriber, handle)
|
||||
(collector, handle)
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,6 +229,14 @@ where
|
||||
match self.expected.lock().unwrap().pop_front() {
|
||||
None => {}
|
||||
Some(Expect::Event(mut expected)) => {
|
||||
#[cfg(feature = "tracing-subscriber")]
|
||||
{
|
||||
if expected.scope_mut().is_some() {
|
||||
unimplemented!(
|
||||
"Expected scope for events is not supported with `MockSubscriber`."
|
||||
)
|
||||
}
|
||||
}
|
||||
let get_parent_name = || {
|
||||
let stack = self.current.lock().unwrap();
|
||||
let spans = self.spans.lock().unwrap();
|
||||
@ -454,6 +462,7 @@ where
|
||||
}
|
||||
|
||||
impl MockHandle {
|
||||
#[cfg(feature = "tracing-subscriber")]
|
||||
pub(crate) fn new(expected: Arc<Mutex<VecDeque<Expect>>>, name: String) -> Self {
|
||||
Self(expected, name)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user