mock: document public APIs in span module (#2442)

This change adds documentation to the tracing-mock span module and all
the public APIs within it. This includes doctests on all the methods
which serve as examples.

Additionally, the validation on `ExpectedSpan` was improved so that it
validates the level and target during `enter` and `exit` as well as on
`new_span`.

The method `ExpectedSpan::with_field` was renamed to `with_fields`
(plural) to match the same method on `ExpectedEvent` (and because
multiple fields can be passed to it).

A copy-paste typo was also fixed in the documentation for
`ExpectedEvent::with_contextual_parent`.

Refs: #539

Co-authored-by: David Barsky <me@davidbarsky.com>
This commit is contained in:
Hayden Stainsby
2023-11-14 10:57:14 +01:00
parent 70a867877d
commit 2ac64c7e28
14 changed files with 599 additions and 80 deletions

View File

@@ -199,8 +199,8 @@ fn async_fn_with_async_trait() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("self"))
.with_field(expect::field("v")),
.with_fields(expect::field("self"))
.with_fields(expect::field("v")),
)
.enter(span.clone())
.new_span(span3.clone())
@@ -210,7 +210,7 @@ fn async_fn_with_async_trait() {
.enter(span3.clone())
.exit(span3.clone())
.drop_span(span3)
.new_span(span2.clone().with_field(expect::field("self")))
.new_span(span2.clone().with_fields(expect::field("self")))
.enter(span2.clone())
.event(expect::event().with_fields(expect::field("val").with_value(&5u64)))
.exit(span2.clone())
@@ -260,7 +260,7 @@ fn async_fn_with_async_trait_and_fields_expressions() {
let span = expect::span().named("call");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("_v")
.with_value(&5usize)
.and(expect::field("test").with_value(&tracing::field::debug(10)))
@@ -330,7 +330,7 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
let span4 = expect::span().named("sync_fun");
let (subscriber, handle) = subscriber::mock()
/*.new_span(span.clone()
.with_field(
.with_fields(
expect::field("Self").with_value(&"TestImpler")))
.enter(span.clone())
.exit(span.clone())
@@ -338,13 +338,13 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
.new_span(
span2
.clone()
.with_field(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
.with_fields(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
)
.enter(span2.clone())
.new_span(
span4
.clone()
.with_field(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
.with_fields(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
)
.enter(span4.clone())
.exit(span4.clone())
@@ -357,7 +357,7 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
.new_span(
span3
.clone()
.with_field(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
.with_fields(expect::field("Self").with_value(&std::any::type_name::<TestImpl>())),
)
.enter(span3.clone())
.exit(span3.clone())

View File

@@ -11,7 +11,7 @@ fn destructure_tuples() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("1"))
.and(expect::field("arg2").with_value(&format_args!("2")))
@@ -40,7 +40,7 @@ fn destructure_nested_tuples() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("1"))
.and(expect::field("arg2").with_value(&format_args!("2")))
@@ -72,7 +72,7 @@ fn destructure_refs() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("arg1").with_value(&1usize).only()),
.with_fields(expect::field("arg1").with_value(&1usize).only()),
)
.enter(span.clone())
.exit(span.clone())
@@ -98,7 +98,7 @@ fn destructure_tuple_structs() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("1"))
.and(expect::field("arg2").with_value(&format_args!("2")))
@@ -139,7 +139,7 @@ fn destructure_structs() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("1"))
.and(expect::field("arg2").with_value(&format_args!("2")))
@@ -184,7 +184,7 @@ fn destructure_everything() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("1"))
.and(expect::field("arg2").with_value(&format_args!("2")))

View File

@@ -159,7 +159,7 @@ fn impl_trait_return_type() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("x").with_value(&10usize).only()),
.with_fields(expect::field("x").with_value(&10usize).only()),
)
.enter(span.clone())
.exit(span.clone())

View File

@@ -46,7 +46,7 @@ impl HasField {
#[test]
fn fields() {
let span = expect::span().with_field(
let span = expect::span().with_fields(
expect::field("foo")
.with_value(&"bar")
.and(expect::field("dsa").with_value(&true))
@@ -60,7 +60,7 @@ fn fields() {
#[test]
fn expr_field() {
let span = expect::span().with_field(
let span = expect::span().with_fields(
expect::field("s")
.with_value(&"hello world")
.and(expect::field("len").with_value(&"hello world".len()))
@@ -73,7 +73,7 @@ fn expr_field() {
#[test]
fn two_expr_fields() {
let span = expect::span().with_field(
let span = expect::span().with_fields(
expect::field("s")
.with_value(&"hello world")
.and(expect::field("s.len").with_value(&"hello world".len()))
@@ -87,7 +87,7 @@ fn two_expr_fields() {
#[test]
fn clashy_expr_field() {
let span = expect::span().with_field(
let span = expect::span().with_fields(
// Overriding the `s` field should record `s` as a `Display` value,
// rather than as a `Debug` value.
expect::field("s")
@@ -99,7 +99,7 @@ fn clashy_expr_field() {
fn_clashy_expr_field("hello world");
});
let span = expect::span().with_field(expect::field("s").with_value(&"s").only());
let span = expect::span().with_fields(expect::field("s").with_value(&"s").only());
run_test(span, || {
fn_clashy_expr_field2("hello world");
});
@@ -108,7 +108,7 @@ fn clashy_expr_field() {
#[test]
fn self_expr_field() {
let span =
expect::span().with_field(expect::field("my_field").with_value(&"hello world").only());
expect::span().with_fields(expect::field("my_field").with_value(&"hello world").only());
run_test(span, || {
let has_field = HasField {
my_field: "hello world",
@@ -119,7 +119,7 @@ fn self_expr_field() {
#[test]
fn parameters_with_fields() {
let span = expect::span().with_field(
let span = expect::span().with_fields(
expect::field("foo")
.with_value(&"bar")
.and(expect::field("param").with_value(&1u32))
@@ -132,7 +132,7 @@ fn parameters_with_fields() {
#[test]
fn empty_field() {
let span = expect::span().with_field(expect::field("foo").with_value(&"bar").only());
let span = expect::span().with_fields(expect::field("foo").with_value(&"bar").only());
run_test(span, || {
fn_empty_field();
});
@@ -140,7 +140,7 @@ fn empty_field() {
#[test]
fn string_field() {
let span = expect::span().with_field(expect::field("s").with_value(&"hello world").only());
let span = expect::span().with_fields(expect::field("s").with_value(&"hello world").only());
run_test(span, || {
fn_string(String::from("hello world"));
});

View File

@@ -64,7 +64,7 @@ fn fields() {
.with_target("my_target");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&2usize)
.and(expect::field("arg2").with_value(&false))
@@ -76,7 +76,7 @@ fn fields() {
.exit(span.clone())
.drop_span(span)
.new_span(
span2.clone().with_field(
span2.clone().with_fields(
expect::field("arg1")
.with_value(&3usize)
.and(expect::field("arg2").with_value(&true))
@@ -126,7 +126,7 @@ fn skip() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("arg1").with_value(&2usize).only()),
.with_fields(expect::field("arg1").with_value(&2usize).only()),
)
.enter(span.clone())
.exit(span.clone())
@@ -134,7 +134,7 @@ fn skip() {
.new_span(
span2
.clone()
.with_field(expect::field("arg1").with_value(&3usize).only()),
.with_fields(expect::field("arg1").with_value(&3usize).only()),
)
.enter(span2.clone())
.exit(span2.clone())
@@ -171,7 +171,7 @@ fn generics() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("arg1")
.with_value(&format_args!("Foo"))
.and(expect::field("arg2").with_value(&format_args!("false"))),
@@ -204,7 +204,7 @@ fn methods() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
span.clone().with_fields(
expect::field("self")
.with_value(&format_args!("Foo"))
.and(expect::field("arg1").with_value(&42usize)),
@@ -236,7 +236,7 @@ fn impl_trait_return_type() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("x").with_value(&10usize).only()),
.with_fields(expect::field("x").with_value(&10usize).only()),
)
.enter(span.clone())
.exit(span.clone())

View File

@@ -121,7 +121,7 @@ let span = expect::span().named("yak_shaving");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(expect::field("number_of_yaks").with_value(&yak_count).only()),
.with_fields(expect::field("number_of_yaks").with_value(&yak_count).only()),
)
.enter(span.clone())
.event(

View File

@@ -364,7 +364,7 @@ impl ExpectedEvent {
///
/// # Examples
///
/// The explicit parent is matched by name:
/// The contextual parent is matched by name:
///
/// ```
/// use tracing::subscriber::with_default;

View File

@@ -1,16 +1,128 @@
//! Define expectations to match and validate spans.
//!
//! The [`ExpectedSpan`] and [`NewSpan`] structs define expectations
//! for spans to be matched by the mock subscriber API in the
//! [`subscriber`] module.
//!
//! Expected spans should be created with [`expect::span`] and a
//! chain of method calls describing the assertions made about the
//! span. Expectations about the lifecycle of the span can be set on the [`MockSubscriber`].
//!
//! # Examples
//!
//! ```
//! use tracing_mock::{subscriber, expect};
//!
//! let span = expect::span()
//! .named("interesting_span")
//! .at_level(tracing::Level::INFO);
//!
//! let (subscriber, handle) = subscriber::mock()
//! .enter(span.clone())
//! .exit(span)
//! .run_with_handle();
//!
//! tracing::collect::with_default(subscriber, || {
//! let span = tracing::info_span!("interesting_span");
//! let _guard = span.enter();
//! });
//!
//! handle.assert_finished();
//! ```
//!
//! The following example asserts the name, level, parent, and fields of the span:
//!
//! ```
//! use tracing_mock::{subscriber, expect};
//!
//! let span = expect::span()
//! .named("interesting_span")
//! .at_level(tracing::Level::INFO);
//! let new_span = span
//! .clone()
//! .with_fields(expect::field("field.name").with_value(&"field_value"))
//! .with_explicit_parent(Some("parent_span"));
//!
//! let (subscriber, handle) = subscriber::mock()
//! .new_span(expect::span().named("parent_span"))
//! .new_span(new_span)
//! .enter(span.clone())
//! .exit(span)
//! .run_with_handle();
//!
//! tracing::collect::with_default(subscriber, || {
//! let parent = tracing::info_span!("parent_span");
//!
//! let span = tracing::info_span!(
//! parent: parent.id(),
//! "interesting_span",
//! field.name = "field_value",
//! );
//! let _guard = span.enter();
//! });
//!
//! handle.assert_finished();
//! ```
//!
//! All expectations must be met for the test to pass. For example,
//! the following test will fail due to a mismatch in the spans' names:
//!
//! ```should_panic
//! use tracing_mock::{subscriber, expect};
//!
//! let span = expect::span()
//! .named("interesting_span")
//! .at_level(tracing::Level::INFO);
//!
//! let (subscriber, handle) = subscriber::mock()
//! .enter(span.clone())
//! .exit(span)
//! .run_with_handle();
//!
//! tracing::collect::with_default(subscriber, || {
//! let span = tracing::info_span!("another_span");
//! let _guard = span.enter();
//! });
//!
//! handle.assert_finished();
//! ```
//!
//! [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
//! [`subscriber`]: mod@crate::subscriber
//! [`expect::span`]: fn@crate::expect::span
#![allow(missing_docs)]
use super::{expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent};
use crate::{
subscriber::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent,
};
use std::fmt;
/// A mock span.
///
/// This is intended for use with the mock subscriber API in the
/// `subscriber` module.
/// [`subscriber`] module.
///
/// [`subscriber`]: mod@crate::subscriber
#[derive(Clone, Default, Eq, PartialEq)]
pub struct ExpectedSpan {
pub(crate) metadata: ExpectedMetadata,
}
/// A mock new span.
///
/// **Note**: This struct contains expectations that can only be asserted
/// on when expecting a new span via [`MockSubscriber::new_span`]. They
/// cannot be validated on [`MockSubscriber::enter`],
/// [`MockSubscriber::exit`], or any other method on [`MockSubscriber`]
/// that takes an `ExpectedSpan`.
///
/// For more details on how to use this struct, see the documentation
/// on the [`subscriber`] module.
///
/// [`subscriber`]: mod@crate::subscriber
/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
/// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
/// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
/// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::new_span
#[derive(Default, Eq, PartialEq)]
pub struct NewSpan {
pub(crate) span: ExpectedSpan,
@@ -26,6 +138,47 @@ where
}
impl ExpectedSpan {
/// Sets a name to expect when matching a span.
///
/// If an event is recorded with a name that differs from the one provided to this method, the expectation will fail.
///
/// # Examples
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span().named("span name");
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::info_span!("span name");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
///
/// When the span name is different, the assertion will fail:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span().named("span name");
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::info_span!("a different span name");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
pub fn named<I>(self, name: I) -> Self
where
I: Into<String>,
@@ -38,6 +191,50 @@ impl ExpectedSpan {
}
}
/// Sets the [`Level`](tracing::Level) to expect when matching a span.
///
/// If an span is record with a level that differs from the one provided to this method, the expectation will fail.
///
/// # Examples
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .at_level(tracing::Level::INFO);
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::info_span!("span");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
///
/// Expecting a span at `INFO` level will fail if the event is
/// recorded at any other level:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .at_level(tracing::Level::INFO);
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::warn_span!("a serious span");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
pub fn at_level(self, level: tracing::Level) -> Self {
Self {
metadata: ExpectedMetadata {
@@ -47,6 +244,50 @@ impl ExpectedSpan {
}
}
/// Sets the target to expect when matching a span.
///
/// If an event is recorded with a target that doesn't match the
/// provided target, this expectation will fail.
///
/// # Examples
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_target("some_target");
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::info_span!(target: "some_target", "span");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
///
/// The test will fail if the target is different:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_target("some_target");
///
/// let (subscriber, handle) = subscriber::mock()
/// .enter(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let span = tracing::info_span!(target: "a_different_target", "span");
/// let _guard = span.enter();
/// });
///
/// handle.assert_finished();
/// ```
pub fn with_target<I>(self, target: I) -> Self
where
I: Into<String>,
@@ -59,6 +300,100 @@ impl ExpectedSpan {
}
}
/// Configures this `ExpectedSpan` to expect an explicit parent
/// span or to be an explicit root.
///
/// **Note**: This method returns a [`NewSpan`] and as such, this
/// expectation can only be validated when expecting a new span via
/// [`MockSubscriber::new_span`]. It cannot be validated on
/// [`MockSubscriber::enter`], [`MockSubscriber::exit`], or any other
/// method on [`MockSubscriber`] that takes an `ExpectedSpan`.
///
/// An _explicit_ parent span is one passed to the `span!` macro in the
/// `parent:` field.
///
/// If `Some("parent_name")` is passed to `with_explicit_parent` then,
/// the provided string is the name of the parent span to expect.
///
/// To expect that a span is recorded with no parent, `None`
/// can be passed to `with_explicit_parent` instead.
///
/// If a span is recorded without an explicit parent, or if the
/// explicit parent has a different name, this expectation will
/// fail.
///
/// # Examples
///
/// The explicit parent is matched by name:
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_explicit_parent(Some("parent_span"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(expect::span().named("parent_span"))
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let parent = tracing::info_span!("parent_span");
/// tracing::info_span!(parent: parent.id(), "span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// In the following example, the expected span is an explicit root:
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_explicit_parent(None);
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// tracing::info_span!(parent: None, "span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// In the example below, the expectation fails because the
/// span is *contextually*—as opposed to explicitly—within the span
/// `parent_span`:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let parent_span = expect::span().named("parent_span");
/// let span = expect::span()
/// .with_explicit_parent(Some("parent_span"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(parent_span.clone())
/// .enter(parent_span)
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let parent = tracing::info_span!("parent_span");
/// let _guard = parent.enter();
/// tracing::info_span!("span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
/// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
/// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
/// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::new_span
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Explicit(name.into()),
@@ -71,6 +406,99 @@ impl ExpectedSpan {
}
}
/// Configures this `ExpectedSpan` to expect a
/// contextually-determined parent span, or be a contextual
/// root.
///
/// **Note**: This method returns a [`NewSpan`] and as such, this
/// expectation can only be validated when expecting a new span via
/// [`MockSubscriber::new_span`]. It cannot be validated on
/// [`MockSubscriber::enter`], [`MockSubscriber::exit`], or any other
/// method on [`MockSubscriber`] that takes an `ExpectedSpan`.
///
/// The provided string is the name of the parent span to expect.
/// To expect that the event is a contextually-determined root, pass
/// `None` instead.
///
/// To expect a span with an explicit parent span, use
/// [`ExpectedSpan::with_explicit_parent`].
///
/// If a span is recorded which is not inside a span, has an explicitly
/// overridden parent span, or has a differently-named span as its
/// parent, this expectation will fail.
///
/// # Examples
///
/// The contextual parent is matched by name:
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let parent_span = expect::span().named("parent_span");
/// let span = expect::span()
/// .with_contextual_parent(Some("parent_span"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(parent_span.clone())
/// .enter(parent_span)
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let parent = tracing::info_span!("parent_span");
/// let _guard = parent.enter();
/// tracing::info_span!("span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// In the following example, we expect that the matched span is
/// a contextually-determined root:
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_contextual_parent(None);
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// tracing::info_span!("span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// In the example below, the expectation fails because the
/// span is recorded with an explicit parent:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_contextual_parent(Some("parent_span"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(expect::span().named("parent_span"))
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// let parent = tracing::info_span!("parent_span");
/// tracing::info_span!(parent: parent.id(), "span");
/// });
///
/// handle.assert_finished();
/// ```
///
/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
/// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
/// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
/// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::new_span
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Contextual(name.into()),
@@ -83,19 +511,68 @@ impl ExpectedSpan {
}
}
pub fn name(&self) -> Option<&str> {
self.metadata.name.as_ref().map(String::as_ref)
}
pub fn level(&self) -> Option<tracing::Level> {
self.metadata.level
}
pub fn target(&self) -> Option<&str> {
self.metadata.target.as_deref()
}
pub fn with_field<I>(self, fields: I) -> NewSpan
/// Adds fields to expect when matching a span.
///
/// **Note**: This method returns a [`NewSpan`] and as such, this
/// expectation can only be validated when expecting a new span via
/// [`MockSubscriber::new_span`]. It cannot be validated on
/// [`MockSubscriber::enter`], [`MockSubscriber::exit`], or any other
/// method on [`MockSubscriber`] that takes an `ExpectedSpan`.
///
/// If a span is recorded with fields that do not match the provided
/// [`ExpectedFields`], this expectation will fail.
///
/// If the provided field is not present on the recorded span or
/// if the value for that field diffs, then the expectation
/// will fail.
///
/// More information on the available validations is available in
/// the [`ExpectedFields`] documentation.
///
/// # Examples
///
/// ```
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_fields(expect::field("field.name").with_value(&"field_value"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// tracing::info_span!("span", field.name = "field_value");
/// });
///
/// handle.assert_finished();
/// ```
///
/// A different field value will cause the expectation to fail:
///
/// ```should_panic
/// use tracing_mock::{subscriber, expect};
///
/// let span = expect::span()
/// .with_fields(expect::field("field.name").with_value(&"field_value"));
///
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
///
/// tracing::collect::with_default(subscriber, || {
/// tracing::info_span!("span", field.name = "different_field_value");
/// });
///
/// handle.assert_finished();
/// ```
///
/// [`ExpectedFields`]: struct@crate::field::ExpectedFields
/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
/// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
/// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
/// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::new_span
pub fn with_fields<I>(self, fields: I) -> NewSpan
where
I: Into<ExpectedFields>,
{
@@ -105,6 +582,25 @@ impl ExpectedSpan {
..Default::default()
}
}
pub(crate) fn name(&self) -> Option<&str> {
self.metadata.name.as_ref().map(String::as_ref)
}
pub(crate) fn level(&self) -> Option<tracing::Level> {
self.metadata.level
}
pub(crate) fn target(&self) -> Option<&str> {
self.metadata.target.as_deref()
}
pub(crate) fn check(&self, actual: &SpanState, subscriber_name: &str) {
let meta = actual.metadata();
let name = meta.name();
self.metadata
.check(meta, format_args!("span `{}`", name), subscriber_name);
}
}
impl fmt::Debug for ExpectedSpan {
@@ -147,6 +643,13 @@ impl From<ExpectedSpan> for NewSpan {
}
impl NewSpan {
/// Configures this `ExpectedSpan` to expect an explicit parent
/// span or to be an explicit root.
///
/// For more information and examples, see the documentation on
/// [`ExpectedSpan::with_explicit_parent`].
///
/// [`ExpectedSpan::with_explicit_parent`]: fn@crate::span::ExpectedSpan::with_explicit_parent
pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Explicit(name.into()),
@@ -158,6 +661,14 @@ impl NewSpan {
}
}
/// Configures this `NewSpan` to expect a
/// contextually-determined parent span, or to be a contextual
/// root.
///
/// For more information and examples, see the documentation on
/// [`ExpectedSpan::with_contextual_parent`].
///
/// [`ExpectedSpan::with_contextual_parent`]: fn@crate::span::ExpectedSpan::with_contextual_parent
pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
let parent = match parent {
Some(name) => Parent::Contextual(name.into()),
@@ -169,7 +680,13 @@ impl NewSpan {
}
}
pub fn with_field<I>(self, fields: I) -> NewSpan
/// Adds fields to expect when matching a span.
///
/// For more information and examples, see the documentation on
/// [`ExpectedSpan::with_fields`].
///
/// [`ExpectedSpan::with_fields`]: fn@crate::span::ExpectedSpan::with_fields
pub fn with_fields<I>(self, fields: I) -> NewSpan
where
I: Into<ExpectedFields>,
{
@@ -179,7 +696,7 @@ impl NewSpan {
}
}
pub fn check(
pub(crate) fn check(
&mut self,
span: &tracing_core::span::Attributes<'_>,
get_parent_name: impl FnOnce() -> Option<String>,

View File

@@ -158,12 +158,18 @@ use tracing::{
Event, Metadata, Subscriber,
};
struct SpanState {
pub(crate) struct SpanState {
name: &'static str,
refs: usize,
meta: &'static Metadata<'static>,
}
impl SpanState {
pub(crate) fn metadata(&self) -> &'static Metadata<'static> {
self.meta
}
}
struct Running<F: Fn(&Metadata<'_>) -> bool> {
spans: Mutex<HashMap<Id, SpanState>>,
expected: Arc<Mutex<VecDeque<Expect>>>,
@@ -399,7 +405,7 @@ where
/// let span = expect::span()
/// .at_level(tracing::Level::INFO)
/// .named("the span we're testing")
/// .with_field(expect::field("testing").with_value(&"yes"));
/// .with_fields(expect::field("testing").with_value(&"yes"));
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
@@ -420,7 +426,7 @@ where
/// let span = expect::span()
/// .at_level(tracing::Level::INFO)
/// .named("the span we're testing")
/// .with_field(expect::field("testing").with_value(&"yes"));
/// .with_fields(expect::field("testing").with_value(&"yes"));
/// let (subscriber, handle) = subscriber::mock()
/// .new_span(span)
/// .run_with_handle();
@@ -1122,9 +1128,7 @@ where
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Enter(ref expected_span)) => {
if let Some(name) = expected_span.name() {
assert_eq!(name, span.name);
}
expected_span.check(span, &self.name);
}
Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name)),
}
@@ -1147,9 +1151,7 @@ where
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Exit(ref expected_span)) => {
if let Some(name) = expected_span.name() {
assert_eq!(name, span.name);
}
expected_span.check(span, &self.name);
let curr = self.current.lock().unwrap().pop();
assert_eq!(
Some(id),

View File

@@ -42,13 +42,13 @@ fn same_name_spans() {
expect::span()
.named("foo")
.at_level(Level::TRACE)
.with_field(expect::field("bar")),
.with_fields(expect::field("bar")),
)
.new_span(
expect::span()
.named("foo")
.at_level(Level::TRACE)
.with_field(expect::field("baz")),
.with_fields(expect::field("baz")),
)
.only()
.run_with_handle();

View File

@@ -37,13 +37,13 @@ fn same_name_spans() {
expect::span()
.named("foo")
.at_level(Level::TRACE)
.with_field(expect::field("bar")),
.with_fields(expect::field("bar")),
)
.new_span(
expect::span()
.named("foo")
.at_level(Level::TRACE)
.with_field(expect::field("baz")),
.with_fields(expect::field("baz")),
)
.only()
.run_with_handle();

View File

@@ -61,13 +61,13 @@ fn same_num_fields_and_name_len() {
expect::span()
.named("foo")
.at_level(Level::TRACE)
.with_field(expect::field("bar")),
.with_fields(expect::field("bar")),
)
.new_span(
expect::span()
.named("baz")
.at_level(Level::TRACE)
.with_field(expect::field("boz")),
.with_fields(expect::field("boz")),
)
.only()
.run_with_handle();

View File

@@ -342,7 +342,7 @@ fn entered_api() {
fn moved_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -373,7 +373,7 @@ fn dotted_field_name() {
.new_span(
expect::span()
.named("foo")
.with_field(expect::field("fields.bar").with_value(&true).only()),
.with_fields(expect::field("fields.bar").with_value(&true).only()),
)
.only()
.run_with_handle();
@@ -389,7 +389,7 @@ fn dotted_field_name() {
fn borrowed_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -432,7 +432,7 @@ fn move_field_out_of_struct() {
};
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("x")
.with_value(&debug(3.234))
.and(expect::field("y").with_value(&debug(-1.223)))
@@ -442,7 +442,7 @@ fn move_field_out_of_struct() {
.new_span(
expect::span()
.named("bar")
.with_field(expect::field("position").with_value(&debug(&pos)).only()),
.with_fields(expect::field("position").with_value(&debug(&pos)).only()),
)
.run_with_handle();
@@ -465,7 +465,7 @@ fn move_field_out_of_struct() {
fn float_values() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("x")
.with_value(&3.234)
.and(expect::field("y").with_value(&-1.223))
@@ -492,7 +492,7 @@ fn add_field_after_new_span() {
.new_span(
expect::span()
.named("foo")
.with_field(expect::field("bar").with_value(&5)
.with_fields(expect::field("bar").with_value(&5)
.and(expect::field("baz").with_value).only()),
)
.record(
@@ -549,7 +549,7 @@ fn add_fields_only_after_new_span() {
fn record_new_value_for_field() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&5)
.and(expect::field("baz").with_value(&false))
@@ -580,7 +580,7 @@ fn record_new_value_for_field() {
fn record_new_values_for_fields() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&4)
.and(expect::field("baz").with_value(&false))
@@ -781,7 +781,7 @@ fn contextual_child() {
fn display_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("my_span").with_field(
expect::span().named("my_span").with_fields(
expect::field("my_field")
.with_value(&display("hello world"))
.only(),
@@ -801,7 +801,7 @@ fn display_shorthand() {
fn debug_shorthand() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("my_span").with_field(
expect::span().named("my_span").with_fields(
expect::field("my_field")
.with_value(&debug("hello world"))
.only(),
@@ -821,7 +821,7 @@ fn debug_shorthand() {
fn both_shorthands() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("my_span").with_field(
expect::span().named("my_span").with_fields(
expect::field("display_field")
.with_value(&display("hello world"))
.and(expect::field("debug_field").with_value(&debug("hello world")))
@@ -842,7 +842,7 @@ fn both_shorthands() {
fn constant_field_name() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("my_span").with_field(
expect::span().named("my_span").with_fields(
expect::field("foo")
.with_value(&"bar")
.and(expect::field("constant string").with_value(&"also works"))

View File

@@ -60,7 +60,7 @@ fn event_macros_dont_infinite_loop() {
fn boxed_subscriber() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),
@@ -93,7 +93,7 @@ fn arced_subscriber() {
let (subscriber, handle) = subscriber::mock()
.new_span(
expect::span().named("foo").with_field(
expect::span().named("foo").with_fields(
expect::field("bar")
.with_value(&display("hello from my span"))
.only(),