Skip to content

Commit 717b791

Browse files
committed
mock: correct contextual/explicit parent assertions
When recording the parent of an event or span, the `MockCollector` treats an explicit parent of `None` (i.e. an event or span that is an explicit root) in the same way as if there is no explicit root. This leads to it picking up the contextual parent or treating the event or span as a contextual root. This change refactors the recording of the parent to use `is_contextual` to distinguish whether or not an explicit parent has been specified. The actual parent is also written into a `Parent` enum so that the expected and actual values can be compared in a more explicit way. Additionally, the `Parent` struct has been moved into its own module and the check behavior has been fixed. The error message has also been unified across all cases. Given the number of different cases involved in checking parents, separate integration tests have been added to `tracing-mock` specifically for testing all the positive and negative cases when asserting on the parents of events and spans. There were two tests in `tracing-attributes` which specified both an explicit and a contextual parent. This behavior was never intended to work as all events and spans are either contextual or not. The tests have been corrected to only expect one of the two. Fixes: #2440
1 parent ad2bfaa commit 717b791

File tree

9 files changed

+914
-156
lines changed

9 files changed

+914
-156
lines changed

tracing-attributes/tests/parents.rs

+6-31
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,15 @@ fn default_parent_test() {
1818
let child = expect::span().named("with_default_parent");
1919

2020
let (collector, handle) = collector::mock()
21-
.new_span(
22-
contextual_parent
23-
.clone()
24-
.with_contextual_parent(None)
25-
.with_explicit_parent(None),
26-
)
27-
.new_span(
28-
child
29-
.clone()
30-
.with_contextual_parent(Some("contextual_parent"))
31-
.with_explicit_parent(None),
32-
)
21+
.new_span(contextual_parent.clone().with_contextual_parent(None))
22+
.new_span(child.clone().with_contextual_parent(None))
3323
.enter(child.clone())
3424
.exit(child.clone())
3525
.enter(contextual_parent.clone())
3626
.new_span(
3727
child
3828
.clone()
39-
.with_contextual_parent(Some("contextual_parent"))
40-
.with_explicit_parent(None),
29+
.with_contextual_parent(Some("contextual_parent")),
4130
)
4231
.enter(child.clone())
4332
.exit(child)
@@ -65,24 +54,10 @@ fn explicit_parent_test() {
6554
let child = expect::span().named("with_explicit_parent");
6655

6756
let (collector, handle) = collector::mock()
68-
.new_span(
69-
contextual_parent
70-
.clone()
71-
.with_contextual_parent(None)
72-
.with_explicit_parent(None),
73-
)
74-
.new_span(
75-
explicit_parent
76-
.with_contextual_parent(None)
77-
.with_explicit_parent(None),
78-
)
57+
.new_span(contextual_parent.clone().with_contextual_parent(None))
58+
.new_span(explicit_parent.with_contextual_parent(None))
7959
.enter(contextual_parent.clone())
80-
.new_span(
81-
child
82-
.clone()
83-
.with_contextual_parent(Some("contextual_parent"))
84-
.with_explicit_parent(Some("explicit_parent")),
85-
)
60+
.new_span(child.clone().with_explicit_parent(Some("explicit_parent")))
8661
.enter(child.clone())
8762
.exit(child)
8863
.exit(contextual_parent)

tracing-mock/src/collector.rs

+59-15
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ use crate::{
141141
event::ExpectedEvent,
142142
expect::Expect,
143143
field::ExpectedFields,
144+
parent::Parent,
144145
span::{ExpectedSpan, NewSpan},
145146
};
146147
use std::{
@@ -1034,16 +1035,38 @@ where
10341035
)
10351036
}
10361037
}
1037-
let get_parent_name = || {
1038-
let stack = self.current.lock().unwrap();
1038+
1039+
let get_parent = || {
10391040
let spans = self.spans.lock().unwrap();
1040-
event
1041-
.parent()
1042-
.and_then(|id| spans.get(id))
1043-
.or_else(|| stack.last().and_then(|id| spans.get(id)))
1044-
.map(|s| s.name.to_string())
1041+
1042+
if event.is_contextual() {
1043+
let stack = self.current.lock().unwrap();
1044+
if let Some(parent_id) = stack.last() {
1045+
let contextual_parent = spans.get(parent_id).expect(
1046+
"tracing-mock: contextual parent cannot \
1047+
be looked up by ID. Was it recorded correctly?",
1048+
);
1049+
Parent::Contextual(contextual_parent.name.to_string())
1050+
} else {
1051+
Parent::ContextualRoot
1052+
}
1053+
} else {
1054+
if event.is_root() {
1055+
Parent::ExplicitRoot
1056+
} else {
1057+
let parent_id = event.parent().expect(
1058+
"tracing-mock: is_contextual=false is_root=false \
1059+
but no explicit parent found. This is a bug!",
1060+
);
1061+
let explicit_parent = spans.get(parent_id).expect(
1062+
"tracing-mock: explicit parent cannot be looked \
1063+
up by ID. Is the provided Span ID valid: {parent_id}",
1064+
);
1065+
Parent::Explicit(explicit_parent.name.to_string())
1066+
}
1067+
}
10451068
};
1046-
expected.check(event, get_parent_name, &self.name);
1069+
expected.check(event, get_parent, &self.name);
10471070
}
10481071
Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)),
10491072
}
@@ -1100,14 +1123,35 @@ where
11001123
let mut spans = self.spans.lock().unwrap();
11011124
if was_expected {
11021125
if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() {
1103-
let get_parent_name = || {
1104-
let stack = self.current.lock().unwrap();
1105-
span.parent()
1106-
.and_then(|id| spans.get(id))
1107-
.or_else(|| stack.last().and_then(|id| spans.get(id)))
1108-
.map(|s| s.name.to_string())
1126+
let get_parent = || {
1127+
if span.is_contextual() {
1128+
let stack = self.current.lock().unwrap();
1129+
if let Some(parent_id) = stack.last() {
1130+
let contextual_parent = spans.get(parent_id).expect(
1131+
"tracing-mock: contextual parent cannot \
1132+
be looked up by ID. Was it recorded correctly?",
1133+
);
1134+
Parent::Contextual(contextual_parent.name.to_string())
1135+
} else {
1136+
Parent::ContextualRoot
1137+
}
1138+
} else {
1139+
if span.is_root() {
1140+
Parent::ExplicitRoot
1141+
} else {
1142+
let parent_id = span.parent().expect(
1143+
"tracing-mock: is_contextual=false is_root=false \
1144+
but no explicit parent found. This is a bug!",
1145+
);
1146+
let explicit_parent = spans.get(parent_id).expect(
1147+
"tracing-mock: explicit parent cannot be looked \
1148+
up by ID. Is the provided Span ID valid: {parent_id}",
1149+
);
1150+
Parent::Explicit(explicit_parent.name.to_string())
1151+
}
1152+
}
11091153
};
1110-
expected.check(span, get_parent_name, &self.name);
1154+
expected.check(span, get_parent, &self.name);
11111155
}
11121156
}
11131157
spans.insert(

tracing-mock/src/event.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
//! [`collector`]: mod@crate::collector
3030
//! [`expect::event`]: fn@crate::expect::event
3131
#![allow(missing_docs)]
32-
use super::{expect, field, metadata::ExpectedMetadata, span, Parent};
32+
use super::{expect, field, metadata::ExpectedMetadata, parent::Parent, span};
3333

3434
use std::fmt;
3535

@@ -307,6 +307,7 @@ impl ExpectedEvent {
307307
/// .run_with_handle();
308308
///
309309
/// with_default(collector, || {
310+
/// let _guard = tracing::info_span!("contextual parent").entered();
310311
/// tracing::info!(parent: None, field = &"value");
311312
/// });
312313
///
@@ -557,7 +558,7 @@ impl ExpectedEvent {
557558
pub(crate) fn check(
558559
&mut self,
559560
event: &tracing::Event<'_>,
560-
get_parent_name: impl FnOnce() -> Option<String>,
561+
get_parent: impl FnOnce() -> Parent,
561562
collector_name: &str,
562563
) {
563564
let meta = event.metadata();
@@ -578,13 +579,14 @@ impl ExpectedEvent {
578579
}
579580

580581
if let Some(ref expected_parent) = self.parent {
581-
let actual_parent = get_parent_name();
582-
expected_parent.check_parent_name(
583-
actual_parent.as_deref(),
584-
event.parent().cloned(),
585-
event.metadata().name(),
586-
collector_name,
587-
)
582+
let actual_parent = get_parent();
583+
expected_parent.check(&actual_parent, event.metadata().name(), collector_name);
584+
// expected_parent.check_parent_name(
585+
// actual_parent.as_deref(),
586+
// event.parent().cloned(),
587+
// event.metadata().name(),
588+
// collector_name,
589+
// )
588590
}
589591
}
590592
}

tracing-mock/src/lib.rs

+1-85
Original file line numberDiff line numberDiff line change
@@ -4,92 +4,8 @@ pub mod event;
44
pub mod expect;
55
pub mod field;
66
mod metadata;
7+
mod parent;
78
pub mod span;
89

910
#[cfg(feature = "tracing-subscriber")]
1011
pub mod subscriber;
11-
12-
#[derive(Debug, Eq, PartialEq)]
13-
pub enum Parent {
14-
ContextualRoot,
15-
Contextual(String),
16-
ExplicitRoot,
17-
Explicit(String),
18-
}
19-
20-
impl Parent {
21-
pub fn check_parent_name(
22-
&self,
23-
parent_name: Option<&str>,
24-
provided_parent: Option<tracing_core::span::Id>,
25-
ctx: impl std::fmt::Display,
26-
collector_name: &str,
27-
) {
28-
match self {
29-
Parent::ExplicitRoot => {
30-
assert!(
31-
provided_parent.is_none(),
32-
"[{}] expected {} to be an explicit root, but its parent was actually {:?} (name: {:?})",
33-
collector_name,
34-
ctx,
35-
provided_parent,
36-
parent_name,
37-
);
38-
}
39-
Parent::Explicit(expected_parent) => {
40-
assert!(
41-
provided_parent.is_some(),
42-
"[{}] expected {} to have explicit parent {}, but it has no explicit parent",
43-
collector_name,
44-
ctx,
45-
expected_parent,
46-
);
47-
assert_eq!(
48-
Some(expected_parent.as_ref()),
49-
parent_name,
50-
"[{}] expected {} to have explicit parent {}, but its parent was actually {:?} (name: {:?})",
51-
collector_name,
52-
ctx,
53-
expected_parent,
54-
provided_parent,
55-
parent_name,
56-
);
57-
}
58-
Parent::ContextualRoot => {
59-
assert!(
60-
provided_parent.is_none(),
61-
"[{}] expected {} to be a contextual root, but its parent was actually {:?} (name: {:?})",
62-
collector_name,
63-
ctx,
64-
provided_parent,
65-
parent_name,
66-
);
67-
assert!(
68-
parent_name.is_none(),
69-
"[{}] expected {} to be contextual a root, but we were inside span {:?}",
70-
collector_name,
71-
ctx,
72-
parent_name,
73-
);
74-
}
75-
Parent::Contextual(expected_parent) => {
76-
assert!(provided_parent.is_none(),
77-
"[{}] expected {} to have a contextual parent\nbut it has the explicit parent {:?} (name: {:?})",
78-
collector_name,
79-
ctx,
80-
provided_parent,
81-
parent_name,
82-
);
83-
assert_eq!(
84-
Some(expected_parent.as_ref()),
85-
parent_name,
86-
"[{}] expected {} to have contextual parent {:?}, but got {:?}",
87-
collector_name,
88-
ctx,
89-
expected_parent,
90-
parent_name,
91-
);
92-
}
93-
}
94-
}
95-
}

tracing-mock/src/parent.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#[derive(Debug, Eq, PartialEq)]
2+
pub(crate) enum Parent {
3+
ContextualRoot,
4+
Contextual(String),
5+
ExplicitRoot,
6+
Explicit(String),
7+
}
8+
9+
impl Parent {
10+
#[track_caller]
11+
pub(crate) fn check(
12+
&self,
13+
actual_parent: &Parent,
14+
ctx: impl std::fmt::Display,
15+
collector_name: &str,
16+
) {
17+
let expected_description = |parent: &Parent| match parent {
18+
Self::ExplicitRoot => format!("be an explicit root"),
19+
Self::Explicit(name) => format!("have an explicit parent with name='{name}'"),
20+
Self::ContextualRoot => format!("be a contextual root"),
21+
Self::Contextual(name) => format!("have a contextual parent with name='{name}'"),
22+
};
23+
24+
let actual_description = |parent: &Parent| match parent {
25+
Self::ExplicitRoot => format!("was actually an explicit root"),
26+
Self::Explicit(name) => format!("actually has an explicit parent with name='{name}'"),
27+
Self::ContextualRoot => format!("was actually a contextual root"),
28+
Self::Contextual(name) => {
29+
format!("actually has a contextual parent with name='{name}'")
30+
}
31+
};
32+
33+
assert_eq!(
34+
self,
35+
actual_parent,
36+
"[{collector_name}] expected {ctx} to {expected_description}, but {actual_description}",
37+
expected_description = expected_description(self),
38+
actual_description = actual_description(actual_parent)
39+
);
40+
}
41+
}

tracing-mock/src/span.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
//! [`expect::span`]: fn@crate::expect::span
9393
#![allow(missing_docs)]
9494
use crate::{
95-
collector::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent,
95+
collector::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, parent::Parent,
9696
};
9797
use std::fmt;
9898

@@ -699,7 +699,7 @@ impl NewSpan {
699699
pub(crate) fn check(
700700
&mut self,
701701
span: &tracing_core::span::Attributes<'_>,
702-
get_parent_name: impl FnOnce() -> Option<String>,
702+
get_parent: impl FnOnce() -> Parent,
703703
collector_name: &str,
704704
) {
705705
let meta = span.metadata();
@@ -711,14 +711,13 @@ impl NewSpan {
711711
span.record(&mut checker);
712712
checker.finish();
713713

714-
if let Some(expected_parent) = self.parent.as_ref() {
715-
let actual_parent = get_parent_name();
716-
expected_parent.check_parent_name(
717-
actual_parent.as_deref(),
718-
span.parent().cloned(),
714+
if let Some(ref expected_parent) = self.parent {
715+
let actual_parent = get_parent();
716+
expected_parent.check(
717+
&actual_parent,
719718
format_args!("span `{}`", name),
720719
collector_name,
721-
)
720+
);
722721
}
723722
}
724723
}

0 commit comments

Comments
 (0)