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}