tracing_mock/subscriber.rs
1//! An implementation of the [`Subscribe`] trait which validates that
2//! the `tracing` data it receives matches the expected output for a test.
3//!
4//!
5//! The [`MockSubscriber`] is the central component in these tools. The
6//! `MockSubscriber` has expectations set on it which are later
7//! validated as the code under test is run.
8//!
9//! ```
10//! use tracing_mock::{expect, subscriber};
11//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
12//!
13//! let (subscriber, handle) = subscriber::mock()
14//! // Expect a single event with a specified message
15//! .event(expect::event().with_fields(expect::msg("droids")))
16//! .run_with_handle();
17//!
18//! // Use `set_default` to apply the `MockSubscriber` until the end
19//! // of the current scope (when the guard `_collect` is dropped).
20//! let _collect = tracing_subscriber::registry()
21//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
22//! .set_default();
23//!
24//! // These *are* the droids we are looking for
25//! tracing::info!("droids");
26//!
27//! // Use the handle to check the assertions. This line will panic if an
28//! // assertion is not met.
29//! handle.assert_finished();
30//! ```
31//!
32//! A more complex example may consider multiple spans and events with
33//! their respective fields:
34//!
35//! ```
36//! use tracing_mock::{expect, subscriber};
37//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
38//!
39//! let span = expect::span()
40//! .named("my_span");
41//! let (subscriber, handle) = subscriber::mock()
42//! // Enter a matching span
43//! .enter(&span)
44//! // Record an event with message "collect parting message"
45//! .event(expect::event().with_fields(expect::msg("say hello")))
46//! // Exit a matching span
47//! .exit(&span)
48//! // Expect no further messages to be recorded
49//! .only()
50//! // Return the collector and handle
51//! .run_with_handle();
52//!
53//! // Use `set_default` to apply the `MockSubscriber` until the end
54//! // of the current scope (when the guard `_collect` is dropped).
55//! let _collect = tracing_subscriber::registry()
56//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
57//! .set_default();
58//!
59//! {
60//! let span = tracing::trace_span!(
61//! "my_span",
62//! greeting = "hello world",
63//! );
64//!
65//! let _guard = span.enter();
66//! tracing::info!("say hello");
67//! }
68//!
69//! // Use the handle to check the assertions. This line will panic if an
70//! // assertion is not met.
71//! handle.assert_finished();
72//! ```
73//!
74//! If we modify the previous example so that we **don't** enter the
75//! span before recording an event, the test will fail:
76//!
77//! ```should_panic
78//! use tracing_mock::{expect, subscriber};
79//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
80//!
81//! let span = expect::span()
82//! .named("my_span");
83//! let (subscriber, handle) = subscriber::mock()
84//! // Enter a matching span
85//! .enter(&span)
86//! // Record an event with message "collect parting message"
87//! .event(expect::event().with_fields(expect::msg("say hello")))
88//! // Exit a matching span
89//! .exit(&span)
90//! // Expect no further messages to be recorded
91//! .only()
92//! // Return the collector and handle
93//! .run_with_handle();
94//!
95//! // Use `set_default` to apply the `MockSubscriber` until the end
96//! // of the current scope (when the guard `_collect` is dropped).
97//! let _collect = tracing_subscriber::registry()
98//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
99//! .set_default();
100//!
101//! {
102//! let span = tracing::trace_span!(
103//! "my_span",
104//! greeting = "hello world",
105//! );
106//!
107//! // Don't enter the span.
108//! // let _guard = span.enter();
109//! tracing::info!("say hello");
110//! }
111//!
112//! // Use the handle to check the assertions. This line will panic if an
113//! // assertion is not met.
114//! handle.assert_finished();
115//! ```
116//!
117//! [`Subscribe`]: trait@tracing_subscriber::subscribe::Subscribe
118use std::{
119 collections::VecDeque,
120 fmt,
121 sync::{Arc, Mutex},
122};
123
124use tracing_core::{
125 span::{Attributes, Id, Record},
126 Collect, Event,
127};
128use tracing_subscriber::{
129 registry::{LookupSpan, SpanRef},
130 subscribe::{Context, Subscribe},
131};
132
133use crate::{
134 ancestry::{get_ancestry, ActualAncestry, HasAncestry},
135 collector::MockHandle,
136 event::ExpectedEvent,
137 expect::Expect,
138 span::{ActualSpan, ExpectedSpan, NewSpan},
139};
140
141/// Create a [`MockSubscriberBuilder`] used to construct a
142/// [`MockSubscriber`].
143///
144/// For additional information and examples, see the [`subscriber`]
145/// module and [`MockSubscriberBuilder`] documentation.
146///
147/// # Examples
148///
149/// ```
150/// use tracing_mock::{expect, subscriber};
151/// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
152///
153/// let span = expect::span()
154/// .named("my_span");
155/// let (subscriber, handle) = subscriber::mock()
156/// // Enter a matching span
157/// .enter(&span)
158/// // Record an event with message "collect parting message"
159/// .event(expect::event().with_fields(expect::msg("say hello")))
160/// // Exit a matching span
161/// .exit(&span)
162/// // Expect no further messages to be recorded
163/// .only()
164/// // Return the collector and handle
165/// .run_with_handle();
166///
167/// // Use `set_default` to apply the `MockSubscriber` until the end
168/// // of the current scope (when the guard `_collect` is dropped).
169/// let _collect = tracing_subscriber::registry()
170/// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
171/// .set_default();
172///
173/// {
174/// let span = tracing::trace_span!(
175/// "my_span",
176/// greeting = "hello world",
177/// );
178///
179/// let _guard = span.enter();
180/// tracing::info!("say hello");
181/// }
182///
183/// // Use the handle to check the assertions. This line will panic if an
184/// // assertion is not met.
185/// handle.assert_finished();
186/// ```
187///
188/// [`subscriber`]: mod@crate::subscriber
189#[must_use]
190pub fn mock() -> MockSubscriberBuilder {
191 MockSubscriberBuilder {
192 expected: Default::default(),
193 name: std::thread::current()
194 .name()
195 .map(String::from)
196 .unwrap_or_default(),
197 }
198}
199
200/// Create a [`MockSubscriberBuilder`] with a name already set.
201///
202/// This constructor is equivalent to calling
203/// [`MockSubscriberBuilder::named`] in the following way
204/// `subscriber::mock().named(name)`.
205///
206/// For additional information and examples, see the [`subscriber`]
207/// module and [`MockSubscriberBuilder`] documentation.
208///
209/// # Examples
210///
211/// The example from [`MockSubscriberBuilder::named`] could be
212/// rewritten as:
213///
214/// ```should_panic
215/// use tracing_mock::{subscriber, expect};
216/// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
217///
218/// let (subscriber_1, handle_1) = subscriber::named("subscriber-1")
219/// .event(expect::event())
220/// .run_with_handle();
221///
222/// let (subscriber_2, handle_2) = subscriber::named("subscriber-2")
223/// .event(expect::event())
224/// .run_with_handle();
225///
226/// let _collect = tracing_subscriber::registry()
227/// .with(
228/// subscriber_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
229/// )
230/// .set_default();
231/// {
232/// let _collect = tracing_subscriber::registry()
233/// .with(
234/// subscriber_1
235/// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
236/// )
237/// .set_default();
238///
239/// tracing::info!("a");
240/// }
241///
242/// handle_1.assert_finished();
243/// handle_2.assert_finished();
244/// ```
245///
246/// [`named`]: fn@crate::subscriber::MockSubscriberBuilder::named
247/// [`subscriber`]: mod@crate::subscriber
248#[must_use]
249pub fn named(name: impl std::fmt::Display) -> MockSubscriberBuilder {
250 mock().named(name)
251}
252
253/// A builder for constructing [`MockSubscriber`]s.
254///
255/// The methods on this builder set expectations which are then
256/// validated by the constructed [`MockSubscriber`].
257///
258/// For a detailed description and examples see the documentation
259/// for the methods and the [`subscriber`] module.
260///
261/// [`subscriber`]: mod@crate::subscriber
262
263#[derive(Debug)]
264pub struct MockSubscriberBuilder {
265 expected: VecDeque<Expect>,
266 name: String,
267}
268
269/// A subscriber which validates the traces it receives.
270///
271/// A `MockSubscriber` is constructed with a
272/// [`MockSubscriberBuilder`]. For a detailed description and examples,
273/// see the documentation for that struct and for the [`subscriber`]
274/// module.
275///
276/// [`subscriber`]: mod@crate::subscriber
277pub struct MockSubscriber {
278 expected: Arc<Mutex<VecDeque<Expect>>>,
279 current: Mutex<Vec<Id>>,
280 name: String,
281}
282
283impl MockSubscriberBuilder {
284 /// Overrides the name printed by the mock subscriber's debugging output.
285 ///
286 /// The debugging output is displayed if the test panics, or if the test is
287 /// run with `--nocapture`.
288 ///
289 /// By default, the mock subscriber's name is the name of the test
290 /// (*technically*, the name of the thread where it was created, which is
291 /// the name of the test unless tests are run with `--test-threads=1`).
292 /// When a test has only one mock subscriber, this is sufficient. However,
293 /// some tests may include multiple subscribers, in order to test
294 /// interactions between multiple subscribers. In that case, it can be
295 /// helpful to give each subscriber a separate name to distinguish where the
296 /// debugging output comes from.
297 ///
298 /// # Examples
299 ///
300 /// In the following example, we create two subscribers, both
301 /// expecting to receive an event. As we only record a single
302 /// event, the test will fail:
303 ///
304 /// ```should_panic
305 /// use tracing_mock::{subscriber, expect};
306 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
307 ///
308 /// let (subscriber_1, handle_1) = subscriber::mock()
309 /// .named("subscriber-1")
310 /// .event(expect::event())
311 /// .run_with_handle();
312 ///
313 /// let (subscriber_2, handle_2) = subscriber::mock()
314 /// .named("subscriber-2")
315 /// .event(expect::event())
316 /// .run_with_handle();
317 ///
318 /// let _collect = tracing_subscriber::registry()
319 /// .with(
320 /// subscriber_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
321 /// )
322 /// .set_default();
323 /// {
324 /// let _collect = tracing_subscriber::registry()
325 /// .with(
326 /// subscriber_1
327 /// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
328 /// )
329 /// .set_default();
330 ///
331 /// tracing::info!("a");
332 /// }
333 ///
334 /// handle_1.assert_finished();
335 /// handle_2.assert_finished();
336 /// ```
337 ///
338 /// In the test output, we see that the subscriber which didn't
339 /// received the event was the one named `subscriber-2`, which is
340 /// correct as the subscriber named `subscriber-1` was the default
341 /// when the event was recorded:
342 ///
343 /// ```text
344 /// [main::subscriber-2] more notifications expected: [
345 /// Event(
346 /// MockEvent,
347 /// ),
348 /// ]', tracing-mock/src/collector.rs:472:13
349 /// ```
350 pub fn named(mut self, name: impl fmt::Display) -> Self {
351 use std::fmt::Write;
352 if !self.name.is_empty() {
353 write!(&mut self.name, "::{}", name).unwrap();
354 } else {
355 self.name = name.to_string();
356 }
357 self
358 }
359
360 /// Adds an expectation that an event matching the [`ExpectedEvent`]
361 /// will be recorded next.
362 ///
363 /// The `event` can be a default mock which will match any event
364 /// (`expect::event()`) or can include additional expectations.
365 /// See the [`ExpectedEvent`] documentation for more details.
366 ///
367 /// If an event is recorded that doesn't match the `ExpectedEvent`,
368 /// or if something else (such as entering a span) is recorded
369 /// first, then the expectation will fail.
370 ///
371 /// # Examples
372 ///
373 /// ```
374 /// use tracing_mock::{expect, subscriber};
375 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
376 ///
377 /// let (subscriber, handle) = subscriber::mock()
378 /// .event(expect::event())
379 /// .run_with_handle();
380 ///
381 /// let _collect = tracing_subscriber::registry()
382 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
383 /// .set_default();
384 ///
385 /// tracing::info!("event");
386 ///
387 /// handle.assert_finished();
388 /// ```
389 ///
390 /// A span is entered before the event, causing the test to fail:
391 ///
392 /// ```should_panic
393 /// use tracing_mock::{expect, subscriber};
394 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
395 ///
396 /// let (subscriber, handle) = subscriber::mock()
397 /// .event(expect::event())
398 /// .run_with_handle();
399 ///
400 /// let _collect = tracing_subscriber::registry()
401 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
402 /// .set_default();
403 ///
404 /// let span = tracing::info_span!("span");
405 /// let _guard = span.enter();
406 /// tracing::info!("event");
407 ///
408 /// handle.assert_finished();
409 /// ```
410 pub fn event(mut self, event: ExpectedEvent) -> Self {
411 self.expected.push_back(Expect::Event(event));
412 self
413 }
414
415 /// Adds an expectation that the creation of a span will be
416 /// recorded next.
417 ///
418 /// This function accepts `Into<NewSpan>` instead of
419 /// [`ExpectedSpan`] directly. [`NewSpan`] can be used to test
420 /// span fields and the span ancestry.
421 ///
422 /// The new span doesn't need to be entered for this expectation
423 /// to succeed.
424 ///
425 /// If a span is recorded that doesn't match the `ExpectedSpan`,
426 /// or if something else (such as an event) is recorded first,
427 /// then the expectation will fail.
428 ///
429 /// # Examples
430 ///
431 /// ```
432 /// use tracing_mock::{expect, subscriber};
433 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
434 ///
435 /// let span = expect::span()
436 /// .at_level(tracing::Level::INFO)
437 /// .named("the span we're testing")
438 /// .with_fields(expect::field("testing").with_value(&"yes"));
439 /// let (subscriber, handle) = subscriber::mock()
440 /// .new_span(span)
441 /// .run_with_handle();
442 ///
443 /// let _collect = tracing_subscriber::registry()
444 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
445 /// .set_default();
446 ///
447 /// _ = tracing::info_span!("the span we're testing", testing = "yes");
448 ///
449 /// handle.assert_finished();
450 /// ```
451 ///
452 /// An event is recorded before the span is created, causing the
453 /// test to fail:
454 ///
455 /// ```should_panic
456 /// use tracing_mock::{expect, subscriber};
457 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
458 ///
459 /// let span = expect::span()
460 /// .at_level(tracing::Level::INFO)
461 /// .named("the span we're testing")
462 /// .with_fields(expect::field("testing").with_value(&"yes"));
463 /// let (subscriber, handle) = subscriber::mock()
464 /// .new_span(span)
465 /// .run_with_handle();
466 ///
467 /// let _collect = tracing_subscriber::registry()
468 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
469 /// .set_default();
470 ///
471 /// tracing::info!("an event");
472 /// _ = tracing::info_span!("the span we're testing", testing = "yes");
473 ///
474 /// handle.assert_finished();
475 /// ```
476 ///
477 /// [`ExpectedSpan`]: struct@crate::span::ExpectedSpan
478 /// [`NewSpan`]: struct@crate::span::NewSpan
479 pub fn new_span<I>(mut self, new_span: I) -> Self
480 where
481 I: Into<NewSpan>,
482 {
483 self.expected.push_back(Expect::NewSpan(new_span.into()));
484 self
485 }
486
487 /// Adds an expectation that entering a span matching the
488 /// [`ExpectedSpan`] will be recorded next.
489 ///
490 /// This expectation is generally accompanied by a call to
491 /// [`exit`], since an entered span will typically be exited. If used
492 /// together with [`only`], this is likely necessary, because the span
493 /// will be dropped before the test completes (except in rare cases,
494 /// such as if [`std::mem::forget`] is used).
495 ///
496 /// If the span that is entered doesn't match the [`ExpectedSpan`],
497 /// or if something else (such as an event) is recorded first,
498 /// then the expectation will fail.
499 ///
500 /// # Examples
501 ///
502 /// ```
503 /// use tracing_mock::{expect, subscriber};
504 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
505 ///
506 /// let span = expect::span()
507 /// .at_level(tracing::Level::INFO)
508 /// .named("the span we're testing");
509 /// let (subscriber, handle) = subscriber::mock()
510 /// .enter(&span)
511 /// .exit(&span)
512 /// .only()
513 /// .run_with_handle();
514 ///
515 /// let _collect = tracing_subscriber::registry()
516 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
517 /// .set_default();
518 ///
519 /// {
520 /// let span = tracing::info_span!("the span we're testing");
521 /// let _entered = span.enter();
522 /// }
523 ///
524 /// handle.assert_finished();
525 /// ```
526 ///
527 /// An event is recorded before the span is entered, causing the
528 /// test to fail:
529 ///
530 /// ```should_panic
531 /// use tracing_mock::{expect, subscriber};
532 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
533 ///
534 /// let span = expect::span()
535 /// .at_level(tracing::Level::INFO)
536 /// .named("the span we're testing");
537 /// let (subscriber, handle) = subscriber::mock()
538 /// .enter(&span)
539 /// .exit(&span)
540 /// .only()
541 /// .run_with_handle();
542 ///
543 /// let _collect = tracing_subscriber::registry()
544 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
545 /// .set_default();
546 ///
547 /// {
548 /// tracing::info!("an event");
549 /// let span = tracing::info_span!("the span we're testing");
550 /// let _entered = span.enter();
551 /// }
552 ///
553 /// handle.assert_finished();
554 /// ```
555 ///
556 /// [`exit`]: fn@Self::exit
557 /// [`only`]: fn@Self::only
558 pub fn enter<S>(mut self, span: S) -> Self
559 where
560 S: Into<ExpectedSpan>,
561 {
562 self.expected.push_back(Expect::Enter(span.into()));
563 self
564 }
565
566 /// Adds an expectation that exiting a span matching the
567 /// [`ExpectedSpan`] will be recorded next.
568 ///
569 /// As a span may be entered and exited multiple times,
570 /// this is different from the span being closed. In
571 /// general [`enter`] and `exit` should be paired.
572 ///
573 /// If the span that is exited doesn't match the [`ExpectedSpan`],
574 /// or if something else (such as an event) is recorded first,
575 /// then the expectation will fail.
576 ///
577 /// **Note**: Ensure that the guard returned by [`Span::enter`]
578 /// is dropped before calling [`MockHandle::assert_finished`].
579 ///
580 /// # Examples
581 ///
582 /// ```
583 /// use tracing_mock::{expect, subscriber};
584 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
585 ///
586 /// let span = expect::span()
587 /// .at_level(tracing::Level::INFO)
588 /// .named("the span we're testing");
589 /// let (subscriber, handle) = subscriber::mock()
590 /// .enter(&span)
591 /// .exit(&span)
592 /// .only()
593 /// .run_with_handle();
594 ///
595 /// let _collect = tracing_subscriber::registry()
596 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
597 /// .set_default();
598 /// {
599 /// let span = tracing::info_span!("the span we're testing");
600 /// let _entered = span.enter();
601 /// }
602 ///
603 /// handle.assert_finished();
604 /// ```
605 ///
606 /// An event is recorded before the span is exited, causing the
607 /// test to fail:
608 ///
609 /// ```should_panic
610 /// use tracing_mock::{expect, subscriber};
611 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
612 ///
613 /// let span = expect::span()
614 /// .at_level(tracing::Level::INFO)
615 /// .named("the span we're testing");
616 /// let (subscriber, handle) = subscriber::mock()
617 /// .enter(&span)
618 /// .exit(&span)
619 /// .only()
620 /// .run_with_handle();
621 ///
622 /// let _collect = tracing_subscriber::registry()
623 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
624 /// .set_default();
625 ///
626 /// {
627 /// let span = tracing::info_span!("the span we're testing");
628 /// let _entered = span.enter();
629 /// tracing::info!("an event");
630 /// }
631 ///
632 /// handle.assert_finished();
633 /// ```
634 ///
635 /// [`enter`]: fn@Self::enter
636 /// [`MockHandle::assert_finished`]: fn@crate::collector::MockHandle::assert_finished
637 /// [`Span::enter`]: fn@tracing::Span::enter
638 pub fn exit<S>(mut self, span: S) -> Self
639 where
640 S: Into<ExpectedSpan>,
641 {
642 self.expected.push_back(Expect::Exit(span.into()));
643 self
644 }
645
646 /// Expects that no further traces are received.
647 ///
648 /// The call to `only` should appear immediately before the final
649 /// call to [`run`] or [`run_with_handle`], as any expectations which
650 /// are added after `only` will not be considered.
651 ///
652 /// # Examples
653 ///
654 /// Consider this simple test. It passes even though we only
655 /// expect a single event, but receive three:
656 ///
657 /// ```
658 /// use tracing_mock::{expect, subscriber};
659 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
660 ///
661 /// let (subscriber, handle) = subscriber::mock()
662 /// .event(expect::event())
663 /// .run_with_handle();
664 ///
665 /// let _collect = tracing_subscriber::registry()
666 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
667 /// .set_default();
668 ///
669 /// tracing::info!("a");
670 /// tracing::info!("b");
671 /// tracing::info!("c");
672 ///
673 /// handle.assert_finished();
674 /// ```
675 ///
676 /// After including `only`, the test will fail:
677 ///
678 /// ```should_panic
679 /// use tracing_mock::{expect, subscriber};
680 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
681 ///
682 /// let (subscriber, handle) = subscriber::mock()
683 /// .event(expect::event())
684 /// .only()
685 /// .run_with_handle();
686 ///
687 /// let _collect = tracing_subscriber::registry()
688 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
689 /// .set_default();
690 ///
691 /// tracing::info!("a");
692 /// tracing::info!("b");
693 /// tracing::info!("c");
694 ///
695 /// handle.assert_finished();
696 /// ```
697 ///
698 /// [`run`]: fn@Self::run
699 /// [`run_with_handle`]: fn@Self::run_with_handle
700 pub fn only(mut self) -> Self {
701 self.expected.push_back(Expect::Nothing);
702 self
703 }
704
705 /// Consume this builder and return a [`MockSubscriber`] which can
706 /// be set as the default subscriber.
707 ///
708 /// This function is similar to [`run_with_handle`], but it doesn't
709 /// return a [`MockHandle`]. This is useful if the desired
710 /// assertions can be checked externally to the subscriber.
711 ///
712 /// # Examples
713 ///
714 /// The following test is used within the `tracing-subscriber`
715 /// codebase:
716 ///
717 /// ```
718 /// use tracing::Collect;
719 /// use tracing_mock::subscriber;
720 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
721 ///
722 /// let unfiltered = subscriber::named("unfiltered").run().boxed();
723 /// let info = subscriber::named("info")
724 /// .run()
725 /// .with_filter(tracing_core::LevelFilter::INFO)
726 /// .boxed();
727 /// let debug = subscriber::named("debug")
728 /// .run()
729 /// .with_filter(tracing_core::LevelFilter::DEBUG)
730 /// .boxed();
731 ///
732 /// let collector = tracing_subscriber::registry().with(vec![unfiltered, info, debug]);
733 ///
734 /// assert_eq!(collector.max_level_hint(), None);
735 /// ```
736 ///
737 /// [`MockHandle`]: struct@crate::collector::MockHandle
738 /// [`run_with_handle`]: fn@Self::run_with_handle
739 pub fn run(self) -> MockSubscriber {
740 MockSubscriber {
741 expected: Arc::new(Mutex::new(self.expected)),
742 name: self.name,
743 current: Mutex::new(Vec::new()),
744 }
745 }
746
747 /// Consume this builder and return a [`MockSubscriber`] which can
748 /// be set as the default subscriber and a [`MockHandle`] which can
749 /// be used to validate the provided expectations.
750 ///
751 /// # Examples
752 ///
753 /// ```
754 /// use tracing_mock::{expect, subscriber};
755 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
756 ///
757 /// let (subscriber, handle) = subscriber::mock()
758 /// .event(expect::event())
759 /// .run_with_handle();
760 ///
761 /// let _collect = tracing_subscriber::registry()
762 /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
763 /// .set_default();
764 ///
765 /// tracing::info!("event");
766 ///
767 /// handle.assert_finished();
768 /// ```
769 ///
770 /// [`MockHandle`]: struct@crate::collector::MockHandle
771 /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
772 pub fn run_with_handle(self) -> (MockSubscriber, MockHandle) {
773 let expected = Arc::new(Mutex::new(self.expected));
774 let handle = MockHandle::new(expected.clone(), self.name.clone());
775 let subscriber = MockSubscriber {
776 expected,
777 name: self.name,
778 current: Mutex::new(Vec::new()),
779 };
780 (subscriber, handle)
781 }
782}
783
784impl<'a, S> From<&SpanRef<'a, S>> for ActualSpan
785where
786 S: LookupSpan<'a>,
787{
788 fn from(span_ref: &SpanRef<'a, S>) -> Self {
789 Self::new(span_ref.id(), Some(span_ref.metadata()))
790 }
791}
792
793impl MockSubscriber {
794 fn check_event_scope<C>(
795 &self,
796 current_scope: Option<tracing_subscriber::registry::Scope<'_, C>>,
797 expected_scope: &mut [ExpectedSpan],
798 ) where
799 C: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
800 {
801 let mut current_scope = current_scope.into_iter().flatten();
802 let mut i = 0;
803 for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
804 println!(
805 "[{}] event_scope[{}] actual={} ({:?}); expected={}",
806 self.name,
807 i,
808 actual.name(),
809 actual.id(),
810 expected
811 );
812 expected.check(
813 &(&actual).into(),
814 format_args!("the {}th span in the event's scope to be", i),
815 &self.name,
816 );
817 i += 1;
818 }
819 let remaining_expected = &expected_scope[i..];
820 assert!(
821 remaining_expected.is_empty(),
822 "\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
823 self.name,
824 self.name,
825 remaining_expected,
826 );
827 assert!(
828 current_scope.next().is_none(),
829 "\n[{}] did not expect all spans in the actual event scope!",
830 self.name,
831 );
832 }
833}
834
835impl<C> Subscribe<C> for MockSubscriber
836where
837 C: Collect + for<'a> LookupSpan<'a>,
838{
839 fn register_callsite(
840 &self,
841 metadata: &'static tracing::Metadata<'static>,
842 ) -> tracing_core::Interest {
843 println!("[{}] register_callsite {:#?}", self.name, metadata);
844 tracing_core::Interest::always()
845 }
846
847 fn on_record(&self, _: &Id, _: &Record<'_>, _: Context<'_, C>) {
848 unimplemented!(
849 "so far, we don't have any tests that need an `on_record` \
850 implementation.\nif you just wrote one that does, feel free to \
851 implement it!"
852 );
853 }
854
855 fn on_event(&self, event: &Event<'_>, cx: Context<'_, C>) {
856 let name = event.metadata().name();
857 println!(
858 "[{}] event: {}; level: {}; target: {}",
859 self.name,
860 name,
861 event.metadata().level(),
862 event.metadata().target(),
863 );
864 match self.expected.lock().unwrap().pop_front() {
865 None => {}
866 Some(Expect::Event(mut expected)) => {
867 expected.check(event, || context_get_ancestry(event, &cx), &self.name);
868
869 if let Some(expected_scope) = expected.scope_mut() {
870 self.check_event_scope(cx.event_scope(event), expected_scope);
871 }
872 }
873 Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)),
874 }
875 }
876
877 fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, C>) {
878 unimplemented!(
879 "so far, we don't have any tests that need an `on_follows_from` \
880 implementation.\nif you just wrote one that does, feel free to \
881 implement it!"
882 );
883 }
884
885 fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, C>) {
886 let meta = span.metadata();
887 println!(
888 "[{}] new_span: name={:?}; target={:?}; id={:?};",
889 self.name,
890 meta.name(),
891 meta.target(),
892 id
893 );
894 let mut expected = self.expected.lock().unwrap();
895 let was_expected = matches!(expected.front(), Some(Expect::NewSpan(_)));
896 if was_expected {
897 if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() {
898 expected.check(span, || context_get_ancestry(span, &cx), &self.name);
899 }
900 }
901 }
902
903 fn on_enter(&self, id: &Id, cx: Context<'_, C>) {
904 let span = cx
905 .span(id)
906 .unwrap_or_else(|| panic!("[{}] no span for ID {:?}", self.name, id));
907 println!("[{}] enter: {}; id={:?};", self.name, span.name(), id);
908 match self.expected.lock().unwrap().pop_front() {
909 None => {}
910 Some(Expect::Enter(ref expected_span)) => {
911 expected_span.check(&(&span).into(), "to enter", &self.name);
912 }
913 Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name())),
914 }
915 self.current.lock().unwrap().push(id.clone());
916 }
917
918 fn on_exit(&self, id: &Id, cx: Context<'_, C>) {
919 if std::thread::panicking() {
920 // `exit()` can be called in `drop` impls, so we must guard against
921 // double panics.
922 println!("[{}] exit {:?} while panicking", self.name, id);
923 return;
924 }
925 let span = cx
926 .span(id)
927 .unwrap_or_else(|| panic!("[{}] no span for ID {:?}", self.name, id));
928 println!("[{}] exit: {}; id={:?};", self.name, span.name(), id);
929 match self.expected.lock().unwrap().pop_front() {
930 None => {}
931 Some(Expect::Exit(ref expected_span)) => {
932 expected_span.check(&(&span).into(), "to exit", &self.name);
933 let curr = self.current.lock().unwrap().pop();
934 assert_eq!(
935 Some(id),
936 curr.as_ref(),
937 "[{}] exited span {:?}, but the current span was {:?}",
938 self.name,
939 span.name(),
940 curr.as_ref().and_then(|id| cx.span(id)).map(|s| s.name())
941 );
942 }
943 Some(ex) => ex.bad(&self.name, format_args!("exited span {:?}", span.name())),
944 };
945 }
946
947 fn on_close(&self, id: Id, cx: Context<'_, C>) {
948 if std::thread::panicking() {
949 // `try_close` can be called in `drop` impls, so we must guard against
950 // double panics.
951 println!("[{}] close {:?} while panicking", self.name, id);
952 return;
953 }
954 let span = cx.span(&id);
955 let name = span.as_ref().map(|span| {
956 println!("[{}] close_span: {}; id={:?};", self.name, span.name(), id,);
957 span.name()
958 });
959 if name.is_none() {
960 println!("[{}] drop_span: id={:?}", self.name, id);
961 }
962 if let Ok(mut expected) = self.expected.try_lock() {
963 let was_expected = match expected.front() {
964 Some(Expect::DropSpan(ref expected_span)) => {
965 // Don't assert if this function was called while panicking,
966 // as failing the assertion can cause a double panic.
967 if !::std::thread::panicking() {
968 if let Some(ref span) = span {
969 expected_span.check(&span.into(), "to close a span", &self.name);
970 }
971 }
972 true
973 }
974 Some(Expect::Event(_)) => {
975 if !::std::thread::panicking() {
976 panic!(
977 "[{}] expected an event, but dropped span {} (id={:?}) instead",
978 self.name,
979 name.unwrap_or("<unknown name>"),
980 id
981 );
982 }
983 true
984 }
985 _ => false,
986 };
987 if was_expected {
988 expected.pop_front();
989 }
990 }
991 }
992
993 fn on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, C>) {
994 panic!("well-behaved subscribers should never do this to us, lol");
995 }
996}
997
998fn context_get_ancestry<C>(item: impl HasAncestry, ctx: &Context<'_, C>) -> ActualAncestry
999where
1000 C: Collect + for<'a> LookupSpan<'a>,
1001{
1002 get_ancestry(
1003 item,
1004 || ctx.lookup_current().map(|s| s.id()),
1005 |span_id| ctx.span(span_id).map(|span| (&span).into()),
1006 )
1007}
1008
1009impl fmt::Debug for MockSubscriber {
1010 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1011 let mut s = f.debug_struct("ExpectSubscriber");
1012 s.field("name", &self.name);
1013
1014 if let Ok(expected) = self.expected.try_lock() {
1015 s.field("expected", &expected);
1016 } else {
1017 s.field("expected", &format_args!("<locked>"));
1018 }
1019
1020 if let Ok(current) = self.current.try_lock() {
1021 s.field("current", &format_args!("{:?}", ¤t));
1022 } else {
1023 s.field("current", &format_args!("<locked>"));
1024 }
1025
1026 s.finish()
1027 }
1028}