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

1//! Define expectations to validate fields on events and spans.
2//!
3//! The [`ExpectedField`] struct define expected values for fields in
4//! order to match events and spans via the mock collector API in the
5//! [`collector`] module.
6//!
7//! Expected fields should be created with [`expect::field`] and a
8//! chain of method calls to specify the field value and additional
9//! fields as necessary.
10//!
11//! # Examples
12//!
13//! The simplest case is to expect that an event has a field with a
14//! specific name, without any expectation about the value:
15//!
16//! ```
17//! use tracing_mock::{collector, expect};
18//!
19//! let event = expect::event()
20//!     .with_fields(expect::field("field_name"));
21//!
22//! let (collector, handle) = collector::mock()
23//!     .event(event)
24//!     .run_with_handle();
25//!
26//! tracing::collect::with_default(collector, || {
27//!     tracing::info!(field_name = "value");
28//! });
29//!
30//! handle.assert_finished();
31//! ```
32//!
33//! It is possible to expect multiple fields and specify the value for
34//! each of them:
35//!
36//! ```
37//! use tracing_mock::{collector, expect};
38//!
39//! let event = expect::event().with_fields(
40//!     expect::field("string_field")
41//!         .with_value(&"field_value")
42//!         .and(expect::field("integer_field").with_value(&54_i64))
43//!         .and(expect::field("bool_field").with_value(&true)),
44//! );
45//!
46//! let (collector, handle) = collector::mock()
47//!     .event(event)
48//!     .run_with_handle();
49//!
50//! tracing::collect::with_default(collector, || {
51//!     tracing::info!(
52//!         string_field = "field_value",
53//!         integer_field = 54_i64,
54//!         bool_field = true,
55//!     );
56//! });
57//!
58//! handle.assert_finished();
59//! ```
60//!
61//! If an expected field is not present, or if the value of the field
62//! is different, the test will fail. In this example, the value is
63//! different:
64//!
65//! ```should_panic
66//! use tracing_mock::{collector, expect};
67//!
68//! let event = expect::event()
69//!     .with_fields(expect::field("field_name").with_value(&"value"));
70//!
71//! let (collector, handle) = collector::mock()
72//!     .event(event)
73//!     .run_with_handle();
74//!
75//! tracing::collect::with_default(collector, || {
76//!     tracing::info!(field_name = "different value");
77//! });
78//!
79//! handle.assert_finished();
80//! ```
81//!
82//! [`collector`]: mod@crate::collector
83//! [`expect::field`]: fn@crate::expect::field
84use std::{collections::HashMap, fmt};
85
86use tracing::{
87    callsite,
88    callsite::Callsite,
89    field::{self, Field, Value, Visit},
90    metadata::Kind,
91};
92
93/// An expectation for multiple fields.
94///
95/// For a detailed description and examples, see the documentation for
96/// the methods and the [`field`] module.
97///
98/// [`field`]: mod@crate::field
99#[derive(Default, Debug, Eq, PartialEq)]
100pub struct ExpectedFields {
101    fields: HashMap<String, ExpectedValue>,
102    only: bool,
103}
104
105/// An expected field.
106///
107/// For a detailed description and examples, see the documentation for
108/// the methods and the [`field`] module.
109///
110/// [`field`]: mod@crate::field
111#[derive(Debug)]
112pub struct ExpectedField {
113    pub(super) name: String,
114    pub(super) value: ExpectedValue,
115}
116
117#[derive(Debug)]
118pub(crate) enum ExpectedValue {
119    F64(f64),
120    I64(i64),
121    U64(u64),
122    Bool(bool),
123    Str(String),
124    Debug(String),
125    Any,
126}
127
128impl Eq for ExpectedValue {}
129
130impl PartialEq for ExpectedValue {
131    fn eq(&self, other: &Self) -> bool {
132        use ExpectedValue::*;
133
134        match (self, other) {
135            (F64(a), F64(b)) => {
136                debug_assert!(!a.is_nan());
137                debug_assert!(!b.is_nan());
138
139                a.eq(b)
140            }
141            (I64(a), I64(b)) => a.eq(b),
142            (U64(a), U64(b)) => a.eq(b),
143            (Bool(a), Bool(b)) => a.eq(b),
144            (Str(a), Str(b)) => a.eq(b),
145            (Debug(a), Debug(b)) => a.eq(b),
146            (Any, _) => true,
147            (_, Any) => true,
148            _ => false,
149        }
150    }
151}
152
153impl ExpectedField {
154    /// Sets the value to expect when matching this field.
155    ///
156    /// If the recorded value for this field is different, the
157    /// expectation will fail.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use tracing_mock::{collector, expect};
163    ///
164    /// let event = expect::event()
165    ///     .with_fields(expect::field("field_name").with_value(&"value"));
166    ///
167    /// let (collector, handle) = collector::mock()
168    ///     .event(event)
169    ///     .run_with_handle();
170    ///
171    /// tracing::collect::with_default(collector, || {
172    ///     tracing::info!(field_name = "value");
173    /// });
174    ///
175    /// handle.assert_finished();
176    /// ```
177    ///
178    /// A different value will cause the test to fail:
179    ///
180    /// ```should_panic
181    /// use tracing_mock::{collector, expect};
182    ///
183    /// let event = expect::event()
184    ///     .with_fields(expect::field("field_name").with_value(&"value"));
185    ///
186    /// let (collector, handle) = collector::mock()
187    ///     .event(event)
188    ///     .run_with_handle();
189    ///
190    /// tracing::collect::with_default(collector, || {
191    ///     tracing::info!(field_name = "different value");
192    /// });
193    ///
194    /// handle.assert_finished();
195    /// ```
196    pub fn with_value(self, value: &dyn Value) -> Self {
197        Self {
198            value: ExpectedValue::from(value),
199            ..self
200        }
201    }
202
203    /// Adds an additional [`ExpectedField`] to be matched.
204    ///
205    /// Both fields must match, if either of them are not present, or
206    /// if the value for either field is different, the expectation
207    /// will fail.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use tracing_mock::{collector, expect};
213    ///
214    /// let event = expect::event().with_fields(
215    ///     expect::field("field")
216    ///         .with_value(&"value")
217    ///         .and(expect::field("another_field").with_value(&42)),
218    /// );
219    ///
220    /// let (collector, handle) = collector::mock()
221    ///     .event(event)
222    ///     .run_with_handle();
223    ///
224    /// tracing::collect::with_default(collector, || {
225    ///     tracing::info!(
226    ///         field = "value",
227    ///         another_field = 42,
228    ///     );
229    /// });
230    ///
231    /// handle.assert_finished();
232    /// ```
233    ///
234    /// If the second field is not present, the test will fail:
235    ///
236    /// ```should_panic
237    /// use tracing_mock::{collector, expect};
238    ///
239    /// let event = expect::event().with_fields(
240    ///     expect::field("field")
241    ///         .with_value(&"value")
242    ///         .and(expect::field("another_field").with_value(&42)),
243    /// );
244    ///
245    /// let (collector, handle) = collector::mock()
246    ///     .event(event)
247    ///     .run_with_handle();
248    ///
249    /// tracing::collect::with_default(collector, || {
250    ///     tracing::info!(field = "value");
251    /// });
252    ///
253    /// handle.assert_finished();
254    /// ```
255    pub fn and(self, other: ExpectedField) -> ExpectedFields {
256        ExpectedFields {
257            fields: HashMap::new(),
258            only: false,
259        }
260        .and(self)
261        .and(other)
262    }
263
264    /// Indicates that no fields other than those specified should be
265    /// expected.
266    ///
267    /// If additional fields are present on the recorded event or span,
268    /// the expectation will fail.
269    ///
270    /// # Examples
271    ///
272    /// The following test passes despite the recorded event having
273    /// fields that were not expected because `only` was not
274    /// used:
275    ///
276    /// ```
277    /// use tracing_mock::{collector, expect};
278    ///
279    /// let event = expect::event()
280    ///     .with_fields(expect::field("field").with_value(&"value"));
281    ///
282    /// let (collector, handle) = collector::mock().event(event).run_with_handle();
283    ///
284    /// tracing::collect::with_default(collector, || {
285    ///     tracing::info!(field = "value", another_field = 42,);
286    /// });
287    ///
288    /// handle.assert_finished();
289    /// ```
290    ///
291    /// If we include `only` on the `ExpectedField` then the test
292    /// will fail:
293    ///
294    /// ```should_panic
295    /// use tracing_mock::{collector, expect};
296    ///
297    /// let event = expect::event()
298    ///     .with_fields(expect::field("field").with_value(&"value").only());
299    ///
300    /// let (collector, handle) = collector::mock().event(event).run_with_handle();
301    ///
302    /// tracing::collect::with_default(collector, || {
303    ///     tracing::info!(field = "value", another_field = 42,);
304    /// });
305    ///
306    /// handle.assert_finished();
307    /// ```
308    pub fn only(self) -> ExpectedFields {
309        ExpectedFields {
310            fields: HashMap::new(),
311            only: true,
312        }
313        .and(self)
314    }
315}
316
317impl From<ExpectedField> for ExpectedFields {
318    fn from(field: ExpectedField) -> Self {
319        ExpectedFields {
320            fields: HashMap::new(),
321            only: false,
322        }
323        .and(field)
324    }
325}
326
327impl ExpectedFields {
328    /// Adds an additional [`ExpectedField`] to be matched.
329    ///
330    /// All fields must match, if any of them are not present, or if
331    /// the value for any field is different, the expectation will
332    /// fail.
333    ///
334    /// This method performs the same function as
335    /// [`ExpectedField::and`], but applies in the case where there are
336    /// already multiple fields expected.
337    ///
338    /// # Examples
339    ///
340    /// ```
341    /// use tracing_mock::{collector, expect};
342    ///
343    /// let event = expect::event().with_fields(
344    ///     expect::field("field")
345    ///         .with_value(&"value")
346    ///         .and(expect::field("another_field").with_value(&42))
347    ///         .and(expect::field("a_third_field").with_value(&true)),
348    /// );
349    ///
350    /// let (collector, handle) = collector::mock()
351    ///     .event(event)
352    ///     .run_with_handle();
353    ///
354    /// tracing::collect::with_default(collector, || {
355    ///     tracing::info!(
356    ///         field = "value",
357    ///         another_field = 42,
358    ///         a_third_field = true,
359    ///     );
360    /// });
361    ///
362    /// handle.assert_finished();
363    /// ```
364    ///
365    /// If any of the expected fields are not present on the recorded
366    /// event, the test will fail:
367    ///
368    /// ```should_panic
369    /// use tracing_mock::{collector, expect};
370    ///
371    /// let event = expect::event().with_fields(
372    ///     expect::field("field")
373    ///         .with_value(&"value")
374    ///         .and(expect::field("another_field").with_value(&42))
375    ///         .and(expect::field("a_third_field").with_value(&true)),
376    /// );
377    ///
378    /// let (collector, handle) = collector::mock()
379    ///     .event(event)
380    ///     .run_with_handle();
381    ///
382    /// tracing::collect::with_default(collector, || {
383    ///     tracing::info!(
384    ///         field = "value",
385    ///         a_third_field = true,
386    ///     );
387    /// });
388    ///
389    /// handle.assert_finished();
390    /// ```
391    ///
392    /// [`ExpectedField::and`]: fn@crate::field::ExpectedField::and
393    pub fn and(mut self, field: ExpectedField) -> Self {
394        self.fields.insert(field.name, field.value);
395        self
396    }
397
398    /// Indicates that no fields other than those specified should be
399    /// expected.
400    ///
401    /// This method performs the same function as
402    /// [`ExpectedField::only`], but applies in the case where there are
403    /// multiple fields expected.
404    ///
405    /// # Examples
406    ///
407    /// The following test will pass, even though additional fields are
408    /// recorded on the event.
409    ///
410    /// ```
411    /// use tracing_mock::{collector, expect};
412    ///
413    /// let event = expect::event().with_fields(
414    ///     expect::field("field")
415    ///         .with_value(&"value")
416    ///         .and(expect::field("another_field").with_value(&42)),
417    /// );
418    ///
419    /// let (collector, handle) = collector::mock()
420    ///     .event(event)
421    ///     .run_with_handle();
422    ///
423    /// tracing::collect::with_default(collector, || {
424    ///     tracing::info!(
425    ///         field = "value",
426    ///         another_field = 42,
427    ///         a_third_field = true,
428    ///     );
429    /// });
430    ///
431    /// handle.assert_finished();
432    /// ```
433    ///
434    /// If we include `only` on the `ExpectedFields` then the test
435    /// will fail:
436    ///
437    /// ```should_panic
438    /// use tracing_mock::{collector, expect};
439    ///
440    /// let event = expect::event().with_fields(
441    ///     expect::field("field")
442    ///         .with_value(&"value")
443    ///         .and(expect::field("another_field").with_value(&42))
444    ///         .only(),
445    /// );
446    ///
447    /// let (collector, handle) = collector::mock()
448    ///     .event(event)
449    ///     .run_with_handle();
450    ///
451    /// tracing::collect::with_default(collector, || {
452    ///     tracing::info!(
453    ///         field = "value",
454    ///         another_field = 42,
455    ///         a_third_field = true,
456    ///     );
457    /// });
458    ///
459    /// handle.assert_finished();
460    /// ```
461    pub fn only(self) -> Self {
462        Self { only: true, ..self }
463    }
464
465    fn compare_or_panic(&mut self, name: &str, value: &dyn Value, ctx: &str, collector_name: &str) {
466        let value = value.into();
467        match self.fields.remove(name) {
468            Some(ExpectedValue::Any) => {}
469            Some(expected) => assert!(
470                expected == value,
471                "\n[{}] expected `{}` to contain:\n\t`{}{}`\nbut got:\n\t`{}{}`",
472                collector_name,
473                ctx,
474                name,
475                expected,
476                name,
477                value
478            ),
479            None if self.only => panic!(
480                "[{}]expected `{}` to contain only:\n\t`{}`\nbut got:\n\t`{}{}`",
481                collector_name, ctx, self, name, value
482            ),
483            _ => {}
484        }
485    }
486
487    pub(crate) fn checker<'a>(
488        &'a mut self,
489        ctx: &'a str,
490        collector_name: &'a str,
491    ) -> CheckVisitor<'a> {
492        CheckVisitor {
493            expect: self,
494            ctx,
495            collector_name,
496        }
497    }
498
499    pub(crate) fn is_empty(&self) -> bool {
500        self.fields.is_empty()
501    }
502}
503
504impl fmt::Display for ExpectedValue {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        match self {
507            ExpectedValue::F64(v) => write!(f, "f64 = {:?}", v),
508            ExpectedValue::I64(v) => write!(f, "i64 = {:?}", v),
509            ExpectedValue::U64(v) => write!(f, "u64 = {:?}", v),
510            ExpectedValue::Bool(v) => write!(f, "bool = {:?}", v),
511            ExpectedValue::Str(v) => write!(f, "&str = {:?}", v),
512            ExpectedValue::Debug(v) => write!(f, "&fmt::Debug = {:?}", v),
513            ExpectedValue::Any => write!(f, "_ = _"),
514        }
515    }
516}
517
518pub(crate) struct CheckVisitor<'a> {
519    expect: &'a mut ExpectedFields,
520    ctx: &'a str,
521    collector_name: &'a str,
522}
523
524impl Visit for CheckVisitor<'_> {
525    fn record_f64(&mut self, field: &Field, value: f64) {
526        self.expect
527            .compare_or_panic(field.name(), &value, self.ctx, self.collector_name)
528    }
529
530    fn record_i64(&mut self, field: &Field, value: i64) {
531        self.expect
532            .compare_or_panic(field.name(), &value, self.ctx, self.collector_name)
533    }
534
535    fn record_u64(&mut self, field: &Field, value: u64) {
536        self.expect
537            .compare_or_panic(field.name(), &value, self.ctx, self.collector_name)
538    }
539
540    fn record_bool(&mut self, field: &Field, value: bool) {
541        self.expect
542            .compare_or_panic(field.name(), &value, self.ctx, self.collector_name)
543    }
544
545    fn record_str(&mut self, field: &Field, value: &str) {
546        self.expect
547            .compare_or_panic(field.name(), &value, self.ctx, self.collector_name)
548    }
549
550    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
551        self.expect.compare_or_panic(
552            field.name(),
553            &field::debug(value),
554            self.ctx,
555            self.collector_name,
556        )
557    }
558}
559
560impl CheckVisitor<'_> {
561    pub(crate) fn finish(self) {
562        assert!(
563            self.expect.fields.is_empty(),
564            "[{}] {}missing {}",
565            self.collector_name,
566            self.expect,
567            self.ctx
568        );
569    }
570}
571
572impl<'a> From<&'a dyn Value> for ExpectedValue {
573    fn from(value: &'a dyn Value) -> Self {
574        struct MockValueBuilder {
575            value: Option<ExpectedValue>,
576        }
577
578        impl Visit for MockValueBuilder {
579            fn record_f64(&mut self, _: &Field, value: f64) {
580                self.value = Some(ExpectedValue::F64(value));
581            }
582
583            fn record_i64(&mut self, _: &Field, value: i64) {
584                self.value = Some(ExpectedValue::I64(value));
585            }
586
587            fn record_u64(&mut self, _: &Field, value: u64) {
588                self.value = Some(ExpectedValue::U64(value));
589            }
590
591            fn record_bool(&mut self, _: &Field, value: bool) {
592                self.value = Some(ExpectedValue::Bool(value));
593            }
594
595            fn record_str(&mut self, _: &Field, value: &str) {
596                self.value = Some(ExpectedValue::Str(value.to_owned()));
597            }
598
599            fn record_debug(&mut self, _: &Field, value: &dyn fmt::Debug) {
600                self.value = Some(ExpectedValue::Debug(format!("{:?}", value)));
601            }
602        }
603
604        let fake_field = callsite!(name: "fake", kind: Kind::EVENT, fields: fake_field)
605            .metadata()
606            .fields()
607            .field("fake_field")
608            .unwrap();
609        let mut builder = MockValueBuilder { value: None };
610        value.record(&fake_field, &mut builder);
611        builder
612            .value
613            .expect("finish called before a value was recorded")
614    }
615}
616
617impl fmt::Display for ExpectedFields {
618    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619        write!(f, "fields ")?;
620        let entries = self
621            .fields
622            .iter()
623            .map(|(k, v)| (field::display(k), field::display(v)));
624        f.debug_map().entries(entries).finish()
625    }
626}