• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(missing_docs, dead_code)]
2 pub use tracing_mock::{event, field, span, subscriber};
3 
4 use tracing_core::{
5     span::{Attributes, Id, Record},
6     Event, Subscriber,
7 };
8 use tracing_mock::{
9     event::MockEvent,
10     span::{MockSpan, NewSpan},
11     subscriber::{Expect, MockHandle},
12 };
13 use tracing_subscriber::{
14     layer::{Context, Layer},
15     registry::{LookupSpan, SpanRef},
16 };
17 
18 use std::{
19     collections::VecDeque,
20     fmt,
21     sync::{Arc, Mutex},
22 };
23 
24 pub mod layer {
25     use super::ExpectLayerBuilder;
26 
mock() -> ExpectLayerBuilder27     pub fn mock() -> ExpectLayerBuilder {
28         ExpectLayerBuilder {
29             expected: Default::default(),
30             name: std::thread::current()
31                 .name()
32                 .map(String::from)
33                 .unwrap_or_default(),
34         }
35     }
36 
named(name: impl std::fmt::Display) -> ExpectLayerBuilder37     pub fn named(name: impl std::fmt::Display) -> ExpectLayerBuilder {
38         mock().named(name)
39     }
40 }
41 
42 pub struct ExpectLayerBuilder {
43     expected: VecDeque<Expect>,
44     name: String,
45 }
46 
47 pub struct ExpectLayer {
48     expected: Arc<Mutex<VecDeque<Expect>>>,
49     current: Mutex<Vec<Id>>,
50     name: String,
51 }
52 
53 impl ExpectLayerBuilder {
54     /// Overrides the name printed by the mock subscriber's debugging output.
55     ///
56     /// The debugging output is displayed if the test panics, or if the test is
57     /// run with `--nocapture`.
58     ///
59     /// By default, the mock subscriber's name is the  name of the test
60     /// (*technically*, the name of the thread where it was created, which is
61     /// the name of the test unless tests are run with `--test-threads=1`).
62     /// When a test has only one mock subscriber, this is sufficient. However,
63     /// some tests may include multiple subscribers, in order to test
64     /// interactions between multiple subscribers. In that case, it can be
65     /// helpful to give each subscriber a separate name to distinguish where the
66     /// debugging output comes from.
named(mut self, name: impl fmt::Display) -> Self67     pub fn named(mut self, name: impl fmt::Display) -> Self {
68         use std::fmt::Write;
69         if !self.name.is_empty() {
70             write!(&mut self.name, "::{}", name).unwrap();
71         } else {
72             self.name = name.to_string();
73         }
74         self
75     }
76 
enter(mut self, span: MockSpan) -> Self77     pub fn enter(mut self, span: MockSpan) -> Self {
78         self.expected.push_back(Expect::Enter(span));
79         self
80     }
81 
event(mut self, event: MockEvent) -> Self82     pub fn event(mut self, event: MockEvent) -> Self {
83         self.expected.push_back(Expect::Event(event));
84         self
85     }
86 
exit(mut self, span: MockSpan) -> Self87     pub fn exit(mut self, span: MockSpan) -> Self {
88         self.expected.push_back(Expect::Exit(span));
89         self
90     }
91 
done(mut self) -> Self92     pub fn done(mut self) -> Self {
93         self.expected.push_back(Expect::Nothing);
94         self
95     }
96 
record<I>(mut self, span: MockSpan, fields: I) -> Self where I: Into<field::Expect>,97     pub fn record<I>(mut self, span: MockSpan, fields: I) -> Self
98     where
99         I: Into<field::Expect>,
100     {
101         self.expected.push_back(Expect::Visit(span, fields.into()));
102         self
103     }
104 
new_span<I>(mut self, new_span: I) -> Self where I: Into<NewSpan>,105     pub fn new_span<I>(mut self, new_span: I) -> Self
106     where
107         I: Into<NewSpan>,
108     {
109         self.expected.push_back(Expect::NewSpan(new_span.into()));
110         self
111     }
112 
run(self) -> ExpectLayer113     pub fn run(self) -> ExpectLayer {
114         ExpectLayer {
115             expected: Arc::new(Mutex::new(self.expected)),
116             name: self.name,
117             current: Mutex::new(Vec::new()),
118         }
119     }
120 
run_with_handle(self) -> (ExpectLayer, MockHandle)121     pub fn run_with_handle(self) -> (ExpectLayer, MockHandle) {
122         let expected = Arc::new(Mutex::new(self.expected));
123         let handle = MockHandle::new(expected.clone(), self.name.clone());
124         let layer = ExpectLayer {
125             expected,
126             name: self.name,
127             current: Mutex::new(Vec::new()),
128         };
129         (layer, handle)
130     }
131 }
132 
133 impl ExpectLayer {
check_span_ref<'spans, S>( &self, expected: &MockSpan, actual: &SpanRef<'spans, S>, what_happened: impl fmt::Display, ) where S: LookupSpan<'spans>,134     fn check_span_ref<'spans, S>(
135         &self,
136         expected: &MockSpan,
137         actual: &SpanRef<'spans, S>,
138         what_happened: impl fmt::Display,
139     ) where
140         S: LookupSpan<'spans>,
141     {
142         if let Some(exp_name) = expected.name() {
143             assert_eq!(
144                 actual.name(),
145                 exp_name,
146                 "\n[{}] expected {} a span named {:?}\n\
147                  [{}] but it was named {:?} instead (span {} {:?})",
148                 self.name,
149                 what_happened,
150                 exp_name,
151                 self.name,
152                 actual.name(),
153                 actual.name(),
154                 actual.id()
155             );
156         }
157 
158         if let Some(exp_level) = expected.level() {
159             let actual_level = actual.metadata().level();
160             assert_eq!(
161                 actual_level,
162                 &exp_level,
163                 "\n[{}] expected {} a span at {:?}\n\
164                  [{}] but it was at {:?} instead (span {} {:?})",
165                 self.name,
166                 what_happened,
167                 exp_level,
168                 self.name,
169                 actual_level,
170                 actual.name(),
171                 actual.id(),
172             );
173         }
174 
175         if let Some(exp_target) = expected.target() {
176             let actual_target = actual.metadata().target();
177             assert_eq!(
178                 actual_target,
179                 exp_target,
180                 "\n[{}] expected {} a span with target {:?}\n\
181                  [{}] but it had the target {:?} instead (span {} {:?})",
182                 self.name,
183                 what_happened,
184                 exp_target,
185                 self.name,
186                 actual_target,
187                 actual.name(),
188                 actual.id(),
189             );
190         }
191     }
192 }
193 
194 impl<S> Layer<S> for ExpectLayer
195 where
196     S: Subscriber + for<'a> LookupSpan<'a>,
197 {
register_callsite( &self, metadata: &'static tracing::Metadata<'static>, ) -> tracing_core::Interest198     fn register_callsite(
199         &self,
200         metadata: &'static tracing::Metadata<'static>,
201     ) -> tracing_core::Interest {
202         println!("[{}] register_callsite {:#?}", self.name, metadata);
203         tracing_core::Interest::always()
204     }
205 
on_record(&self, _: &Id, _: &Record<'_>, _: Context<'_, S>)206     fn on_record(&self, _: &Id, _: &Record<'_>, _: Context<'_, S>) {
207         unimplemented!(
208             "so far, we don't have any tests that need an `on_record` \
209             implementation.\nif you just wrote one that does, feel free to \
210             implement it!"
211         );
212     }
213 
on_event(&self, event: &Event<'_>, cx: Context<'_, S>)214     fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) {
215         let name = event.metadata().name();
216         println!(
217             "[{}] event: {}; level: {}; target: {}",
218             self.name,
219             name,
220             event.metadata().level(),
221             event.metadata().target(),
222         );
223         match self.expected.lock().unwrap().pop_front() {
224             None => {}
225             Some(Expect::Event(mut expected)) => {
226                 let get_parent_name = || cx.event_span(event).map(|span| span.name().to_string());
227                 expected.check(event, get_parent_name, &self.name);
228                 let mut current_scope = cx.event_scope(event).into_iter().flatten();
229                 let expected_scope = expected.scope_mut();
230                 let mut i = 0;
231                 for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
232                     println!(
233                         "[{}] event_scope[{}] actual={} ({:?}); expected={}",
234                         self.name,
235                         i,
236                         actual.name(),
237                         actual.id(),
238                         expected
239                     );
240                     self.check_span_ref(
241                         expected,
242                         &actual,
243                         format_args!("the {}th span in the event's scope to be", i),
244                     );
245                     i += 1;
246                 }
247                 let remaining_expected = &expected_scope[i..];
248                 assert!(
249                     remaining_expected.is_empty(),
250                     "\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
251                     self.name,
252                     self.name,
253                     remaining_expected,
254                 );
255                 assert!(
256                     current_scope.next().is_none(),
257                     "\n[{}] did not expect all spans in the actual event scope!",
258                     self.name,
259                 );
260             }
261             Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)),
262         }
263     }
264 
on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, S>)265     fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, S>) {
266         // TODO: it should be possible to expect spans to follow from other spans
267     }
268 
on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, S>)269     fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, S>) {
270         let meta = span.metadata();
271         println!(
272             "[{}] new_span: name={:?}; target={:?}; id={:?};",
273             self.name,
274             meta.name(),
275             meta.target(),
276             id
277         );
278         let mut expected = self.expected.lock().unwrap();
279         let was_expected = matches!(expected.front(), Some(Expect::NewSpan(_)));
280         if was_expected {
281             if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() {
282                 let get_parent_name = || {
283                     span.parent()
284                         .and_then(|id| cx.span(id))
285                         .or_else(|| cx.lookup_current())
286                         .map(|span| span.name().to_string())
287                 };
288                 expected.check(span, get_parent_name, &self.name);
289             }
290         }
291     }
292 
on_enter(&self, id: &Id, cx: Context<'_, S>)293     fn on_enter(&self, id: &Id, cx: Context<'_, S>) {
294         let span = cx
295             .span(id)
296             .unwrap_or_else(|| panic!("[{}] no span for ID {:?}", self.name, id));
297         println!("[{}] enter: {}; id={:?};", self.name, span.name(), id);
298         match self.expected.lock().unwrap().pop_front() {
299             None => {}
300             Some(Expect::Enter(ref expected_span)) => {
301                 self.check_span_ref(expected_span, &span, "to enter");
302             }
303             Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name())),
304         }
305         self.current.lock().unwrap().push(id.clone());
306     }
307 
on_exit(&self, id: &Id, cx: Context<'_, S>)308     fn on_exit(&self, id: &Id, cx: Context<'_, S>) {
309         if std::thread::panicking() {
310             // `exit()` can be called in `drop` impls, so we must guard against
311             // double panics.
312             println!("[{}] exit {:?} while panicking", self.name, id);
313             return;
314         }
315         let span = cx
316             .span(id)
317             .unwrap_or_else(|| panic!("[{}] no span for ID {:?}", self.name, id));
318         println!("[{}] exit: {}; id={:?};", self.name, span.name(), id);
319         match self.expected.lock().unwrap().pop_front() {
320             None => {}
321             Some(Expect::Exit(ref expected_span)) => {
322                 self.check_span_ref(expected_span, &span, "to exit");
323                 let curr = self.current.lock().unwrap().pop();
324                 assert_eq!(
325                     Some(id),
326                     curr.as_ref(),
327                     "[{}] exited span {:?}, but the current span was {:?}",
328                     self.name,
329                     span.name(),
330                     curr.as_ref().and_then(|id| cx.span(id)).map(|s| s.name())
331                 );
332             }
333             Some(ex) => ex.bad(&self.name, format_args!("exited span {:?}", span.name())),
334         };
335     }
336 
on_close(&self, id: Id, cx: Context<'_, S>)337     fn on_close(&self, id: Id, cx: Context<'_, S>) {
338         if std::thread::panicking() {
339             // `try_close` can be called in `drop` impls, so we must guard against
340             // double panics.
341             println!("[{}] close {:?} while panicking", self.name, id);
342             return;
343         }
344         let span = cx.span(&id);
345         let name = span.as_ref().map(|span| {
346             println!("[{}] close_span: {}; id={:?};", self.name, span.name(), id,);
347             span.name()
348         });
349         if name.is_none() {
350             println!("[{}] drop_span: id={:?}", self.name, id);
351         }
352         if let Ok(mut expected) = self.expected.try_lock() {
353             let was_expected = match expected.front() {
354                 Some(Expect::DropSpan(ref expected_span)) => {
355                     // Don't assert if this function was called while panicking,
356                     // as failing the assertion can cause a double panic.
357                     if !::std::thread::panicking() {
358                         if let Some(ref span) = span {
359                             self.check_span_ref(expected_span, span, "to close");
360                         }
361                     }
362                     true
363                 }
364                 Some(Expect::Event(_)) => {
365                     if !::std::thread::panicking() {
366                         panic!(
367                             "[{}] expected an event, but dropped span {} (id={:?}) instead",
368                             self.name,
369                             name.unwrap_or("<unknown name>"),
370                             id
371                         );
372                     }
373                     true
374                 }
375                 _ => false,
376             };
377             if was_expected {
378                 expected.pop_front();
379             }
380         }
381     }
382 
on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, S>)383     fn on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, S>) {
384         panic!("well-behaved subscribers should never do this to us, lol");
385     }
386 }
387 
388 impl fmt::Debug for ExpectLayer {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result389     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390         let mut s = f.debug_struct("ExpectLayer");
391         s.field("name", &self.name);
392 
393         if let Ok(expected) = self.expected.try_lock() {
394             s.field("expected", &expected);
395         } else {
396             s.field("expected", &format_args!("<locked>"));
397         }
398 
399         if let Ok(current) = self.current.try_lock() {
400             s.field("current", &format_args!("{:?}", &current));
401         } else {
402             s.field("current", &format_args!("<locked>"));
403         }
404 
405         s.finish()
406     }
407 }
408