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 {}