🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_mock/
span.rs

1//! Define expectations to match and validate spans.
2//!
3//! The [`ExpectedSpan`] and [`NewSpan`] structs define expectations
4//! for spans to be matched by the mock collector API in the
5//! [`collector`] module.
6//!
7//! Expected spans should be created with [`expect::span`] and a
8//! chain of method calls describing the assertions made about the
9//! span. Expectations about the lifecycle of the span can be set on the [`MockCollector`].
10//!
11//! # Examples
12//!
13//! ```
14//! use tracing_mock::{collector, expect};
15//!
16//! let span = expect::span()
17//!     .named("interesting_span")
18//!     .at_level(tracing::Level::INFO);
19//!
20//! let (collector, handle) = collector::mock()
21//!     .enter(&span)
22//!     .exit(&span)
23//!     .run_with_handle();
24//!
25//! tracing::collect::with_default(collector, || {
26//!    let span = tracing::info_span!("interesting_span");
27//!     let _guard = span.enter();
28//! });
29//!
30//! handle.assert_finished();
31//! ```
32//!
33//! Instead of passing an `ExpectedSpan`, the collector methods will also accept
34//! anything that implements `Into<String>` which is shorthand for
35//! `expect::span().named(name)`.
36//!
37//! ```
38//! use tracing_mock::collector;
39//!
40//! let (collector, handle) = collector::mock()
41//!     .enter("interesting_span")
42//!     .run_with_handle();
43//!
44//! tracing::collect::with_default(collector, || {
45//!    let span = tracing::info_span!("interesting_span");
46//!     let _guard = span.enter();
47//! });
48//!
49//! handle.assert_finished();
50//! ```
51//
52//! The following example asserts the name, level, parent, and fields of the span:
53//!
54//! ```
55//! use tracing_mock::{collector, expect};
56//!
57//! let span = expect::span()
58//!     .named("interesting_span")
59//!     .at_level(tracing::Level::INFO);
60//! let new_span = span
61//!     .clone()
62//!     .with_fields(expect::field("field.name").with_value(&"field_value"))
63//!     .with_ancestry(expect::has_explicit_parent("parent_span"));
64//!
65//! let (collector, handle) = collector::mock()
66//!     .new_span("parent_span")
67//!     .new_span(new_span)
68//!     .enter(&span)
69//!     .exit(&span)
70//!     .run_with_handle();
71//!
72//! tracing::collect::with_default(collector, || {
73//!     let parent = tracing::info_span!("parent_span");
74//!
75//!     let span = tracing::info_span!(
76//!         parent: parent.id(),
77//!         "interesting_span",
78//!         field.name = "field_value",
79//!     );
80//!     let _guard = span.enter();
81//! });
82//!
83//! handle.assert_finished();
84//! ```
85//!
86//! All expectations must be met for the test to pass. For example,
87//! the following test will fail due to a mismatch in the spans' names:
88//!
89//! ```should_panic
90//! use tracing_mock::{collector, expect};
91//!
92//! let span = expect::span()
93//!     .named("interesting_span")
94//!     .at_level(tracing::Level::INFO);
95//!
96//! let (collector, handle) = collector::mock()
97//!     .enter(&span)
98//!     .exit(&span)
99//!     .run_with_handle();
100//!
101//! tracing::collect::with_default(collector, || {
102//!    let span = tracing::info_span!("another_span");
103//!    let _guard = span.enter();
104//! });
105//!
106//! handle.assert_finished();
107//! ```
108//!
109//! [`MockCollector`]: struct@crate::collector::MockCollector
110//! [`collector`]: mod@crate::collector
111//! [`expect::span`]: fn@crate::expect::span
112use std::{
113    error, fmt,
114    sync::{
115        atomic::{AtomicU64, Ordering},
116        Arc,
117    },
118};
119
120use crate::{
121    ancestry::{ActualAncestry, ExpectedAncestry},
122    field::ExpectedFields,
123    metadata::ExpectedMetadata,
124};
125
126/// A mock span.
127///
128/// This is intended for use with the mock collector API in the
129/// [`collector`] module.
130///
131/// [`collector`]: mod@crate::collector
132#[derive(Clone, Default, Eq, PartialEq)]
133pub struct ExpectedSpan {
134    pub(crate) id: Option<ExpectedId>,
135    pub(crate) metadata: ExpectedMetadata,
136}
137
138impl<I> From<I> for ExpectedSpan
139where
140    I: Into<String>,
141{
142    fn from(name: I) -> Self {
143        ExpectedSpan::default().named(name)
144    }
145}
146
147impl From<&ExpectedId> for ExpectedSpan {
148    fn from(id: &ExpectedId) -> Self {
149        ExpectedSpan::default().with_id(id.clone())
150    }
151}
152
153impl From<&ExpectedSpan> for ExpectedSpan {
154    fn from(span: &ExpectedSpan) -> Self {
155        span.clone()
156    }
157}
158
159/// A mock new span.
160///
161/// **Note**: This struct contains expectations that can only be asserted
162/// on when expecting a new span via [`MockCollector::new_span`]. They
163/// cannot be validated on [`MockCollector::enter`],
164/// [`MockCollector::exit`], or any other method on [`MockCollector`]
165/// that takes an `ExpectedSpan`.
166///
167/// For more details on how to use this struct, see the documentation
168/// on the [`collector`] module.
169///
170/// [`collector`]: mod@crate::collector
171/// [`MockCollector`]: struct@crate::collector::MockCollector
172/// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
173/// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
174/// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
175#[derive(Default, Eq, PartialEq)]
176pub struct NewSpan {
177    pub(crate) span: ExpectedSpan,
178    pub(crate) fields: ExpectedFields,
179    pub(crate) ancestry: Option<ExpectedAncestry>,
180}
181
182pub(crate) struct ActualSpan {
183    id: tracing_core::span::Id,
184    metadata: Option<&'static tracing_core::Metadata<'static>>,
185}
186
187impl ActualSpan {
188    pub(crate) fn new(
189        id: tracing_core::span::Id,
190        metadata: Option<&'static tracing_core::Metadata<'static>>,
191    ) -> Self {
192        Self { id, metadata }
193    }
194
195    /// The Id of the actual span.
196    pub(crate) fn id(&self) -> tracing_core::span::Id {
197        self.id.clone()
198    }
199
200    /// The metadata for the actual span if it is available.
201    pub(crate) fn metadata(&self) -> Option<&'static tracing_core::Metadata<'static>> {
202        self.metadata
203    }
204}
205
206impl From<&tracing_core::span::Id> for ActualSpan {
207    fn from(id: &tracing_core::span::Id) -> Self {
208        Self::new(id.clone(), None)
209    }
210}
211
212/// A mock span ID.
213///
214/// This ID makes it possible to link together calls to different
215/// [`MockCollector`] span methods that take an [`ExpectedSpan`] in
216/// addition to those that take a [`NewSpan`].
217///
218/// Use [`expect::id`] to construct a new, unset `ExpectedId`.
219///
220/// For more details on how to use this struct, see the documentation
221/// on [`ExpectedSpan::with_id`].
222///
223/// [`expect::id`]: fn@crate::expect::id
224/// [`MockCollector`]: struct@crate::collector::MockCollector
225#[derive(Clone, Default)]
226pub struct ExpectedId {
227    inner: Arc<AtomicU64>,
228}
229
230impl ExpectedSpan {
231    /// Sets a name to expect when matching a span.
232    ///
233    /// If an event is recorded with a name that differs from the one provided to this method, the
234    /// expectation will fail.
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// use tracing_mock::{collector, expect};
240    ///
241    /// let span = expect::span().named("span name");
242    ///
243    /// let (collector, handle) = collector::mock()
244    ///     .enter(span)
245    ///     .run_with_handle();
246    ///
247    /// tracing::collect::with_default(collector, || {
248    ///     let span = tracing::info_span!("span name");
249    ///     let _guard = span.enter();
250    /// });
251    ///
252    /// handle.assert_finished();
253    /// ```
254    ///
255    /// If only the name of the span needs to be validated, then
256    /// instead of using the `named` method, a string can be passed
257    /// to the [`MockCollector`] functions directly.
258    ///
259    /// ```
260    /// use tracing_mock::collector;
261    ///
262    /// let (collector, handle) = collector::mock()
263    ///     .enter("span name")
264    ///     .run_with_handle();
265    ///
266    /// tracing::collect::with_default(collector, || {
267    ///     let span = tracing::info_span!("span name");
268    ///     let _guard = span.enter();
269    /// });
270    ///
271    /// handle.assert_finished();
272    /// ```
273    ///
274    /// When the span name is different, the assertion will fail:
275    ///
276    /// ```should_panic
277    /// use tracing_mock::{collector, expect};
278    ///
279    /// let span = expect::span().named("span name");
280    ///
281    /// let (collector, handle) = collector::mock()
282    ///     .enter(span)
283    ///     .run_with_handle();
284    ///
285    /// tracing::collect::with_default(collector, || {
286    ///     let span = tracing::info_span!("a different span name");
287    ///     let _guard = span.enter();
288    /// });
289    ///
290    /// handle.assert_finished();
291    /// ```
292    ///
293    /// [`MockCollector`]: struct@crate::collector::MockCollector
294    pub fn named<I>(self, name: I) -> Self
295    where
296        I: Into<String>,
297    {
298        Self {
299            metadata: ExpectedMetadata {
300                name: Some(name.into()),
301                ..self.metadata
302            },
303            ..self
304        }
305    }
306
307    /// Sets the `ID` to expect when matching a span.
308    ///
309    /// The [`ExpectedId`] can be used to differentiate spans that are
310    /// otherwise identical. An [`ExpectedId`] needs to be attached to
311    /// an `ExpectedSpan` or [`NewSpan`] which is passed to
312    /// [`MockCollector::new_span`]. The same [`ExpectedId`] can then
313    /// be used to match the exact same span when passed to
314    /// [`MockCollector::enter`], [`MockCollector::exit`], and
315    /// [`MockCollector::drop_span`].
316    ///
317    /// This is especially useful when `tracing-mock` is being used to
318    /// test the traces being generated within your own crate, in which
319    /// case you may need to distinguish between spans which have
320    /// identical metadata but different field values, which can
321    /// otherwise only be checked in [`MockCollector::new_span`].
322    ///
323    /// # Examples
324    ///
325    /// Here we expect that the span that is created first is entered
326    /// second:
327    ///
328    /// ```
329    /// use tracing_mock::{collector, expect};
330    /// let id1 = expect::id();
331    /// let span1 = expect::span().named("span").with_id(id1.clone());
332    /// let id2 = expect::id();
333    /// let span2 = expect::span().named("span").with_id(id2.clone());
334    ///
335    /// let (collector, handle) = collector::mock()
336    ///     .new_span(&span1)
337    ///     .new_span(&span2)
338    ///     .enter(&span2)
339    ///     .enter(&span1)
340    ///     .run_with_handle();
341    ///
342    /// tracing::collect::with_default(collector, || {
343    ///     fn create_span() -> tracing::Span {
344    ///         tracing::info_span!("span")
345    ///     }
346    ///
347    ///     let span1 = create_span();
348    ///     let span2 = create_span();
349    ///
350    ///     let _guard2 = span2.enter();
351    ///     let _guard1 = span1.enter();
352    /// });
353    ///
354    /// handle.assert_finished();
355    /// ```
356    ///
357    /// Since `ExpectedId` implements `Into<ExpectedSpan>`, in cases where
358    /// only checking on Id is desired, a shorthand version of the previous
359    /// example can be used.
360    ///
361    /// ```
362    /// use tracing_mock::{collector, expect};
363    /// let id1 = expect::id();
364    /// let id2 = expect::id();
365    ///
366    /// let (collector, handle) = collector::mock()
367    ///     .new_span(&id1)
368    ///     .new_span(&id2)
369    ///     .enter(&id2)
370    ///     .enter(&id1)
371    ///     .run_with_handle();
372    ///
373    /// tracing::collect::with_default(collector, || {
374    ///     fn create_span() -> tracing::Span {
375    ///         tracing::info_span!("span")
376    ///     }
377    ///
378    ///     let span1 = create_span();
379    ///     let span2 = create_span();
380    ///
381    ///     let _guard2 = span2.enter();
382    ///     let _guard1 = span1.enter();
383    /// });
384    ///
385    /// handle.assert_finished();
386    /// ```
387    ///
388    /// If the order that the spans are entered changes, the test will
389    /// fail:
390    ///
391    /// ```should_panic
392    /// use tracing_mock::{collector, expect};
393    /// let id1 = expect::id();
394    /// let span1 = expect::span().named("span").with_id(id1.clone());
395    /// let id2 = expect::id();
396    /// let span2 = expect::span().named("span").with_id(id2.clone());
397    ///
398    /// let (collector, handle) = collector::mock()
399    ///     .new_span(&span1)
400    ///     .new_span(&span2)
401    ///     .enter(&span2)
402    ///     .enter(&span1)
403    ///     .run_with_handle();
404    ///
405    /// tracing::collect::with_default(collector, || {
406    ///     fn create_span() -> tracing::Span {
407    ///         tracing::info_span!("span")
408    ///     }
409    ///
410    ///     let span1 = create_span();
411    ///     let span2 = create_span();
412    ///
413    ///     let _guard1 = span1.enter();
414    ///     let _guard2 = span2.enter();
415    /// });
416    ///
417    /// handle.assert_finished();
418    /// ```
419    ///
420    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
421    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
422    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
423    /// [`MockCollector::drop_span`]: fn@crate::collector::MockCollector::drop_span
424    pub fn with_id(self, id: ExpectedId) -> Self {
425        Self {
426            id: Some(id),
427            ..self
428        }
429    }
430
431    /// Sets the [`Level`](tracing::Level) to expect when matching a span.
432    ///
433    /// If an span is record with a level that differs from the one provided to this method, the expectation will fail.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// use tracing_mock::{collector, expect};
439    ///
440    /// let span = expect::span()
441    ///     .at_level(tracing::Level::INFO);
442    ///
443    /// let (collector, handle) = collector::mock()
444    ///     .enter(span)
445    ///     .run_with_handle();
446    ///
447    /// tracing::collect::with_default(collector, || {
448    ///     let span = tracing::info_span!("span");
449    ///     let _guard = span.enter();
450    /// });
451    ///
452    /// handle.assert_finished();
453    /// ```
454    ///
455    /// Expecting a span at `INFO` level will fail if the event is
456    /// recorded at any other level:
457    ///
458    /// ```should_panic
459    /// use tracing_mock::{collector, expect};
460    ///
461    /// let span = expect::span()
462    ///     .at_level(tracing::Level::INFO);
463    ///
464    /// let (collector, handle) = collector::mock()
465    ///     .enter(span)
466    ///     .run_with_handle();
467    ///
468    /// tracing::collect::with_default(collector, || {
469    ///     let span = tracing::warn_span!("a serious span");
470    ///     let _guard = span.enter();
471    /// });
472    ///
473    /// handle.assert_finished();
474    /// ```
475    pub fn at_level(self, level: tracing::Level) -> Self {
476        Self {
477            metadata: ExpectedMetadata {
478                level: Some(level),
479                ..self.metadata
480            },
481            ..self
482        }
483    }
484
485    /// Sets the target to expect when matching a span.
486    ///
487    /// If an event is recorded with a target that doesn't match the
488    /// provided target, this expectation will fail.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// use tracing_mock::{collector, expect};
494    ///
495    /// let span = expect::span()
496    ///     .with_target("some_target");
497    ///
498    /// let (collector, handle) = collector::mock()
499    ///     .enter(span)
500    ///     .run_with_handle();
501    ///
502    /// tracing::collect::with_default(collector, || {
503    ///     let span = tracing::info_span!(target: "some_target", "span");
504    ///     let _guard = span.enter();
505    /// });
506    ///
507    /// handle.assert_finished();
508    /// ```
509    ///
510    /// The test will fail if the target is different:
511    ///
512    /// ```should_panic
513    /// use tracing_mock::{collector, expect};
514    ///
515    /// let span = expect::span()
516    ///     .with_target("some_target");
517    ///
518    /// let (collector, handle) = collector::mock()
519    ///     .enter(span)
520    ///     .run_with_handle();
521    ///
522    /// tracing::collect::with_default(collector, || {
523    ///     let span = tracing::info_span!(target: "a_different_target", "span");
524    ///     let _guard = span.enter();
525    /// });
526    ///
527    /// handle.assert_finished();
528    /// ```
529    pub fn with_target<I>(self, target: I) -> Self
530    where
531        I: Into<String>,
532    {
533        Self {
534            metadata: ExpectedMetadata {
535                target: Some(target.into()),
536                ..self.metadata
537            },
538            ..self
539        }
540    }
541
542    /// Configures this `ExpectedSpan` to expect the specified
543    /// [`ExpectedAncestry`]. A span's ancestry indicates whether it has a
544    /// parent or is a root span and whether the parent is explitly or
545    /// contextually assigned.
546    ///
547    /// **Note**: This method returns a [`NewSpan`] and as such, this
548    /// expectation can only be validated when expecting a new span via
549    /// [`MockCollector::new_span`]. It cannot be validated on
550    /// [`MockCollector::enter`], [`MockCollector::exit`], or any other
551    /// method on [`MockCollector`] that takes an `ExpectedSpan`.
552    ///
553    /// An _explicit_ parent span is one passed to the `span!` macro in the
554    /// `parent:` field. If no `parent:` field is specified, then the span
555    /// will have a contextually determined parent or be a contextual root if
556    /// there is no parent.
557    ///
558    /// If the ancestry is different from the provided one, this expectation
559    /// will fail.
560    ///
561    /// # Examples
562    ///
563    /// An explicit or contextual parent can be matched on an `ExpectedSpan`.
564    ///
565    /// ```
566    /// use tracing_mock::{collector, expect};
567    ///
568    /// let parent = expect::span()
569    ///     .named("parent_span")
570    ///     .with_target("custom-target")
571    ///     .at_level(tracing::Level::INFO);
572    /// let span = expect::span()
573    ///     .with_ancestry(expect::has_explicit_parent(&parent));
574    ///
575    /// let (collector, handle) = collector::mock()
576    ///     .new_span(&parent)
577    ///     .new_span(span)
578    ///     .run_with_handle();
579    ///
580    /// tracing::collect::with_default(collector, || {
581    ///     let parent = tracing::info_span!(target: "custom-target", "parent_span");
582    ///     tracing::info_span!(parent: parent.id(), "span");
583    /// });
584    ///
585    /// handle.assert_finished();
586    /// ```
587    ///
588    /// The functions `expect::has_explicit_parent` and
589    /// `expect::has_contextual_parent` take `Into<ExpectedSpan>`, so a string
590    /// passed directly will match on a span with that name, or an
591    /// [`ExpectedId`] can be passed to match a span with that Id.
592    ///
593    /// ```
594    /// use tracing_mock::{collector, expect};
595    ///
596    /// let span = expect::span()
597    ///     .with_ancestry(expect::has_explicit_parent("parent_span"));
598    ///
599    /// let (collector, handle) = collector::mock()
600    ///     .new_span(expect::span().named("parent_span"))
601    ///     .new_span(span)
602    ///     .run_with_handle();
603    ///
604    /// tracing::collect::with_default(collector, || {
605    ///     let parent = tracing::info_span!("parent_span");
606    ///     tracing::info_span!(parent: parent.id(), "span");
607    /// });
608    ///
609    /// handle.assert_finished();
610    /// ```
611    ///
612    /// In the following example, the expected span is an explicit root:
613    ///
614    /// ```
615    /// use tracing_mock::{collector, expect};
616    ///
617    /// let span = expect::span()
618    ///     .with_ancestry(expect::is_explicit_root());
619    ///
620    /// let (collector, handle) = collector::mock()
621    ///     .new_span(span)
622    ///     .run_with_handle();
623    ///
624    /// tracing::collect::with_default(collector, || {
625    ///     tracing::info_span!(parent: None, "span");
626    /// });
627    ///
628    /// handle.assert_finished();
629    /// ```
630    ///
631    /// When `expect::has_contextual_parent("parent_name")` is passed to
632    /// `with_ancestry` then the provided string is the name of the contextual
633    /// parent span to expect.
634    ///
635    /// ```
636    /// use tracing_mock::{collector, expect};
637    ///
638    /// let parent_span = expect::span().named("parent_span");
639    /// let span = expect::span()
640    ///     .with_ancestry(expect::has_contextual_parent("parent_span"));
641    ///
642    /// let (collector, handle) = collector::mock()
643    ///     .new_span(&parent_span)
644    ///     .enter(&parent_span)
645    ///     .new_span(span)
646    ///     .run_with_handle();
647    ///
648    /// tracing::collect::with_default(collector, || {
649    ///     let parent = tracing::info_span!("parent_span");
650    ///     let _guard = parent.enter();
651    ///     tracing::info_span!("span");
652    /// });
653    ///
654    /// handle.assert_finished();
655    /// ```
656    ///
657    /// In the following example, we expect that the matched span is
658    /// a contextually-determined root:
659    ///
660    /// ```
661    /// use tracing_mock::{collector, expect};
662    ///
663    /// let span = expect::span()
664    ///     .with_ancestry(expect::is_contextual_root());
665    ///
666    /// let (collector, handle) = collector::mock()
667    ///     .new_span(span)
668    ///     .run_with_handle();
669    ///
670    /// tracing::collect::with_default(collector, || {
671    ///     tracing::info_span!("span");
672    /// });
673    ///
674    /// handle.assert_finished();
675    /// ```
676    ///
677    /// In the example below, the expectation fails because the
678    /// span is *contextually*—as opposed to explicitly—within the span
679    /// `parent_span`:
680    ///
681    /// ```should_panic
682    /// use tracing_mock::{collector, expect};
683    ///
684    /// let parent_span = expect::span().named("parent_span");
685    /// let span = expect::span()
686    ///     .with_ancestry(expect::has_explicit_parent("parent_span"));
687    ///
688    /// let (collector, handle) = collector::mock()
689    ///     .new_span(&parent_span)
690    ///     .enter(&parent_span)
691    ///     .new_span(span)
692    ///     .run_with_handle();
693    ///
694    /// tracing::collect::with_default(collector, || {
695    ///     let parent = tracing::info_span!("parent_span");
696    ///     let _guard = parent.enter();
697    ///     tracing::info_span!("span");
698    /// });
699    ///
700    /// handle.assert_finished();
701    /// ```
702    ///
703    /// [`MockCollector`]: struct@crate::collector::MockCollector
704    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
705    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
706    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
707    pub fn with_ancestry(self, ancestry: ExpectedAncestry) -> NewSpan {
708        NewSpan {
709            ancestry: Some(ancestry),
710            span: self,
711            ..Default::default()
712        }
713    }
714
715    /// Adds fields to expect when matching a span.
716    ///
717    /// **Note**: This method returns a [`NewSpan`] and as such, this
718    /// expectation can only be validated when expecting a new span via
719    /// [`MockCollector::new_span`]. It cannot be validated on
720    /// [`MockCollector::enter`], [`MockCollector::exit`], or any other
721    /// method on [`MockCollector`] that takes an `ExpectedSpan`.
722    ///
723    /// If a span is recorded with fields that do not match the provided
724    /// [`ExpectedFields`], this expectation will fail.
725    ///
726    /// If the provided field is not present on the recorded span or
727    /// if the value for that field diffs, then the expectation
728    /// will fail.
729    ///
730    /// More information on the available validations is available in
731    /// the [`ExpectedFields`] documentation.
732    ///
733    /// # Examples
734    ///
735    /// ```
736    /// use tracing_mock::{collector, expect};
737    ///
738    /// let span = expect::span()
739    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
740    ///
741    /// let (collector, handle) = collector::mock()
742    ///     .new_span(span)
743    ///     .run_with_handle();
744    ///
745    /// tracing::collect::with_default(collector, || {
746    ///     tracing::info_span!("span", field.name = "field_value");
747    /// });
748    ///
749    /// handle.assert_finished();
750    /// ```
751    ///
752    /// A different field value will cause the expectation to fail:
753    ///
754    /// ```should_panic
755    /// use tracing_mock::{collector, expect};
756    ///
757    /// let span = expect::span()
758    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
759    ///
760    /// let (collector, handle) = collector::mock()
761    ///     .new_span(span)
762    ///     .run_with_handle();
763    ///
764    /// tracing::collect::with_default(collector, || {
765    ///     tracing::info_span!("span", field.name = "different_field_value");
766    /// });
767    ///
768    /// handle.assert_finished();
769    /// ```
770    ///
771    /// [`ExpectedFields`]: struct@crate::field::ExpectedFields
772    /// [`MockCollector`]: struct@crate::collector::MockCollector
773    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
774    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
775    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
776    pub fn with_fields<I>(self, fields: I) -> NewSpan
777    where
778        I: Into<ExpectedFields>,
779    {
780        NewSpan {
781            span: self,
782            fields: fields.into(),
783            ..Default::default()
784        }
785    }
786
787    pub(crate) fn id(&self) -> Option<&ExpectedId> {
788        self.id.as_ref()
789    }
790
791    pub(crate) fn name(&self) -> Option<&str> {
792        self.metadata.name.as_ref().map(String::as_ref)
793    }
794
795    pub(crate) fn level(&self) -> Option<tracing::Level> {
796        self.metadata.level
797    }
798
799    pub(crate) fn target(&self) -> Option<&str> {
800        self.metadata.target.as_deref()
801    }
802
803    pub(crate) fn check(&self, actual: &ActualSpan, ctx: impl fmt::Display, collector_name: &str) {
804        if let Some(expected_id) = &self.id {
805            expected_id.check(&actual.id(), format_args!("{ctx} a span"), collector_name);
806        }
807
808        match actual.metadata() {
809            Some(actual_metadata) => self.metadata.check(actual_metadata, ctx, collector_name),
810            None => {
811                if self.metadata.has_expectations() {
812                    panic!(
813                        "{}",
814                        format_args!(
815                            "[{collector_name}] expected {ctx} a span with valid metadata, \
816                            but got one with unknown Id={actual_id}",
817                            actual_id = actual.id().into_u64()
818                        )
819                    );
820                }
821            }
822        }
823    }
824}
825
826impl fmt::Debug for ExpectedSpan {
827    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
828        let mut s = f.debug_struct("MockSpan");
829
830        if let Some(id) = self.id() {
831            s.field("id", &id);
832        }
833
834        if let Some(name) = self.name() {
835            s.field("name", &name);
836        }
837
838        if let Some(level) = self.level() {
839            s.field("level", &format_args!("{:?}", level));
840        }
841
842        if let Some(target) = self.target() {
843            s.field("target", &target);
844        }
845
846        s.finish()
847    }
848}
849
850impl fmt::Display for ExpectedSpan {
851    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
852        if self.metadata.name.is_some() {
853            write!(f, "a span{}", self.metadata)
854        } else {
855            write!(f, "any span{}", self.metadata)
856        }
857    }
858}
859
860impl<S> From<S> for NewSpan
861where
862    S: Into<ExpectedSpan>,
863{
864    fn from(span: S) -> Self {
865        Self {
866            span: span.into(),
867            ..Default::default()
868        }
869    }
870}
871
872impl NewSpan {
873    /// Configures this `NewSpan` to expect the specified [`ExpectedAncestry`].
874    /// A span's ancestry indicates whether it has a parent or is a root span
875    /// and whether the parent is explitly or contextually assigned.
876    ///
877    /// For more information and examples, see the documentation on
878    /// [`ExpectedSpan::with_ancestry`].
879    pub fn with_ancestry(self, ancestry: ExpectedAncestry) -> NewSpan {
880        NewSpan {
881            ancestry: Some(ancestry),
882            ..self
883        }
884    }
885
886    /// Adds fields to expect when matching a span.
887    ///
888    /// For more information and examples, see the documentation on
889    /// [`ExpectedSpan::with_fields`].
890    ///
891    /// [`ExpectedSpan::with_fields`]: fn@crate::span::ExpectedSpan::with_fields
892    pub fn with_fields<I>(self, fields: I) -> NewSpan
893    where
894        I: Into<ExpectedFields>,
895    {
896        NewSpan {
897            fields: fields.into(),
898            ..self
899        }
900    }
901
902    pub(crate) fn check(
903        &mut self,
904        span: &tracing_core::span::Attributes<'_>,
905        get_ancestry: impl FnOnce() -> ActualAncestry,
906        collector_name: &str,
907    ) {
908        let meta = span.metadata();
909        let name = meta.name();
910        self.span.metadata.check(meta, "a new span", collector_name);
911        let mut checker = self.fields.checker(name, collector_name);
912        span.record(&mut checker);
913        checker.finish();
914
915        if let Some(ref expected_ancestry) = self.ancestry {
916            let actual_ancestry = get_ancestry();
917            expected_ancestry.check(
918                &actual_ancestry,
919                format_args!("span `{}`", name),
920                collector_name,
921            );
922        }
923    }
924}
925
926impl fmt::Display for NewSpan {
927    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928        write!(f, "a new span{}", self.span.metadata)?;
929        if !self.fields.is_empty() {
930            write!(f, " with {}", self.fields)?;
931        }
932        Ok(())
933    }
934}
935
936impl fmt::Debug for NewSpan {
937    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
938        let mut s = f.debug_struct("NewSpan");
939
940        if let Some(name) = self.span.name() {
941            s.field("name", &name);
942        }
943
944        if let Some(level) = self.span.level() {
945            s.field("level", &format_args!("{:?}", level));
946        }
947
948        if let Some(target) = self.span.target() {
949            s.field("target", &target);
950        }
951
952        if let Some(ref parent) = self.ancestry {
953            s.field("parent", &format_args!("{:?}", parent));
954        }
955
956        if !self.fields.is_empty() {
957            s.field("fields", &self.fields);
958        }
959
960        s.finish()
961    }
962}
963
964impl PartialEq for ExpectedId {
965    fn eq(&self, other: &Self) -> bool {
966        self.inner.load(Ordering::Relaxed) == other.inner.load(Ordering::Relaxed)
967    }
968}
969
970impl Eq for ExpectedId {}
971
972impl fmt::Debug for ExpectedId {
973    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
974        f.debug_tuple("ExpectedId").field(&self.inner).finish()
975    }
976}
977
978impl ExpectedId {
979    const UNSET: u64 = 0;
980
981    pub(crate) fn new_unset() -> Self {
982        Self {
983            inner: Arc::new(AtomicU64::from(Self::UNSET)),
984        }
985    }
986
987    pub(crate) fn set(&self, span_id: u64) -> Result<(), SetActualSpanIdError> {
988        self.inner
989            .compare_exchange(Self::UNSET, span_id, Ordering::Relaxed, Ordering::Relaxed)
990            .map_err(|current| SetActualSpanIdError {
991                previous_span_id: current,
992                new_span_id: span_id,
993            })?;
994        Ok(())
995    }
996
997    pub(crate) fn check(
998        &self,
999        actual: &tracing_core::span::Id,
1000        ctx: fmt::Arguments<'_>,
1001        collector_name: &str,
1002    ) {
1003        let expected_id = self.inner.load(Ordering::Relaxed);
1004        let actual_id = actual.into_u64();
1005
1006        assert!(
1007            expected_id != Self::UNSET,
1008            "{}",
1009            format!(
1010                "\n[{collector_name}] expected {ctx} with an expected Id set,\n\
1011                [{collector_name}] but it hasn't been, perhaps this `ExpectedId` \
1012                wasn't used in a call to `new_span()`?"
1013            )
1014        );
1015
1016        assert_eq!(
1017            expected_id,
1018            actual_id,
1019            "{}",
1020            format_args!(
1021                "\n[{collector_name}] expected {ctx} with Id `{expected_id}`,\n\
1022                [{collector_name}] but got one with Id `{actual_id}` instead",
1023            )
1024        );
1025    }
1026}
1027
1028#[derive(Debug)]
1029pub(crate) struct SetActualSpanIdError {
1030    previous_span_id: u64,
1031    new_span_id: u64,
1032}
1033
1034impl fmt::Display for SetActualSpanIdError {
1035    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1036        write!(
1037            f,
1038            "Could not set `ExpecedId` to {new}, \
1039            it had already been set to {previous}",
1040            new = self.new_span_id,
1041            previous = self.previous_span_id
1042        )
1043    }
1044}
1045
1046impl error::Error for SetActualSpanIdError {}