Skip to content

Commit 760a212

Browse files
committed
mock: match parent span on ExpectedSpan (#3098)
The `with_ancestry` methods on `NewSpan` and `ExpectedEvent` provide a way to match whether the span or event is a contextual or explicit root or if it has a contextual or explicit parent span. However, in the case of matching on a contextual or explicit parent span, only the span name could be used for matching. This is sufficiently precise when testing tracing instrumentation in other libraries or applications as opposed to testing tracing itself. It is likely that a user would like to test that some span or event has a specific span as a parent, and not just any span with a specific name, in many cases, all the possible parent spans may have the same name. This is the case when testing tracing instrumentation in Tokio. To solve this problem, the `Ancestry` struct was renamed to `ExpectedAncestry` and in the case of expecting an explicit or conextual parent, an `ExpectedSpan` object can be passed in. This provides the maximum possible flexibility. The convenience functions in the `expect` module now take `Into<ExpectedSpan>` so that existing tests that pass a string type object for the parent will see the same behaviour as previously and shorthand use for expected Ids is also available. Additionally, the span checking code has been unified between the `MockCollector` and `MockSubscriber` cases and the assertion descriptions have been improved to make them more readable.
1 parent 7b2cbc6 commit 760a212

File tree

9 files changed

+589
-329
lines changed

9 files changed

+589
-329
lines changed

tracing-mock/src/ancestry.rs

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,102 @@
11
//! Define the ancestry of an event or span.
22
//!
3-
//! See the documentation on the [`Ancestry`] enum for further details.
3+
//! See the documentation on the [`ExpectedAncestry`] enum for further details.
44
55
use tracing_core::{
66
span::{self, Attributes},
77
Event,
88
};
99

10+
use crate::span::{ActualSpan, ExpectedSpan};
11+
1012
/// The ancestry of an event or span.
1113
///
1214
/// An event or span can have an explicitly assigned parent, or be an explicit root. Otherwise,
1315
/// an event or span may have a contextually assigned parent or in the final case will be a
1416
/// contextual root.
1517
#[derive(Debug, Eq, PartialEq)]
16-
pub enum Ancestry {
17-
/// The event or span has an explicitly assigned parent (created with `parent: span_id`) with
18-
/// the specified name.
19-
HasExplicitParent(String),
18+
pub enum ExpectedAncestry {
19+
/// The event or span has an explicitly assigned parent (created with `parent: span_id`) span.
20+
HasExplicitParent(ExpectedSpan),
2021
/// The event or span is an explicitly defined root. It was created with `parent: None` and
2122
/// has no parent.
2223
IsExplicitRoot,
23-
/// The event or span has a contextually assigned parent with the specified name. It has no
24-
/// explicitly assigned parent, nor has it been explicitly defined as a root (it was created
25-
/// without the `parent:` directive). There was a span in context when this event or span was
26-
/// created.
27-
HasContextualParent(String),
24+
/// The event or span has a contextually assigned parent span. It has no explicitly assigned
25+
/// parent span, nor has it been explicitly defined as a root (it was created without the
26+
/// `parent:` directive). There was a span in context when this event or span was created.
27+
HasContextualParent(ExpectedSpan),
2828
/// The event or span is a contextual root. It has no explicitly assigned parent, nor has it
2929
/// been explicitly defined as a root (it was created without the `parent:` directive).
3030
/// Additionally, no span was in context when this event or span was created.
3131
IsContextualRoot,
3232
}
3333

34-
impl Ancestry {
34+
pub(crate) enum ActualAncestry {
35+
HasExplicitParent(ActualSpan),
36+
IsExplicitRoot,
37+
HasContextualParent(ActualSpan),
38+
IsContextualRoot,
39+
}
40+
41+
impl ExpectedAncestry {
3542
#[track_caller]
3643
pub(crate) fn check(
3744
&self,
38-
actual_ancestry: &Ancestry,
45+
actual_ancestry: &ActualAncestry,
3946
ctx: impl std::fmt::Display,
4047
collector_name: &str,
4148
) {
42-
let expected_description = |ancestry: &Ancestry| match ancestry {
43-
Self::IsExplicitRoot => "be an explicit root".to_string(),
44-
Self::HasExplicitParent(name) => format!("have an explicit parent with name='{name}'"),
45-
Self::IsContextualRoot => "be a contextual root".to_string(),
46-
Self::HasContextualParent(name) => {
47-
format!("have a contextual parent with name='{name}'")
49+
match (self, actual_ancestry) {
50+
(Self::IsExplicitRoot, ActualAncestry::IsExplicitRoot) => {}
51+
(Self::IsContextualRoot, ActualAncestry::IsContextualRoot) => {}
52+
(
53+
Self::HasExplicitParent(expected_parent),
54+
ActualAncestry::HasExplicitParent(actual_parent),
55+
) => {
56+
expected_parent.check(
57+
actual_parent,
58+
format_args!("{ctx} to have an explicit parent span"),
59+
collector_name,
60+
);
4861
}
49-
};
50-
51-
let actual_description = |ancestry: &Ancestry| match ancestry {
52-
Self::IsExplicitRoot => "was actually an explicit root".to_string(),
53-
Self::HasExplicitParent(name) => {
54-
format!("actually has an explicit parent with name='{name}'")
62+
(
63+
Self::HasContextualParent(expected_parent),
64+
ActualAncestry::HasContextualParent(actual_parent),
65+
) => {
66+
println!("----> [{collector_name}] check {expected_parent:?} against actual parent with Id={id:?}", id = actual_parent.id());
67+
expected_parent.check(
68+
actual_parent,
69+
format_args!("{ctx} to have a contextual parent span"),
70+
collector_name,
71+
);
5572
}
56-
Self::IsContextualRoot => "was actually a contextual root".to_string(),
57-
Self::HasContextualParent(name) => {
58-
format!("actually has a contextual parent with name='{name}'")
73+
_ => {
74+
// Ancestry types don't match at all.
75+
let expected_description = match self {
76+
Self::IsExplicitRoot => "be an explicit root",
77+
Self::HasExplicitParent(_) => "have an explicit parent span",
78+
Self::IsContextualRoot => "be a contextual root",
79+
Self::HasContextualParent(_) => "have a contextual parent span",
80+
};
81+
82+
let actual_description = match actual_ancestry {
83+
ActualAncestry::IsExplicitRoot => "is actually an explicit root",
84+
ActualAncestry::HasExplicitParent(_) => "actually has an explicit parent span",
85+
ActualAncestry::IsContextualRoot => "is actually a contextual root",
86+
ActualAncestry::HasContextualParent(_) => {
87+
"actually has a contextual parent span"
88+
}
89+
};
90+
91+
panic!(
92+
"{}",
93+
format!(
94+
"[{collector_name}] expected {ctx} to {expected_description}, \
95+
but it {actual_description}"
96+
)
97+
);
5998
}
60-
};
61-
62-
assert_eq!(
63-
self,
64-
actual_ancestry,
65-
"[{collector_name}] expected {ctx} to {expected_description}, but {actual_description}",
66-
expected_description = expected_description(self),
67-
actual_description = actual_description(actual_ancestry)
68-
);
99+
}
69100
}
70101
}
71102

@@ -120,29 +151,29 @@ impl HasAncestry for &Attributes<'_> {
120151
pub(crate) fn get_ancestry(
121152
item: impl HasAncestry,
122153
lookup_current: impl FnOnce() -> Option<span::Id>,
123-
span_name: impl FnOnce(&span::Id) -> Option<&str>,
124-
) -> Ancestry {
154+
actual_span: impl FnOnce(&span::Id) -> Option<ActualSpan>,
155+
) -> ActualAncestry {
125156
if item.is_contextual() {
126157
if let Some(parent_id) = lookup_current() {
127-
let contextual_parent_name = span_name(&parent_id).expect(
158+
let contextual_parent_span = actual_span(&parent_id).expect(
128159
"tracing-mock: contextual parent cannot \
129160
be looked up by ID. Was it recorded correctly?",
130161
);
131-
Ancestry::HasContextualParent(contextual_parent_name.to_string())
162+
ActualAncestry::HasContextualParent(contextual_parent_span)
132163
} else {
133-
Ancestry::IsContextualRoot
164+
ActualAncestry::IsContextualRoot
134165
}
135166
} else if item.is_root() {
136-
Ancestry::IsExplicitRoot
167+
ActualAncestry::IsExplicitRoot
137168
} else {
138169
let parent_id = item.parent().expect(
139170
"tracing-mock: is_contextual=false is_root=false \
140171
but no explicit parent found. This is a bug!",
141172
);
142-
let explicit_parent_name = span_name(parent_id).expect(
173+
let explicit_parent_span = actual_span(parent_id).expect(
143174
"tracing-mock: explicit parent cannot be looked \
144175
up by ID. Is the provided Span ID valid: {parent_id}",
145176
);
146-
Ancestry::HasExplicitParent(explicit_parent_name.to_string())
177+
ActualAncestry::HasExplicitParent(explicit_parent_span)
147178
}
148179
}

tracing-mock/src/event.rs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@
2929
//! [`subscriber`]: mod@crate::subscriber
3030
//! [`expect::event`]: fn@crate::expect::event
3131
#![allow(missing_docs)]
32-
use crate::{ancestry::Ancestry, expect, field, metadata::ExpectedMetadata, span};
32+
use crate::{
33+
ancestry::{ActualAncestry, ExpectedAncestry},
34+
expect, field,
35+
metadata::ExpectedMetadata,
36+
span,
37+
};
3338

3439
use std::fmt;
3540

@@ -42,7 +47,7 @@ use std::fmt;
4247
#[derive(Default, Eq, PartialEq)]
4348
pub struct ExpectedEvent {
4449
pub(super) fields: Option<field::ExpectedFields>,
45-
pub(super) ancestry: Option<Ancestry>,
50+
pub(super) ancestry: Option<ExpectedAncestry>,
4651
pub(super) in_spans: Option<Vec<span::ExpectedSpan>>,
4752
pub(super) metadata: ExpectedMetadata,
4853
}
@@ -253,9 +258,10 @@ impl ExpectedEvent {
253258
}
254259
}
255260

256-
/// Configures this `ExpectedEvent` to expect the specified [`Ancestry`].
257-
/// An event's ancestry indicates whether is has a parent or is a root, and
258-
/// whether the parent is explicitly or contextually assigned.
261+
/// Configures this `ExpectedEvent` to expect the specified
262+
/// [`ExpectedAncestry`]. An event's ancestry indicates whether is has a
263+
/// parent or is a root, and whether the parent is explicitly or
264+
/// contextually assigned.
259265
///
260266
/// An _explicit_ parent span is one passed to the `event!` macro in the
261267
/// `parent:` field. If no `parent:` field is specified, then the event
@@ -267,9 +273,34 @@ impl ExpectedEvent {
267273
///
268274
/// # Examples
269275
///
270-
/// If `expect::has_explicit_parent("parent_name")` is passed
271-
/// `with_ancestry` then the provided string is the name of the explicit
272-
/// parent span to expect.
276+
/// An explicit or contextual can be matched on an `ExpectedSpan`.
277+
///
278+
/// ```
279+
/// use tracing::subscriber::with_default;
280+
/// use tracing_mock::{subscriber, expect};
281+
///
282+
/// let parent = expect::span()
283+
/// .named("parent_span")
284+
/// .with_target("custom-target")
285+
/// .at_level(tracing::Level::INFO);
286+
/// let event = expect::event()
287+
/// .with_ancestry(expect::has_explicit_parent(parent));
288+
///
289+
/// let (subscriber, handle) = subscriber::mock()
290+
/// .event(event)
291+
/// .run_with_handle();
292+
///
293+
/// with_default(subscriber, || {
294+
/// let parent = tracing::info_span!(target: "custom-target", "parent_span");
295+
/// tracing::info!(parent: parent.id(), field = &"value");
296+
/// });
297+
///
298+
/// handle.assert_finished();
299+
/// ```
300+
/// The functions `expect::has_explicit_parent` and
301+
/// `expect::has_contextual_parent` take `Into<ExpectedSpan>`, so a string
302+
/// passed directly will match on a span with that name, or an
303+
/// [`ExpectedId`] can be passed to match a span with that Id.
273304
///
274305
/// ```
275306
/// use tracing::subscriber::with_default;
@@ -382,7 +413,9 @@ impl ExpectedEvent {
382413
///
383414
/// handle.assert_finished();
384415
/// ```
385-
pub fn with_ancestry(self, ancenstry: Ancestry) -> ExpectedEvent {
416+
///
417+
/// [`ExpectedId`]: struct@crate::span::ExpectedId
418+
pub fn with_ancestry(self, ancenstry: ExpectedAncestry) -> ExpectedEvent {
386419
Self {
387420
ancestry: Some(ancenstry),
388421
..self
@@ -506,7 +539,7 @@ impl ExpectedEvent {
506539
pub(crate) fn check(
507540
&mut self,
508541
event: &tracing::Event<'_>,
509-
get_ancestry: impl FnOnce() -> Ancestry,
542+
get_ancestry: impl FnOnce() -> ActualAncestry,
510543
subscriber_name: &str,
511544
) {
512545
let meta = event.metadata();

tracing-mock/src/expect.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::fmt;
22

33
use crate::{
4-
ancestry::Ancestry,
4+
ancestry::ExpectedAncestry,
55
event::ExpectedEvent,
66
field::{ExpectedField, ExpectedFields, ExpectedValue},
77
span::{ExpectedId, ExpectedSpan, NewSpan},
@@ -71,26 +71,26 @@ pub fn id() -> ExpectedId {
7171
ExpectedId::new_unset()
7272
}
7373

74-
/// Convenience function that returns [`Ancestry::IsContextualRoot`].
75-
pub fn is_contextual_root() -> Ancestry {
76-
Ancestry::IsContextualRoot
74+
/// Convenience function that returns [`ExpectedAncestry::IsContextualRoot`].
75+
pub fn is_contextual_root() -> ExpectedAncestry {
76+
ExpectedAncestry::IsContextualRoot
7777
}
7878

79-
/// Convenience function that returns [`Ancestry::HasContextualParent`] with
79+
/// Convenience function that returns [`ExpectedAncestry::HasContextualParent`] with
8080
/// provided name.
81-
pub fn has_contextual_parent<S: Into<String>>(name: S) -> Ancestry {
82-
Ancestry::HasContextualParent(name.into())
81+
pub fn has_contextual_parent<S: Into<ExpectedSpan>>(span: S) -> ExpectedAncestry {
82+
ExpectedAncestry::HasContextualParent(span.into())
8383
}
8484

85-
/// Convenience function that returns [`Ancestry::IsExplicitRoot`].
86-
pub fn is_explicit_root() -> Ancestry {
87-
Ancestry::IsExplicitRoot
85+
/// Convenience function that returns [`ExpectedAncestry::IsExplicitRoot`].
86+
pub fn is_explicit_root() -> ExpectedAncestry {
87+
ExpectedAncestry::IsExplicitRoot
8888
}
8989

90-
/// Convenience function that returns [`Ancestry::HasExplicitParent`] with
90+
/// Convenience function that returns [`ExpectedAncestry::HasExplicitParent`] with
9191
/// provided name.
92-
pub fn has_explicit_parent<S: Into<String>>(name: S) -> Ancestry {
93-
Ancestry::HasExplicitParent(name.into())
92+
pub fn has_explicit_parent<S: Into<ExpectedSpan>>(span: S) -> ExpectedAncestry {
93+
ExpectedAncestry::HasExplicitParent(span.into())
9494
}
9595

9696
impl Expect {

0 commit comments

Comments
 (0)