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}