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 subscriber API in the
5//! [`subscriber`] 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::{expect, subscriber};
18//!
19//! let event = expect::event()
20//!     .with_fields(expect::field("field_name"));
21//!
22//! let (subscriber, handle) = subscriber::mock()
23//!     .event(event)
24//!     .run_with_handle();
25//!
26//! tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
47//!     .event(event)
48//!     .run_with_handle();
49//!
50//! tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
67//!
68//! let event = expect::event()
69//!     .with_fields(expect::field("field_name").with_value(&"value"));
70//!
71//! let (subscriber, handle) = subscriber::mock()
72//!     .event(event)
73//!     .run_with_handle();
74//!
75//! tracing::subscriber::with_default(subscriber, || {
76//!     tracing::info!(field_name = "different value");
77//! });
78//!
79//! handle.assert_finished();
80//! ```
81//!
82//! [`subscriber`]: mod@crate::subscriber
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::{expect, subscriber};
163    ///
164    /// let event = expect::event()
165    ///     .with_fields(expect::field("field_name").with_value(&"value"));
166    ///
167    /// let (subscriber, handle) = subscriber::mock()
168    ///     .event(event)
169    ///     .run_with_handle();
170    ///
171    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
182    ///
183    /// let event = expect::event()
184    ///     .with_fields(expect::field("field_name").with_value(&"value"));
185    ///
186    /// let (subscriber, handle) = subscriber::mock()
187    ///     .event(event)
188    ///     .run_with_handle();
189    ///
190    /// tracing::subscriber::with_default(subscriber, || {
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    /// Any fields introduced by `.and` must also match. If any fields
206    /// are not present, or if the value for any field is different,
207    /// then the expectation will fail.
208    ///
209    /// # Examples
210    ///
211    /// ```
212    /// use tracing_mock::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
221    ///     .event(event)
222    ///     .run_with_handle();
223    ///
224    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
246    ///     .event(event)
247    ///     .run_with_handle();
248    ///
249    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
278    ///
279    /// let event = expect::event()
280    ///     .with_fields(expect::field("field").with_value(&"value"));
281    ///
282    /// let (subscriber, handle) = subscriber::mock().event(event).run_with_handle();
283    ///
284    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
296    ///
297    /// let event = expect::event()
298    ///     .with_fields(expect::field("field").with_value(&"value").only());
299    ///
300    /// let (subscriber, handle) = subscriber::mock().event(event).run_with_handle();
301    ///
302    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
351    ///     .event(event)
352    ///     .run_with_handle();
353    ///
354    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
379    ///     .event(event)
380    ///     .run_with_handle();
381    ///
382    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
420    ///     .event(event)
421    ///     .run_with_handle();
422    ///
423    /// tracing::subscriber::with_default(subscriber, || {
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::{expect, subscriber};
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 (subscriber, handle) = subscriber::mock()
448    ///     .event(event)
449    ///     .run_with_handle();
450    ///
451    /// tracing::subscriber::with_default(subscriber, || {
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(
466        &mut self,
467        name: &str,
468        value: &dyn Value,
469        ctx: &str,
470        subscriber_name: &str,
471    ) {
472        let value = value.into();
473        match self.fields.remove(name) {
474            Some(ExpectedValue::Any) => {}
475            Some(expected) => assert!(
476                expected == value,
477                "\n[{}] expected `{}` to contain:\n\t`{}{}`\nbut got:\n\t`{}{}`",
478                subscriber_name,
479                ctx,
480                name,
481                expected,
482                name,
483                value
484            ),
485            None if self.only => panic!(
486                "[{}]expected `{}` to contain only:\n\t`{}`\nbut got:\n\t`{}{}`",
487                subscriber_name, ctx, self, name, value
488            ),
489            _ => {}
490        }
491    }
492
493    pub(crate) fn checker<'a>(
494        &'a mut self,
495        ctx: &'a str,
496        subscriber_name: &'a str,
497    ) -> CheckVisitor<'a> {
498        CheckVisitor {
499            expect: self,
500            ctx,
501            subscriber_name,
502        }
503    }
504
505    pub(crate) fn is_empty(&self) -> bool {
506        self.fields.is_empty()
507    }
508}
509
510impl fmt::Display for ExpectedValue {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        match self {
513            ExpectedValue::F64(v) => write!(f, "f64 = {:?}", v),
514            ExpectedValue::I64(v) => write!(f, "i64 = {:?}", v),
515            ExpectedValue::U64(v) => write!(f, "u64 = {:?}", v),
516            ExpectedValue::Bool(v) => write!(f, "bool = {:?}", v),
517            ExpectedValue::Str(v) => write!(f, "&str = {:?}", v),
518            ExpectedValue::Debug(v) => write!(f, "&fmt::Debug = {:?}", v),
519            ExpectedValue::Any => write!(f, "_ = _"),
520        }
521    }
522}
523
524pub(crate) struct CheckVisitor<'a> {
525    expect: &'a mut ExpectedFields,
526    ctx: &'a str,
527    subscriber_name: &'a str,
528}
529
530impl Visit for CheckVisitor<'_> {
531    fn record_f64(&mut self, field: &Field, value: f64) {
532        self.expect
533            .compare_or_panic(field.name(), &value, self.ctx, self.subscriber_name)
534    }
535
536    fn record_i64(&mut self, field: &Field, value: i64) {
537        self.expect
538            .compare_or_panic(field.name(), &value, self.ctx, self.subscriber_name)
539    }
540
541    fn record_u64(&mut self, field: &Field, value: u64) {
542        self.expect
543            .compare_or_panic(field.name(), &value, self.ctx, self.subscriber_name)
544    }
545
546    fn record_bool(&mut self, field: &Field, value: bool) {
547        self.expect
548            .compare_or_panic(field.name(), &value, self.ctx, self.subscriber_name)
549    }
550
551    fn record_str(&mut self, field: &Field, value: &str) {
552        self.expect
553            .compare_or_panic(field.name(), &value, self.ctx, self.subscriber_name)
554    }
555
556    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
557        self.expect.compare_or_panic(
558            field.name(),
559            &field::debug(value),
560            self.ctx,
561            self.subscriber_name,
562        )
563    }
564}
565
566impl CheckVisitor<'_> {
567    pub(crate) fn finish(self) {
568        assert!(
569            self.expect.fields.is_empty(),
570            "[{}] {}missing {}",
571            self.subscriber_name,
572            self.expect,
573            self.ctx
574        );
575    }
576}
577
578impl<'a> From<&'a dyn Value> for ExpectedValue {
579    fn from(value: &'a dyn Value) -> Self {
580        struct MockValueBuilder {
581            value: Option<ExpectedValue>,
582        }
583
584        impl Visit for MockValueBuilder {
585            fn record_f64(&mut self, _: &Field, value: f64) {
586                self.value = Some(ExpectedValue::F64(value));
587            }
588
589            fn record_i64(&mut self, _: &Field, value: i64) {
590                self.value = Some(ExpectedValue::I64(value));
591            }
592
593            fn record_u64(&mut self, _: &Field, value: u64) {
594                self.value = Some(ExpectedValue::U64(value));
595            }
596
597            fn record_bool(&mut self, _: &Field, value: bool) {
598                self.value = Some(ExpectedValue::Bool(value));
599            }
600
601            fn record_str(&mut self, _: &Field, value: &str) {
602                self.value = Some(ExpectedValue::Str(value.to_owned()));
603            }
604
605            fn record_debug(&mut self, _: &Field, value: &dyn fmt::Debug) {
606                self.value = Some(ExpectedValue::Debug(format!("{:?}", value)));
607            }
608        }
609
610        let fake_field = callsite!(name: "fake", kind: Kind::EVENT, fields: fake_field)
611            .metadata()
612            .fields()
613            .field("fake_field")
614            .unwrap();
615        let mut builder = MockValueBuilder { value: None };
616        value.record(&fake_field, &mut builder);
617        builder
618            .value
619            .expect("finish called before a value was recorded")
620    }
621}
622
623impl fmt::Display for ExpectedFields {
624    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625        write!(f, "fields ")?;
626        let entries = self
627            .fields
628            .iter()
629            .map(|(k, v)| (field::display(k), field::display(v)));
630        f.debug_map().entries(entries).finish()
631    }
632}