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 subscriber API in the
5//! [`subscriber`] 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 [`MockSubscriber`].
10//!
11//! # Examples
12//!
13//! ```
14//! use tracing_mock::{expect, subscriber};
15//!
16//! let span = expect::span()
17//! .named("interesting_span")
18//! .at_level(tracing::Level::INFO);
19//!
20//! let (subscriber, handle) = subscriber::mock()
21//! .enter(&span)
22//! .exit(&span)
23//! .run_with_handle();
24//!
25//! tracing::subscriber::with_default(subscriber, || {
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 subscriber 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::subscriber;
39//!
40//! let (subscriber, handle) = subscriber::mock()
41//! .enter("interesting_span")
42//! .run_with_handle();
43//!
44//! tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
66//! .new_span("parent_span")
67//! .new_span(new_span)
68//! .enter(&span)
69//! .exit(&span)
70//! .run_with_handle();
71//!
72//! tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
91//!
92//! let span = expect::span()
93//! .named("interesting_span")
94//! .at_level(tracing::Level::INFO);
95//!
96//! let (subscriber, handle) = subscriber::mock()
97//! .enter(&span)
98//! .exit(&span)
99//! .run_with_handle();
100//!
101//! tracing::subscriber::with_default(subscriber, || {
102//! let span = tracing::info_span!("another_span");
103//! let _guard = span.enter();
104//! });
105//!
106//! handle.assert_finished();
107//! ```
108//!
109//! [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
110//! [`subscriber`]: mod@crate::subscriber
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 subscriber API in the
129/// [`subscriber`] module.
130///
131/// [`subscriber`]: mod@crate::subscriber
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 [`MockSubscriber::new_span`]. They
163/// cannot be validated on [`MockSubscriber::enter`],
164/// [`MockSubscriber::exit`], or any other method on [`MockSubscriber`]
165/// that takes an `ExpectedSpan`.
166///
167/// For more details on how to use this struct, see the documentation
168/// on the [`subscriber`] module.
169///
170/// [`subscriber`]: mod@crate::subscriber
171/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
172/// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
173/// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
174/// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::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/// [`MockSubscriber`] 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/// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
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::{expect, subscriber};
240 ///
241 /// let span = expect::span().named("span name");
242 ///
243 /// let (subscriber, handle) = subscriber::mock()
244 /// .enter(span)
245 /// .run_with_handle();
246 ///
247 /// tracing::subscriber::with_default(subscriber, || {
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 [`MockSubscriber`] functions directly.
258 ///
259 /// ```
260 /// use tracing_mock::subscriber;
261 ///
262 /// let (subscriber, handle) = subscriber::mock()
263 /// .enter("span name")
264 /// .run_with_handle();
265 ///
266 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
278 ///
279 /// let span = expect::span().named("span name");
280 ///
281 /// let (subscriber, handle) = subscriber::mock()
282 /// .enter(span)
283 /// .run_with_handle();
284 ///
285 /// tracing::subscriber::with_default(subscriber, || {
286 /// let span = tracing::info_span!("a different span name");
287 /// let _guard = span.enter();
288 /// });
289 ///
290 /// handle.assert_finished();
291 /// ```
292 ///
293 /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
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 /// [`MockSubscriber::new_span`]. The same [`ExpectedId`] can then
313 /// be used to match the exact same span when passed to
314 /// [`MockSubscriber::enter`], [`MockSubscriber::exit`], and
315 /// [`MockSubscriber::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 [`MockSubscriber::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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
336 /// .new_span(&span1)
337 /// .new_span(&span2)
338 /// .enter(&span2)
339 /// .enter(&span1)
340 /// .run_with_handle();
341 ///
342 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
363 /// let id1 = expect::id();
364 /// let id2 = expect::id();
365 ///
366 /// let (subscriber, handle) = subscriber::mock()
367 /// .new_span(&id1)
368 /// .new_span(&id2)
369 /// .enter(&id2)
370 /// .enter(&id1)
371 /// .run_with_handle();
372 ///
373 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
399 /// .new_span(&span1)
400 /// .new_span(&span2)
401 /// .enter(&span2)
402 /// .enter(&span1)
403 /// .run_with_handle();
404 ///
405 /// tracing::subscriber::with_default(subscriber, || {
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 /// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::new_span
421 /// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
422 /// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
423 /// [`MockSubscriber::drop_span`]: fn@crate::subscriber::MockSubscriber::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::{expect, subscriber};
439 ///
440 /// let span = expect::span()
441 /// .at_level(tracing::Level::INFO);
442 ///
443 /// let (subscriber, handle) = subscriber::mock()
444 /// .enter(span)
445 /// .run_with_handle();
446 ///
447 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
460 ///
461 /// let span = expect::span()
462 /// .at_level(tracing::Level::INFO);
463 ///
464 /// let (subscriber, handle) = subscriber::mock()
465 /// .enter(span)
466 /// .run_with_handle();
467 ///
468 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
494 ///
495 /// let span = expect::span()
496 /// .with_target("some_target");
497 ///
498 /// let (subscriber, handle) = subscriber::mock()
499 /// .enter(span)
500 /// .run_with_handle();
501 ///
502 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
514 ///
515 /// let span = expect::span()
516 /// .with_target("some_target");
517 ///
518 /// let (subscriber, handle) = subscriber::mock()
519 /// .enter(span)
520 /// .run_with_handle();
521 ///
522 /// tracing::subscriber::with_default(subscriber, || {
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 /// [`MockSubscriber::new_span`]. It cannot be validated on
550 /// [`MockSubscriber::enter`], [`MockSubscriber::exit`], or any other
551 /// method on [`MockSubscriber`] 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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
576 /// .new_span(&parent)
577 /// .new_span(span)
578 /// .run_with_handle();
579 ///
580 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
595 ///
596 /// let span = expect::span()
597 /// .with_ancestry(expect::has_explicit_parent("parent_span"));
598 ///
599 /// let (subscriber, handle) = subscriber::mock()
600 /// .new_span(expect::span().named("parent_span"))
601 /// .new_span(span)
602 /// .run_with_handle();
603 ///
604 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
616 ///
617 /// let span = expect::span()
618 /// .with_ancestry(expect::is_explicit_root());
619 ///
620 /// let (subscriber, handle) = subscriber::mock()
621 /// .new_span(span)
622 /// .run_with_handle();
623 ///
624 /// tracing::subscriber::with_default(subscriber, || {
625 /// tracing::info_span!(parent: None, "span");
626 /// });
627 ///
628 /// handle.assert_finished();
629 /// ```
630 ///
631 /// In the example below, the expectation fails because the
632 /// span is *contextually*—as opposed to explicitly—within the span
633 /// `parent_span`:
634 ///
635 /// ```should_panic
636 /// use tracing_mock::{expect, subscriber};
637 ///
638 /// let parent_span = expect::span().named("parent_span");
639 /// let span = expect::span()
640 /// .with_ancestry(expect::has_explicit_parent("parent_span"));
641 ///
642 /// let (subscriber, handle) = subscriber::mock()
643 /// .new_span(&parent_span)
644 /// .enter(&parent_span)
645 /// .new_span(span)
646 /// .run_with_handle();
647 ///
648 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
662 ///
663 /// let span = expect::span()
664 /// .with_ancestry(expect::is_contextual_root());
665 ///
666 /// let (subscriber, handle) = subscriber::mock()
667 /// .new_span(span)
668 /// .run_with_handle();
669 ///
670 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
689 /// .new_span(&parent_span)
690 /// .enter(&parent_span)
691 /// .new_span(span)
692 /// .run_with_handle();
693 ///
694 /// tracing::subscriber::with_default(subscriber, || {
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 /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
704 /// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
705 /// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
706 /// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::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 /// [`MockSubscriber::new_span`]. It cannot be validated on
720 /// [`MockSubscriber::enter`], [`MockSubscriber::exit`], or any other
721 /// method on [`MockSubscriber`] 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::{expect, subscriber};
737 ///
738 /// let span = expect::span()
739 /// .with_fields(expect::field("field.name").with_value(&"field_value"));
740 ///
741 /// let (subscriber, handle) = subscriber::mock()
742 /// .new_span(span)
743 /// .run_with_handle();
744 ///
745 /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
756 ///
757 /// let span = expect::span()
758 /// .with_fields(expect::field("field.name").with_value(&"field_value"));
759 ///
760 /// let (subscriber, handle) = subscriber::mock()
761 /// .new_span(span)
762 /// .run_with_handle();
763 ///
764 /// tracing::subscriber::with_default(subscriber, || {
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 /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
773 /// [`MockSubscriber::enter`]: fn@crate::subscriber::MockSubscriber::enter
774 /// [`MockSubscriber::exit`]: fn@crate::subscriber::MockSubscriber::exit
775 /// [`MockSubscriber::new_span`]: fn@crate::subscriber::MockSubscriber::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, subscriber_name: &str) {
804 if let Some(expected_id) = &self.id {
805 expected_id.check(&actual.id(), format_args!("{ctx} a span"), subscriber_name);
806 }
807
808 match actual.metadata() {
809 Some(actual_metadata) => self.metadata.check(actual_metadata, ctx, subscriber_name),
810 None => {
811 if self.metadata.has_expectations() {
812 panic!(
813 "{}",
814 format_args!(
815 "[{subscriber_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 subscriber_name: &str,
907 ) {
908 let meta = span.metadata();
909 let name = meta.name();
910 self.span
911 .metadata
912 .check(meta, "a new span", subscriber_name);
913 let mut checker = self.fields.checker(name, subscriber_name);
914 span.record(&mut checker);
915 checker.finish();
916
917 if let Some(ref expected_ancestry) = self.ancestry {
918 let actual_ancestry = get_ancestry();
919 expected_ancestry.check(
920 &actual_ancestry,
921 format_args!("span `{}`", name),
922 subscriber_name,
923 );
924 }
925 }
926}
927
928impl fmt::Display for NewSpan {
929 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
930 write!(f, "a new span{}", self.span.metadata)?;
931 if !self.fields.is_empty() {
932 write!(f, " with {}", self.fields)?;
933 }
934 Ok(())
935 }
936}
937
938impl fmt::Debug for NewSpan {
939 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
940 let mut s = f.debug_struct("NewSpan");
941
942 if let Some(name) = self.span.name() {
943 s.field("name", &name);
944 }
945
946 if let Some(level) = self.span.level() {
947 s.field("level", &format_args!("{:?}", level));
948 }
949
950 if let Some(target) = self.span.target() {
951 s.field("target", &target);
952 }
953
954 if let Some(ref parent) = self.ancestry {
955 s.field("parent", &format_args!("{:?}", parent));
956 }
957
958 if !self.fields.is_empty() {
959 s.field("fields", &self.fields);
960 }
961
962 s.finish()
963 }
964}
965
966impl PartialEq for ExpectedId {
967 fn eq(&self, other: &Self) -> bool {
968 self.inner.load(Ordering::Relaxed) == other.inner.load(Ordering::Relaxed)
969 }
970}
971
972impl Eq for ExpectedId {}
973
974impl fmt::Debug for ExpectedId {
975 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
976 f.debug_tuple("ExpectedId").field(&self.inner).finish()
977 }
978}
979
980impl ExpectedId {
981 const UNSET: u64 = 0;
982
983 pub(crate) fn new_unset() -> Self {
984 Self {
985 inner: Arc::new(AtomicU64::from(Self::UNSET)),
986 }
987 }
988
989 pub(crate) fn set(&self, span_id: u64) -> Result<(), SetActualSpanIdError> {
990 self.inner
991 .compare_exchange(Self::UNSET, span_id, Ordering::Relaxed, Ordering::Relaxed)
992 .map_err(|current| SetActualSpanIdError {
993 previous_span_id: current,
994 new_span_id: span_id,
995 })?;
996 Ok(())
997 }
998
999 pub(crate) fn check(
1000 &self,
1001 actual: &tracing_core::span::Id,
1002 ctx: fmt::Arguments<'_>,
1003 subscriber_name: &str,
1004 ) {
1005 let expected_id = self.inner.load(Ordering::Relaxed);
1006 let actual_id = actual.into_u64();
1007
1008 assert!(
1009 expected_id != Self::UNSET,
1010 "{}",
1011 format!(
1012 "\n[{subscriber_name}] expected {ctx} with an expected Id set,\n\
1013 [{subscriber_name}] but it hasn't been, perhaps this `ExpectedId` \
1014 wasn't used in a call to `new_span()`?"
1015 )
1016 );
1017
1018 assert_eq!(
1019 expected_id,
1020 actual_id,
1021 "{}",
1022 format_args!(
1023 "\n[{subscriber_name}] expected {ctx} with Id `{expected_id}`,\n\
1024 [{subscriber_name}] but got one with Id `{actual_id}` instead",
1025 )
1026 );
1027 }
1028}
1029
1030#[derive(Debug)]
1031pub(crate) struct SetActualSpanIdError {
1032 previous_span_id: u64,
1033 new_span_id: u64,
1034}
1035
1036impl fmt::Display for SetActualSpanIdError {
1037 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1038 write!(
1039 f,
1040 "Could not set `ExpecedId` to {new}, \
1041 it had already been set to {previous}",
1042 new = self.new_span_id,
1043 previous = self.previous_span_id
1044 )
1045 }
1046}
1047
1048impl error::Error for SetActualSpanIdError {}