rspec/runner/
mod.rs

1//! Runners are responsible for executing a test suite's examples.
2
3mod configuration;
4mod observer;
5
6pub use runner::configuration::*;
7pub use runner::observer::*;
8
9use std::borrow::Borrow;
10use std::cell::Cell;
11use std::ops::{Deref, DerefMut};
12use std::panic;
13#[cfg(not(test))]
14use std::process;
15use std::sync::{Arc, Mutex};
16
17use time::Instant;
18
19use rayon::prelude::*;
20
21use block::Block;
22use block::Context;
23use block::Example;
24use block::Suite;
25use report::ContextReport;
26use report::ExampleReport;
27use report::SuiteReport;
28use report::{BlockReport, Report};
29use visitor::TestSuiteVisitor;
30
31/// Runner for executing a test suite's examples.
32pub struct Runner {
33    pub configuration: configuration::Configuration,
34    observers: Vec<Arc<dyn RunnerObserver>>,
35    should_exit: Mutex<Cell<bool>>,
36}
37
38impl Runner {
39    pub fn new(configuration: Configuration, observers: Vec<Arc<dyn RunnerObserver>>) -> Runner {
40        Runner {
41            configuration,
42            observers,
43            should_exit: Mutex::new(Cell::new(false)),
44        }
45    }
46}
47
48impl Runner {
49    pub fn run<T>(&self, suite: &Suite<T>) -> SuiteReport
50    where
51        T: Clone + Send + Sync + ::std::fmt::Debug,
52    {
53        let mut environment = suite.environment.clone();
54        self.prepare_before_run();
55        let report = self.visit(suite, &mut environment);
56        self.clean_after_run();
57        if let Ok(mut mutex_guard) = self.should_exit.lock() {
58            *mutex_guard.deref_mut().get_mut() |= report.is_failure();
59        }
60        report
61    }
62
63    fn broadcast<F>(&self, mut handler: F)
64    where
65        F: FnMut(&dyn RunnerObserver),
66    {
67        for observer in &self.observers {
68            handler(observer.borrow());
69        }
70    }
71
72    fn wrap_all<T, U, F>(&self, context: &Context<T>, environment: &mut T, wrapped_block: F) -> U
73    where
74        F: Fn(&mut T) -> U,
75    {
76        for before_function in context.before_all.iter() {
77            before_function(environment);
78        }
79        let result = wrapped_block(environment);
80        for after_function in context.after_all.iter() {
81            after_function(environment);
82        }
83        result
84    }
85
86    fn wrap_each<T, U, F>(&self, context: &Context<T>, environment: &mut T, wrapped_block: F) -> U
87    where
88        F: Fn(&mut T) -> U,
89    {
90        for before_function in context.before_each.iter() {
91            before_function(environment);
92        }
93        let result = wrapped_block(environment);
94        for after_function in context.after_each.iter() {
95            after_function(environment);
96        }
97        result
98    }
99
100    fn evaluate_blocks_parallel<T>(&self, context: &Context<T>, environment: &T) -> Vec<BlockReport>
101    where
102        T: Clone + Send + Sync + ::std::fmt::Debug,
103    {
104        context
105            .blocks
106            .par_iter()
107            .map(|block| self.evaluate_block(block, context, environment))
108            .collect()
109    }
110
111    fn evaluate_blocks_serial<T>(&self, context: &Context<T>, environment: &T) -> Vec<BlockReport>
112    where
113        T: Clone + Send + Sync + ::std::fmt::Debug,
114    {
115        context
116            .blocks
117            .iter()
118            .map(|block| self.evaluate_block(block, context, environment))
119            .collect()
120    }
121
122    fn evaluate_block<T>(
123        &self,
124        block: &Block<T>,
125        context: &Context<T>,
126        environment: &T,
127    ) -> BlockReport
128    where
129        T: Clone + Send + Sync + ::std::fmt::Debug,
130    {
131        let mut environment = environment.clone();
132        self.wrap_each(context, &mut environment, |environment| {
133            self.visit(block, environment)
134        })
135    }
136
137    fn prepare_before_run(&self) {
138        panic::set_hook(Box::new(|_panic_info| {
139            // XXX panics already catched at the test call site, don't output the trace in stdout
140        }));
141    }
142
143    fn clean_after_run(&self) {
144        // XXX reset panic hook back to default hook:
145        let _ = panic::take_hook();
146    }
147}
148
149#[cfg(test)]
150impl Default for Runner {
151    /// Used for testing
152    fn default() -> Self {
153        Runner::new(Configuration::default(), vec![])
154    }
155}
156
157impl Drop for Runner {
158    fn drop(&mut self) {
159        let should_exit = if let Ok(mutex_guard) = self.should_exit.lock() {
160            mutex_guard.deref().get()
161        } else {
162            false
163        };
164
165        if self.configuration.exit_on_failure && should_exit {
166            // XXX Cargo test failure returns 101.
167            //
168            // > "We use 101 as the standard failure exit code because it's something unique
169            // > that the test runner can check for in run-fail tests (as opposed to something
170            // > like 1, which everybody uses). I don't expect this behavior can ever change.
171            // > This behavior probably dates to before 2013,
172            // > all the way back to the creation of compiletest." – @brson
173            #[cfg(not(test))]
174            process::exit(101);
175            #[cfg(test)]
176            panic!("test suite failed !")
177        }
178    }
179}
180
181impl<T> TestSuiteVisitor<Suite<T>> for Runner
182where
183    T: Clone + Send + Sync + ::std::fmt::Debug,
184{
185    type Environment = T;
186    type Output = SuiteReport;
187
188    fn visit(&self, suite: &Suite<T>, environment: &mut Self::Environment) -> Self::Output {
189        self.broadcast(|handler| handler.enter_suite(self, &suite.header));
190        let report = SuiteReport::new(
191            suite.header.clone(),
192            self.visit(&suite.context, environment),
193        );
194        self.broadcast(|handler| handler.exit_suite(self, &suite.header, &report));
195        report
196    }
197}
198
199impl<T> TestSuiteVisitor<Block<T>> for Runner
200where
201    T: Clone + Send + Sync + ::std::fmt::Debug,
202{
203    type Environment = T;
204    type Output = BlockReport;
205
206    fn visit(&self, member: &Block<T>, environment: &mut Self::Environment) -> Self::Output {
207        match member {
208            Block::Example(ref example) => {
209                let header = example.header.clone();
210                let report = self.visit(example, environment);
211                BlockReport::Example(header, report)
212            }
213            Block::Context(ref context) => {
214                let header = context.header.clone();
215                let report = self.visit(context, &mut environment.clone());
216                BlockReport::Context(header, report)
217            }
218        }
219    }
220}
221
222impl<T> TestSuiteVisitor<Context<T>> for Runner
223where
224    T: Clone + Send + Sync + ::std::fmt::Debug,
225{
226    type Environment = T;
227    type Output = ContextReport;
228
229    fn visit(&self, context: &Context<T>, environment: &mut Self::Environment) -> Self::Output {
230        if let Some(ref header) = context.header {
231            self.broadcast(|handler| handler.enter_context(self, &header));
232        }
233        let start_time = Instant::now();
234        let reports: Vec<_> = self.wrap_all(context, environment, |environment| {
235            if self.configuration.parallel {
236                self.evaluate_blocks_parallel(context, environment)
237            } else {
238                self.evaluate_blocks_serial(context, environment)
239            }
240        });
241        let end_time = Instant::now();
242        let elapsed_time = end_time - start_time;
243        let report = ContextReport::new(reports, elapsed_time);
244        if let Some(ref header) = context.header {
245            self.broadcast(|handler| handler.exit_context(self, &header, &report));
246        }
247        report
248    }
249}
250
251impl<T> TestSuiteVisitor<Example<T>> for Runner
252where
253    T: Clone + Send + Sync + ::std::fmt::Debug,
254{
255    type Environment = T;
256    type Output = ExampleReport;
257
258    fn visit(&self, example: &Example<T>, environment: &mut Self::Environment) -> Self::Output {
259        self.broadcast(|handler| handler.enter_example(self, &example.header));
260        let start_time = Instant::now();
261        let result = (example.function)(environment);
262        let end_time = Instant::now();
263        let elapsed_time = end_time - start_time;
264        let report = ExampleReport::new(result, elapsed_time);
265        self.broadcast(|handler| handler.exit_example(self, &example.header, &report));
266        report
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    mod runner {
275        use super::*;
276
277        #[test]
278        fn it_can_be_instanciated() {
279            // arrange
280            let _ = Runner::new(Configuration::default(), vec![]);
281            // act
282            // assert
283        }
284
285        mod broadcast {
286            use super::*;
287
288            use header::*;
289            use std::sync::atomic::*;
290
291            // XXX blank impl for stubbing
292            impl RunnerObserver for () {}
293
294            #[test]
295            fn it_calls_the_closure() {
296                // arrange
297                let spy = Arc::new(());
298                let runner = Runner::new(Configuration::default(), vec![spy]);
299                let has_been_called = AtomicBool::new(false);
300                // act
301                runner.broadcast(|_| has_been_called.store(true, Ordering::SeqCst));
302                // assert
303                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
304            }
305
306            #[test]
307            fn it_calls_it_once_per_observer() {
308                // arrange
309                let spy1 = Arc::new(());
310                let spy2 = Arc::new(());
311                let runner = Runner::new(Configuration::default(), vec![spy1, spy2]);
312                let call_times = AtomicUsize::new(0);
313                // act
314                runner.broadcast(|_| {
315                    call_times.fetch_add(1, Ordering::SeqCst);
316                });
317                // assert
318                assert_eq!(2, call_times.load(Ordering::SeqCst))
319            }
320
321            struct ObserverStub {
322                events: Mutex<Vec<(&'static str, SuiteHeader)>>,
323            }
324            impl ObserverStub {
325                fn new() -> Self {
326                    ObserverStub {
327                        events: Mutex::new(vec![]),
328                    }
329                }
330            }
331
332            // XXX stub implem
333            impl RunnerObserver for ObserverStub {
334                fn enter_suite(&self, _runner: &Runner, header: &SuiteHeader) {
335                    let mut vec = self.events.lock().unwrap();
336                    (*vec).push(("enter_suite", header.clone()));
337                }
338            }
339
340            #[test]
341            fn it_gives_the_observer_as_callback_argument() {
342                // arrange
343                let spy1 = Arc::new(ObserverStub::new());
344                let expected = SuiteHeader::new(SuiteLabel::Describe, "hello");
345                let runner = Runner::new(Configuration::default(), vec![spy1.clone()]);
346                // act
347                runner.broadcast(|observer| observer.enter_suite(&runner, &expected.clone()));
348                // assert
349                let lock = spy1.events.lock().expect("no dangling threads");
350                let res = (*lock).get(0).expect("to have been called once");
351                assert_eq!(&("enter_suite", expected), res);
352            }
353        }
354
355        mod wrap_each {
356            use super::*;
357
358            use std::sync::atomic::*;
359
360            #[test]
361            fn it_can_be_called() {
362                // arrange
363                let runner = Runner::default();
364                // act
365                runner.wrap_each(&Context::default(), &mut (), |_| {})
366                // assert
367            }
368
369            #[test]
370            fn it_calls_the_closure() {
371                // arrange
372                let runner = Runner::default();
373                let has_been_called = AtomicBool::new(false);
374                // act
375                runner.wrap_each(&Context::default(), &mut (), |_| {
376                    has_been_called.store(true, Ordering::SeqCst)
377                });
378                // assert
379                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
380            }
381
382            #[test]
383            fn it_calls_the_before_each_callbacks() {
384                // arrange
385                let runner = Runner::default();
386                let has_been_called = Arc::new(AtomicBool::new(false));
387                let closure_bool_handler = has_been_called.clone();
388                let mut context = Context::default();
389                // act
390                context.before_each(move |_| closure_bool_handler.store(true, Ordering::SeqCst));
391                runner.wrap_each(&context, &mut (), |_| ());
392                // assert
393                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
394            }
395
396            #[test]
397            fn it_calls_the_after_each_callbacks() {
398                // arrange
399                let runner = Runner::default();
400                let has_been_called = Arc::new(AtomicBool::new(false));
401                let closure_bool_handler = has_been_called.clone();
402                let mut context = Context::default();
403                // act
404                context.after_each(move |_| closure_bool_handler.store(true, Ordering::SeqCst));
405                runner.wrap_each(&context, &mut (), |_| ());
406                // assert
407                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
408            }
409
410            #[test]
411            fn it_calls_all_before_each_callbacks() {
412                // arrange
413                let runner = Runner::default();
414                let call_counter = Arc::new(AtomicUsize::new(0));
415                let closure_counter_handler1 = call_counter.clone();
416                let closure_counter_handler2 = call_counter.clone();
417                let mut context = Context::default();
418                // act
419                context.before_each(move |_| {
420                    closure_counter_handler1.fetch_add(1, Ordering::SeqCst);
421                });
422                context.before_each(move |_| {
423                    closure_counter_handler2.fetch_add(1, Ordering::SeqCst);
424                });
425                runner.wrap_each(&context, &mut (), |_| ());
426                // assert
427                assert_eq!(2, call_counter.load(Ordering::SeqCst));
428            }
429
430            #[test]
431            fn it_calls_all_after_each_callbacks() {
432                // arrange
433                let runner = Runner::default();
434                let call_counter = Arc::new(AtomicUsize::new(0));
435                let closure_counter_handler1 = call_counter.clone();
436                let closure_counter_handler2 = call_counter.clone();
437                let mut context = Context::default();
438                // act
439                context.after_each(move |_| {
440                    closure_counter_handler1.fetch_add(1, Ordering::SeqCst);
441                });
442                context.after_each(move |_| {
443                    closure_counter_handler2.fetch_add(1, Ordering::SeqCst);
444                });
445                runner.wrap_each(&context, &mut (), |_| ());
446                // assert
447                assert_eq!(2, call_counter.load(Ordering::SeqCst));
448            }
449
450            #[test]
451            fn it_calls_before_each_hook_before_the_main_closure() {
452                // arrange
453                let runner = Runner::default();
454                let last_caller_id = Arc::new(AtomicUsize::new(0));
455                let last_caller_handler1 = last_caller_id.clone();
456                let last_caller_handler2 = last_caller_id.clone();
457                let mut context = Context::default();
458                // act
459                context.before_each(move |_| {
460                    last_caller_handler1.store(1, Ordering::SeqCst);
461                });
462                runner.wrap_each(&context, &mut (), |_| {
463                    last_caller_handler2.store(2, Ordering::SeqCst);
464                });
465                // assert
466                assert_eq!(2, last_caller_id.load(Ordering::SeqCst));
467            }
468
469            #[test]
470            fn it_calls_after_each_hook_after_the_main_closure() {
471                // arrange
472                let runner = Runner::default();
473                let last_caller_id = Arc::new(AtomicUsize::new(0));
474                let last_caller_handler1 = last_caller_id.clone();
475                let last_caller_handler2 = last_caller_id.clone();
476                let mut context = Context::default();
477                // act
478                context.after_each(move |_| {
479                    last_caller_handler1.store(1, Ordering::SeqCst);
480                });
481                runner.wrap_each(&context, &mut (), |_| {
482                    last_caller_handler2.store(2, Ordering::SeqCst);
483                });
484                // assert
485                assert_eq!(1, last_caller_id.load(Ordering::SeqCst));
486            }
487        }
488
489        mod wrap_all {
490            use super::*;
491
492            use std::sync::atomic::*;
493
494            #[test]
495            fn it_can_be_called() {
496                // arrange
497                let runner = Runner::default();
498                // act
499                runner.wrap_all(&Context::default(), &mut (), |_| {})
500                // assert
501            }
502
503            #[test]
504            fn it_calls_the_closure() {
505                // arrange
506                let runner = Runner::default();
507                let has_been_called = AtomicBool::new(false);
508                // act
509                runner.wrap_all(&Context::default(), &mut (), |_| {
510                    has_been_called.store(true, Ordering::SeqCst)
511                });
512                // assert
513                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
514            }
515
516            #[test]
517            fn it_calls_the_before_all_callbacks() {
518                // arrange
519                let runner = Runner::default();
520                let has_been_called = Arc::new(AtomicBool::new(false));
521                let closure_bool_handler = has_been_called.clone();
522                let mut context = Context::default();
523                // act
524                context.before_all(move |_| closure_bool_handler.store(true, Ordering::SeqCst));
525                runner.wrap_all(&context, &mut (), |_| ());
526                // assert
527                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
528            }
529
530            #[test]
531            fn it_calls_the_after_all_callbacks() {
532                // arrange
533                let runner = Runner::default();
534                let has_been_called = Arc::new(AtomicBool::new(false));
535                let closure_bool_handler = has_been_called.clone();
536                let mut context = Context::default();
537                // act
538                context.after_all(move |_| closure_bool_handler.store(true, Ordering::SeqCst));
539                runner.wrap_all(&context, &mut (), |_| ());
540                // assert
541                assert_eq!(true, has_been_called.load(Ordering::SeqCst));
542            }
543
544            #[test]
545            fn it_calls_all_before_all_callbacks() {
546                // arrange
547                let runner = Runner::default();
548                let call_counter = Arc::new(AtomicUsize::new(0));
549                let closure_counter_handler1 = call_counter.clone();
550                let closure_counter_handler2 = call_counter.clone();
551                let mut context = Context::default();
552                // act
553                context.before_all(move |_| {
554                    closure_counter_handler1.fetch_add(1, Ordering::SeqCst);
555                });
556                context.before_all(move |_| {
557                    closure_counter_handler2.fetch_add(1, Ordering::SeqCst);
558                });
559                runner.wrap_all(&context, &mut (), |_| ());
560                // assert
561                assert_eq!(2, call_counter.load(Ordering::SeqCst));
562            }
563
564            #[test]
565            fn it_calls_all_after_all_callbacks() {
566                // arrange
567                let runner = Runner::default();
568                let call_counter = Arc::new(AtomicUsize::new(0));
569                let closure_counter_handler1 = call_counter.clone();
570                let closure_counter_handler2 = call_counter.clone();
571                let mut context = Context::default();
572                // act
573                context.after_all(move |_| {
574                    closure_counter_handler1.fetch_add(1, Ordering::SeqCst);
575                });
576                context.after_all(move |_| {
577                    closure_counter_handler2.fetch_add(1, Ordering::SeqCst);
578                });
579                runner.wrap_all(&context, &mut (), |_| ());
580                // assert
581                assert_eq!(2, call_counter.load(Ordering::SeqCst));
582            }
583
584            #[test]
585            fn it_calls_before_all_hook_before_the_main_closure() {
586                // arrange
587                let runner = Runner::default();
588                let last_caller_id = Arc::new(AtomicUsize::new(0));
589                let last_caller_handler1 = last_caller_id.clone();
590                let last_caller_handler2 = last_caller_id.clone();
591                let mut context = Context::default();
592                // act
593                context.before_all(move |_| {
594                    last_caller_handler1.store(1, Ordering::SeqCst);
595                });
596                runner.wrap_all(&context, &mut (), |_| {
597                    last_caller_handler2.store(2, Ordering::SeqCst);
598                });
599                // assert
600                assert_eq!(2, last_caller_id.load(Ordering::SeqCst));
601            }
602
603            #[test]
604            fn it_calls_after_all_hook_after_the_main_closure() {
605                // arrange
606                let runner = Runner::default();
607                let last_caller_id = Arc::new(AtomicUsize::new(0));
608                let last_caller_handler1 = last_caller_id.clone();
609                let last_caller_handler2 = last_caller_id.clone();
610                let mut context = Context::default();
611                // act
612                context.after_all(move |_| {
613                    last_caller_handler1.store(1, Ordering::SeqCst);
614                });
615                runner.wrap_all(&context, &mut (), |_| {
616                    last_caller_handler2.store(2, Ordering::SeqCst);
617                });
618                // assert
619                assert_eq!(1, last_caller_id.load(Ordering::SeqCst));
620            }
621        }
622    }
623
624    mod impl_drop_for_runner {
625        use super::*;
626
627        #[test]
628        #[should_panic]
629        fn it_should_abort() {
630            // arrange
631            let config = ConfigurationBuilder::default()
632                .exit_on_failure(true)
633                .build()
634                .unwrap();
635            // act
636            {
637                let runner = Runner::new(config, vec![]);
638                (*runner.should_exit.lock().unwrap()).set(true);
639            }
640            // assert
641            // test should panic
642        }
643    }
644
645    mod impl_visitor_example_for_runner {
646        use super::*;
647
648        use header::*;
649        use report::*;
650        use std::sync::atomic::*;
651
652        #[derive(Default, Debug, Clone)]
653        struct SpyObserver {
654            enter_example: Arc<AtomicBool>,
655            exit_example: Arc<AtomicBool>,
656        }
657        impl RunnerObserver for SpyObserver {
658            fn enter_example(&self, _runner: &Runner, _header: &ExampleHeader) {
659                self.enter_example.store(true, Ordering::SeqCst)
660            }
661
662            fn exit_example(
663                &self,
664                _runner: &Runner,
665                _header: &ExampleHeader,
666                _report: &ExampleReport,
667            ) {
668                self.exit_example.store(true, Ordering::SeqCst)
669            }
670        }
671
672        #[test]
673        fn it_can_be_called() {
674            // arrange
675            let runner = Runner::default();
676            let example = Example::fixture_success();
677            // act
678            // assert
679            runner.visit(&example, &mut ());
680        }
681
682        #[test]
683        fn it_calls_observer_hooks() {
684            // arrange
685            let spy = Arc::new(SpyObserver::default());
686            let runner = Runner::new(Configuration::default(), vec![spy.clone()]);
687            let example = Example::fixture_success();
688            // act
689            runner.visit(&example, &mut ());
690            // assert
691            assert_eq!(true, spy.enter_example.load(Ordering::SeqCst));
692            assert_eq!(true, spy.exit_example.load(Ordering::SeqCst))
693        }
694
695        #[test]
696        fn it_gives_an_env_to_the_example() {
697            // arrange
698            let runner = Runner::default();
699            let mut environment = Arc::new(AtomicBool::new(false));
700            // act
701            let example = Example::new(ExampleHeader::default(), |env: &Arc<AtomicBool>| {
702                env.store(true, Ordering::SeqCst);
703                ExampleResult::Success
704            });
705            runner.visit(&example, &mut environment);
706            // assert
707            assert_eq!(true, environment.load(Ordering::SeqCst));
708        }
709    }
710
711    mod impl_visitor_block_for_runner {
712        use super::*;
713
714        #[test]
715        fn it_can_be_called() {
716            // arrange
717            let runner = Runner::default();
718            let block = Block::Example(Example::fixture_success());
719            // act
720            // assert
721            runner.visit(&block, &mut ());
722        }
723    }
724}