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

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

tracing_mock/
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!("{:?}", &current));
1022        } else {
1023            s.field("current", &format_args!("<locked>"));
1024        }
1025
1026        s.finish()
1027    }
1028}