🛈 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/
event.rs

1//! An [`ExpectedEvent`] defines an event to be matched by the mock
2//! collector API in the [`collector`] module.
3//!
4//! The expected event should be created with [`expect::event`] and a
5//! chain of method calls to describe the assertions we wish to make
6//! about the event.
7//!
8//! # Examples
9//!
10//! ```
11//! use tracing::collect::with_default;
12//! use tracing_mock::{collector, expect};
13//!
14//! let event = expect::event()
15//!     .at_level(tracing::Level::INFO)
16//!     .with_fields(expect::field("field.name").with_value(&"field_value"));
17//!
18//! let (collector, handle) = collector::mock()
19//!     .event(event)
20//!     .run_with_handle();
21//!
22//! with_default(collector, || {
23//!     tracing::info!(field.name = "field_value");
24//! });
25//!
26//! handle.assert_finished();
27//! ```
28//!
29//! [`collector`]: mod@crate::collector
30//! [`expect::event`]: fn@crate::expect::event
31use std::fmt;
32
33use crate::{
34    ancestry::{ActualAncestry, ExpectedAncestry},
35    field,
36    metadata::ExpectedMetadata,
37    span,
38};
39
40/// An expected event.
41///
42/// For a detailed description and examples, see the documentation for
43/// the methods and the [`event`] module.
44///
45/// [`event`]: mod@crate::event
46#[derive(Default, Eq, PartialEq)]
47pub struct ExpectedEvent {
48    pub(super) fields: Option<field::ExpectedFields>,
49    pub(super) ancestry: Option<ExpectedAncestry>,
50    pub(super) in_spans: Option<Vec<span::ExpectedSpan>>,
51    pub(super) metadata: ExpectedMetadata,
52}
53
54impl ExpectedEvent {
55    /// Sets a name to expect when matching an event.
56    ///
57    /// By default, an event's name takes takes the form:
58    /// `event <file>:<line>` where `<file>` and `<line>` refer to the
59    /// location in the source code where the event was generated.
60    ///
61    /// To override the name of an event, it has to be constructed
62    /// directly, rather than by using the `tracing` crate's macros.
63    ///
64    /// In general, there are not many use cases for expecting an
65    /// event with a particular name, as the value includes the file
66    /// name and line number. Assertions about event names are
67    /// therefore quite fragile, since they will change as the source
68    /// code is modified.
69    pub fn named<I>(self, name: I) -> Self
70    where
71        I: Into<String>,
72    {
73        Self {
74            metadata: ExpectedMetadata {
75                name: Some(name.into()),
76                ..self.metadata
77            },
78            ..self
79        }
80    }
81
82    /// Adds fields to expect when matching an event.
83    ///
84    /// If an event is recorded with fields that do not match the provided
85    /// [`ExpectedFields`], this expectation will fail.
86    ///
87    /// If the provided field is not present on the recorded event, or
88    /// if the value for that field is different, then the expectation
89    /// will fail.
90    ///
91    /// More information on the available validations is available in
92    /// the [`ExpectedFields`] documentation.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use tracing::collect::with_default;
98    /// use tracing_mock::{collector, expect};
99    ///
100    /// let event = expect::event()
101    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
102    ///
103    /// let (collector, handle) = collector::mock()
104    ///     .event(event)
105    ///     .run_with_handle();
106    ///
107    /// with_default(collector, || {
108    ///     tracing::info!(field.name = "field_value");
109    /// });
110    ///
111    /// handle.assert_finished();
112    /// ```
113    ///
114    /// A different field value will cause the expectation to fail:
115    ///
116    /// ```should_panic
117    /// use tracing::collect::with_default;
118    /// use tracing_mock::{collector, expect};
119    ///
120    /// let event = expect::event()
121    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
122    ///
123    /// let (collector, handle) = collector::mock()
124    ///     .event(event)
125    ///     .run_with_handle();
126    ///
127    /// with_default(collector, || {
128    ///     tracing::info!(field.name = "different_field_value");
129    /// });
130    ///
131    /// handle.assert_finished();
132    /// ```
133    ///
134    /// [`ExpectedFields`]: struct@crate::field::ExpectedFields
135    pub fn with_fields<I>(self, fields: I) -> Self
136    where
137        I: Into<field::ExpectedFields>,
138    {
139        Self {
140            fields: Some(fields.into()),
141            ..self
142        }
143    }
144
145    /// Sets the [`Level`](tracing::Level) to expect when matching an event.
146    ///
147    /// If an event is recorded at a different level, this expectation
148    /// will fail.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use tracing::collect::with_default;
154    /// use tracing_mock::{collector, expect};
155    ///
156    /// let event = expect::event()
157    ///     .at_level(tracing::Level::WARN);
158    ///
159    /// let (collector, handle) = collector::mock()
160    ///     .event(event)
161    ///     .run_with_handle();
162    ///
163    /// with_default(collector, || {
164    ///     tracing::warn!("this message is bad news");
165    /// });
166    ///
167    /// handle.assert_finished();
168    /// ```
169    ///
170    /// Expecting an event at `INFO` level will fail if the event is
171    /// recorded at any other level:
172    ///
173    /// ```should_panic
174    /// use tracing::collect::with_default;
175    /// use tracing_mock::{collector, expect};
176    ///
177    /// let event = expect::event()
178    ///     .at_level(tracing::Level::INFO);
179    ///
180    /// let (collector, handle) = collector::mock()
181    ///     .event(event)
182    ///     .run_with_handle();
183    ///
184    /// with_default(collector, || {
185    ///     tracing::warn!("this message is bad news");
186    /// });
187    ///
188    /// handle.assert_finished();
189    /// ```
190    pub fn at_level(self, level: tracing::Level) -> Self {
191        Self {
192            metadata: ExpectedMetadata {
193                level: Some(level),
194                ..self.metadata
195            },
196            ..self
197        }
198    }
199
200    /// Sets the target to expect when matching events.
201    ///
202    /// If an event is recorded with a different target, this expectation will fail.
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use tracing::collect::with_default;
208    /// use tracing_mock::{collector, expect};
209    ///
210    /// let event = expect::event()
211    ///     .with_target("some_target");
212    ///
213    /// let (collector, handle) = collector::mock()
214    ///     .event(event)
215    ///     .run_with_handle();
216    ///
217    /// with_default(collector, || {
218    ///     tracing::info!(target: "some_target", field = &"value");
219    /// });
220    ///
221    /// handle.assert_finished();
222    /// ```
223    ///
224    /// The test will fail if the target is different:
225    ///
226    /// ```should_panic
227    /// use tracing::collect::with_default;
228    /// use tracing_mock::{collector, expect};
229    ///
230    /// let event = expect::event()
231    ///     .with_target("some_target");
232    ///
233    /// let (collector, handle) = collector::mock()
234    ///     .event(event)
235    ///     .run_with_handle();
236    ///
237    /// with_default(collector, || {
238    ///     tracing::info!(target: "a_different_target", field = &"value");
239    /// });
240    ///
241    /// handle.assert_finished();
242    /// ```
243    pub fn with_target<I>(self, target: I) -> Self
244    where
245        I: Into<String>,
246    {
247        Self {
248            metadata: ExpectedMetadata {
249                target: Some(target.into()),
250                ..self.metadata
251            },
252            ..self
253        }
254    }
255
256    /// Configures this `ExpectedEvent` to expect the specified
257    /// [`ExpectedAncestry`]. An event's ancestry indicates whether is has a
258    /// parent or is a root, and whether the parent is explicitly or
259    /// contextually assigned.
260    ///
261    /// An _explicit_ parent span is one passed to the `event!` macro in the
262    /// `parent:` field. If no `parent:` field is specified, then the event
263    /// will have a contextually determined parent or be a contextual root if
264    /// there is no parent.
265    ///
266    /// If the parent is different from the provided one, this expectation
267    /// will fail.
268    ///
269    /// # Examples
270    ///
271    /// An explicit or contextual can be matched on an `ExpectedSpan`.
272    ///
273    /// ```
274    /// use tracing::collect::with_default;
275    /// use tracing_mock::{collector, expect};
276    ///
277    /// let parent = expect::span()
278    ///     .named("parent_span")
279    ///     .with_target("custom-target")
280    ///     .at_level(tracing::Level::INFO);
281    /// let event = expect::event()
282    ///     .with_ancestry(expect::has_explicit_parent(parent));
283    ///
284    /// let (collector, handle) = collector::mock()
285    ///     .event(event)
286    ///     .run_with_handle();
287    ///
288    /// with_default(collector, || {
289    ///     let parent = tracing::info_span!(target: "custom-target", "parent_span");
290    ///     tracing::info!(parent: parent.id(), field = &"value");
291    /// });
292    ///
293    /// handle.assert_finished();
294    /// ```
295    /// The functions `expect::has_explicit_parent` and
296    /// `expect::has_contextual_parent` take `Into<ExpectedSpan>`, so a string
297    /// passed directly will match on a span with that name, or an
298    /// [`ExpectedId`] can be passed to match a span with that Id.
299    ///
300    /// ```
301    /// use tracing::collect::with_default;
302    /// use tracing_mock::{collector, expect};
303    ///
304    /// let event = expect::event()
305    ///     .with_ancestry(expect::has_explicit_parent("parent_span"));
306    ///
307    /// let (collector, handle) = collector::mock()
308    ///     .event(event)
309    ///     .run_with_handle();
310    ///
311    /// with_default(collector, || {
312    ///     let parent = tracing::info_span!("parent_span");
313    ///     tracing::info!(parent: parent.id(), field = &"value");
314    /// });
315    ///
316    /// handle.assert_finished();
317    /// ```
318    ///
319    /// In the following example, we expect that the matched event is
320    /// an explicit root:
321    ///
322    /// ```
323    /// use tracing::collect::with_default;
324    /// use tracing_mock::{collector, expect};
325    ///
326    /// let event = expect::event()
327    ///     .with_ancestry(expect::is_explicit_root());
328    ///
329    /// let (collector, handle) = collector::mock()
330    ///     .enter(expect::span())
331    ///     .event(event)
332    ///     .run_with_handle();
333    ///
334    /// with_default(collector, || {
335    ///     let _guard = tracing::info_span!("contextual parent").entered();
336    ///     tracing::info!(parent: None, field = &"value");
337    /// });
338    ///
339    /// handle.assert_finished();
340    /// ```
341    ///
342    /// When `expect::has_contextual_parent("parent_name")` is passed to
343    /// `with_ancestry` then the provided string is the name of the contextual
344    /// parent span to expect.
345    ///
346    /// ```
347    /// use tracing::collect::with_default;
348    /// use tracing_mock::{collector, expect};
349    ///
350    /// let event = expect::event()
351    ///     .with_ancestry(expect::has_contextual_parent("parent_span"));
352    ///
353    /// let (collector, handle) = collector::mock()
354    ///     .enter(expect::span())
355    ///     .event(event)
356    ///     .run_with_handle();
357    ///
358    /// with_default(collector, || {
359    ///     let parent = tracing::info_span!("parent_span");
360    ///     let _guard = parent.enter();
361    ///     tracing::info!(field = &"value");
362    /// });
363    ///
364    /// handle.assert_finished();
365    /// ```
366    ///
367    /// Matching an event recorded outside of a span, a contextual
368    /// root:
369    ///
370    /// ```
371    /// use tracing::collect::with_default;
372    /// use tracing_mock::{collector, expect};
373    ///
374    /// let event = expect::event()
375    ///     .with_ancestry(expect::is_contextual_root());
376    ///
377    /// let (collector, handle) = collector::mock()
378    ///     .event(event)
379    ///     .run_with_handle();
380    ///
381    /// with_default(collector, || {
382    ///     tracing::info!(field = &"value");
383    /// });
384    ///
385    /// handle.assert_finished();
386    /// ```
387    ///
388    /// In the example below, the expectation fails because the event is
389    /// recorded with an explicit parent, however a contextual parent is
390    /// expected.
391    ///
392    /// ```should_panic
393    /// use tracing::collect::with_default;
394    /// use tracing_mock::{collector, expect};
395    ///
396    /// let event = expect::event()
397    ///     .with_ancestry(expect::has_contextual_parent("parent_span"));
398    ///
399    /// let (collector, handle) = collector::mock()
400    ///     .enter(expect::span())
401    ///     .event(event)
402    ///     .run_with_handle();
403    ///
404    /// with_default(collector, || {
405    ///     let parent = tracing::info_span!("parent_span");
406    ///     tracing::info!(parent: parent.id(), field = &"value");
407    /// });
408    ///
409    /// handle.assert_finished();
410    /// ```
411    ///
412    /// [`ExpectedId`]: struct@crate::span::ExpectedId
413    pub fn with_ancestry(self, ancenstry: ExpectedAncestry) -> ExpectedEvent {
414        Self {
415            ancestry: Some(ancenstry),
416            ..self
417        }
418    }
419
420    /// Validates that the event is emitted within the scope of the
421    /// provided `spans`.
422    ///
423    /// The spans must be provided reverse hierarchy order, so the
424    /// closest span to the event would be first, followed by its
425    /// parent, and so on.
426    ///
427    /// If the spans provided do not match the hierarchy of the
428    /// recorded event, the expectation will fail.
429    ///
430    /// **Note**: This validation currently only works with a
431    /// [`MockSubscriber`]. If used with a [`MockCollector`], the
432    /// expectation will fail directly as it is unimplemented.
433    ///
434    /// # Examples
435    ///
436    /// ```
437    /// use tracing_mock::{expect, subscriber};
438    /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
439    ///
440    /// let event = expect::event().in_scope([
441    ///     expect::span().named("parent_span"),
442    ///     expect::span().named("grandparent_span")
443    /// ]);
444    ///
445    /// let (subscriber, handle) = subscriber::mock()
446    ///     .enter(expect::span())
447    ///     .enter(expect::span())
448    ///     .event(event)
449    ///     .run_with_handle();
450    ///
451    /// let _collect = tracing_subscriber::registry()
452    ///     .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
453    ///     .set_default();
454    ///
455    /// let grandparent = tracing::info_span!("grandparent_span");
456    /// let _gp_guard = grandparent.enter();
457    /// let parent = tracing::info_span!("parent_span");
458    /// let _p_guard = parent.enter();
459    /// tracing::info!(field = &"value");    
460    ///
461    /// handle.assert_finished();
462    /// ```
463    ///
464    /// The scope must match exactly, otherwise the expectation will fail:
465    ///
466    /// ```should_panic
467    /// use tracing_mock::{expect, subscriber};
468    /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
469    ///
470    /// let event = expect::event().in_scope([
471    ///     expect::span().named("parent_span"),
472    ///     expect::span().named("grandparent_span")
473    /// ]);
474    ///
475    /// let (subscriber, handle) = subscriber::mock()
476    ///     .enter(expect::span())
477    ///     .event(event)
478    ///     .run_with_handle();
479    ///
480    /// let _collect = tracing_subscriber::registry()
481    ///     .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
482    ///     .set_default();
483    ///
484    /// let parent = tracing::info_span!("parent_span");
485    /// let _p_guard = parent.enter();
486    /// tracing::info!(field = &"value");
487    ///
488    /// handle.assert_finished();
489    /// ```
490    ///
491    /// It is also possible to test that an event has no parent spans
492    /// by passing `None` to `in_scope`. If the event is within a
493    /// span, the test will fail:
494    ///
495    /// ```should_panic
496    /// use tracing_mock::{expect, subscriber};
497    /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
498    ///
499    /// let event = expect::event().in_scope(None);
500    ///
501    /// let (subscriber, handle) = subscriber::mock()
502    ///     .enter(expect::span())
503    ///     .event(event)
504    ///     .run_with_handle();
505    ///
506    /// let _collect = tracing_subscriber::registry()
507    ///     .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
508    ///     .set_default();
509    ///
510    /// let parent = tracing::info_span!("parent_span");
511    /// let _guard = parent.enter();
512    /// tracing::info!(field = &"value");    
513    ///
514    /// handle.assert_finished();
515    /// ```
516    ///
517    /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
518    /// [`MockCollector`]: struct@crate::collector::MockCollector
519    #[cfg(feature = "tracing-subscriber")]
520    pub fn in_scope(self, spans: impl IntoIterator<Item = span::ExpectedSpan>) -> Self {
521        Self {
522            in_spans: Some(spans.into_iter().collect()),
523            ..self
524        }
525    }
526
527    /// Provides access to the expected scope (spans) for this expected
528    /// event.
529    #[cfg(feature = "tracing-subscriber")]
530    pub(crate) fn scope_mut(&mut self) -> Option<&mut [span::ExpectedSpan]> {
531        self.in_spans.as_mut().map(|s| &mut s[..])
532    }
533
534    pub(crate) fn check(
535        &mut self,
536        event: &tracing::Event<'_>,
537        get_ancestry: impl FnOnce() -> ActualAncestry,
538        collector_name: &str,
539    ) {
540        let meta = event.metadata();
541        let name = meta.name();
542        self.metadata
543            .check(meta, format_args!("event \"{}\"", name), collector_name);
544        assert!(
545            meta.is_event(),
546            "[{}] expected {}, but got {:?}",
547            collector_name,
548            self,
549            event
550        );
551        if let Some(ref mut expected_fields) = self.fields {
552            let mut checker = expected_fields.checker(name, collector_name);
553            event.record(&mut checker);
554            checker.finish();
555        }
556
557        if let Some(ref expected_ancestry) = self.ancestry {
558            let actual_ancestry = get_ancestry();
559            expected_ancestry.check(&actual_ancestry, event.metadata().name(), collector_name);
560        }
561    }
562}
563
564impl fmt::Display for ExpectedEvent {
565    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566        write!(f, "an event{}", self.metadata)
567    }
568}
569
570impl fmt::Debug for ExpectedEvent {
571    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
572        let mut s = f.debug_struct("MockEvent");
573
574        if let Some(ref name) = self.metadata.name {
575            s.field("name", name);
576        }
577
578        if let Some(ref target) = self.metadata.target {
579            s.field("target", target);
580        }
581
582        if let Some(ref level) = self.metadata.level {
583            s.field("level", &format_args!("{:?}", level));
584        }
585
586        if let Some(ref fields) = self.fields {
587            s.field("fields", fields);
588        }
589
590        if let Some(ref parent) = self.ancestry {
591            s.field("parent", &format_args!("{:?}", parent));
592        }
593
594        if let Some(in_spans) = &self.in_spans {
595            s.field("in_spans", in_spans);
596        }
597
598        s.finish()
599    }
600}