From ff3587ed05a3a5f6afb43a897f9ae54211762e10 Mon Sep 17 00:00:00 2001 From: Thomas Wickham Date: Sun, 31 Jul 2016 12:52:48 +0200 Subject: [PATCH 1/4] Runner::run now consumes the runner --- src/context.rs | 33 ++++++++--------- src/runner.rs | 98 ++++++++++++++++++++++++++------------------------ 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/src/context.rs b/src/context.rs index 15fb2d3d..ed53de47 100644 --- a/src/context.rs +++ b/src/context.rs @@ -279,7 +279,7 @@ impl<'a> Context<'a> { /// ctx.it("doesn't care if it's before or after a describe", || Ok(()) as Result<(),()>); /// }); /// }); -/// runner.run().unwrap(); +/// let result = runner.run(); /// ``` pub fn describe<'a, 'b, F>(_block_name: &'b str, body: F) -> Runner<'a> where F: 'a + FnOnce(&mut Context<'a>) -> () @@ -329,9 +329,8 @@ pub fn rdescribe<'a, 'b, F>(block_name: &'b str, body: F) -> () where F: 'a + FnOnce(&mut Context<'a>) -> () { - let mut runner = describe(block_name, body); - runner.run().expect("run should be ok"); - let result = runner.result(); + let runner = describe(block_name, body); + let result = runner.run(); assert!(result.is_ok(), "Tests ran with one mor more failures: {:?}", result) @@ -381,11 +380,10 @@ mod tests { #[test] fn is_can_return_a_bool_false() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("a bool true is err", || { false }); }); - runner.run().unwrap(); - assert!(runner.result().is_err()) + assert!(runner.run().is_err()) } #[test] @@ -397,11 +395,10 @@ mod tests { #[test] fn it_can_return_a_result_err() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("is err", || Err(()) as Result<(), ()>); }); - runner.run().unwrap(); - assert!(runner.result().is_err()) + assert!(runner.run().is_err()) } #[test] @@ -417,8 +414,7 @@ mod tests { // let mut runner = describe("a root", |ctx| { // ctx.it("a bool true is err", || { 3 }); // }); - // runner.run().unwrap(); - // assert!(runner.result().is_err()) + // assert!(runner.run().is_err()) //} } @@ -431,7 +427,7 @@ mod tests { let ran_counter = &mut AtomicUsize::new(0); { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.before(|| { ran_counter.fetch_add(1, Ordering::Relaxed); }); @@ -439,7 +435,7 @@ mod tests { ctx.it("second", || Ok(()) as Result<(),()>); ctx.it("third", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); + let _ = runner.run(); } assert_eq!(3, ran_counter.load(Ordering::Relaxed)); @@ -474,7 +470,7 @@ mod tests { let ran_counter = &mut AtomicUsize::new(0); { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.after(|| { ran_counter.fetch_add(1, Ordering::Relaxed); }); @@ -482,7 +478,7 @@ mod tests { ctx.it("second", || Ok(()) as Result<(),()>); ctx.it("third", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); + let _ = runner.run(); } assert_eq!(3, ran_counter.load(Ordering::Relaxed)); @@ -494,7 +490,7 @@ mod tests { let ran_counter = &mut AtomicUsize::new(0); let report = { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.after(|| { ran_counter.fetch_add(1, Ordering::SeqCst); }); @@ -505,8 +501,7 @@ mod tests { ctx.it("third", || 2 == ran_counter.load(Ordering::SeqCst)); }); - runner.run().unwrap(); - runner.result() + runner.run() }; assert!(report.is_ok()); diff --git a/src/runner.rs b/src/runner.rs index caa00721..6cc2bad7 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -57,7 +57,7 @@ impl<'a> Runner<'a> { #[cfg_attr(feature="clippy", allow(redundant_closure))] fn run_test(test_name: &str, - test_function: &mut Box, + mut test_function: Box, handlers: &mut Handlers) -> ExampleResult { @@ -80,18 +80,25 @@ impl<'a> Runner<'a> { child_ctx: &mut Context, handlers: &mut Handlers) -> ExampleResult { + + use std::mem; let mut result = example_result::SUCCESS_RES; - let before_functions = &mut child_ctx.before_each; - let after_functions = &mut child_ctx.after_each; - for test_function in &mut child_ctx.tests { + let ctx = mem::replace(child_ctx, Context::default()); + + let tests = ctx.tests; + let mut before_functions = ctx.before_each; + let mut after_functions = ctx.after_each; + + + for mut test_function in tests { let test_res = { for before_function in before_functions.iter_mut() { before_function() } - let res = match *test_function { - Testable::Test(ref name, ref mut test_function) => { - Runner::run_test(name, test_function, handlers) + let res = match test_function { + Testable::Test(name, test_function) => { + Runner::run_test(&name, test_function, handlers) } Testable::Describe(ref name, ref mut desc) => { handlers.broadcast(&Event::StartDescribe(name.clone())); @@ -120,16 +127,15 @@ impl<'a> Runner<'a> { result } - pub fn run(&mut self) -> Result<(), ()> { + pub fn run(mut self) -> RunnerResult { self.handlers.broadcast(&Event::StartRunner); let mut report = TestReport::default(); let result = Runner::run_and_recurse(&mut report, &mut self.describe, &mut self.handlers); let result = result.res().and(Ok(report)).or_else(|_| Err(report)); - self.report = Some(result); self.handlers.broadcast(&Event::FinishedRunner(result)); - Ok(()) + result } pub fn result(&self) -> RunnerResult { @@ -153,7 +159,7 @@ mod tests { #[test] fn it_create_a_runner_that_can_be_runned() { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("is expected to run", || { assert_eq!(true, true); Ok(()) as Result<(),()> @@ -167,13 +173,13 @@ mod tests { let ran = &mut false; { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("is expected to run", || { *ran = true; Ok(()) as Result<(),()> }) }); - runner.run().unwrap() + runner.run().unwrap(); } assert_eq!(true, *ran) @@ -185,7 +191,7 @@ mod tests { let ran_counter = &mut AtomicUsize::new(0); { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("first run", || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) as Result<(),()> @@ -207,7 +213,7 @@ mod tests { let ran_counter = &mut AtomicUsize::new(0); { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.describe("first describe", |ctx| { ctx.it("first run", || { ran_counter.fetch_add(1, Ordering::Relaxed); @@ -385,60 +391,60 @@ mod tests { #[test] fn tests_can_fail_with_an_error_result() { - let mut runner = describe("A root", |ctx| ctx.it("should fail", || Err(()) as Result<(),()>)); - runner.run().unwrap(); + let runner = describe("A root", |ctx| ctx.it("should fail", || Err(()) as Result<(),()>)); + let result = runner.run(); - assert!(runner.result().is_err()); + assert!(result.is_err()); } #[test] fn should_be_ok_if_tests_are_ok() { - let mut runner = describe("A root", |ctx| ctx.it("should be ok", || Ok(()) as Result<(),()>)); - runner.run().unwrap(); + let runner = describe("A root", |ctx| ctx.it("should be ok", || Ok(()) as Result<(),()>)); + let result = runner.run(); - assert!(runner.result().is_ok()); + assert!(result.is_ok()); } #[test] fn is_ok_if_no_tests_have_been_runned() { - let mut runner = describe("A root", |_ctx| {}); - runner.run().unwrap(); + let runner = describe("A root", |_ctx| {}); + let result = runner.run(); - assert!(runner.result().is_ok()); + assert!(result.is_ok()); } #[test] fn is_err_if_one_test_is_err() { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("an err", || Err(()) as Result<(),()>); ctx.it("an ok", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); + let result = runner.run(); - assert!(runner.result().is_err()); + assert!(result.is_err()); } #[test] fn is_ok_if_all_tests_are_ok() { - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("ok 1", || Ok(()) as Result<(),()>); ctx.it("ok 2", || Ok(()) as Result<(),()>); ctx.it("ok 3", || Ok(()) as Result<(),()>); ctx.it("ok 4", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); + let result = runner.run(); - assert!(runner.result().is_ok()); + assert!(result.is_ok()); } #[test] fn correctly_count_errors() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("first is ok", || ()); ctx.it("second is not", || false); }); - runner.run().unwrap(); - if let Err(res) = runner.result() { + + if let Err(res) = runner.run() { assert_eq!((1, 1), (res.success_count, res.error_count)); } else { @@ -452,7 +458,7 @@ mod tests { use std::sync::atomic::{AtomicUsize, Ordering}; let counter = &mut AtomicUsize::new(0); - let mut runner = describe("A root", |ctx| { + let runner = describe("A root", |ctx| { ctx.it("assert_eq fail", || { assert_eq!(true, false); Ok(()) as Result<(),()> @@ -462,37 +468,36 @@ mod tests { Ok(()) as Result<(),()> }) }); - runner.run().unwrap(); + let result = runner.run(); - assert!(runner.result().is_err()); + // TODO refactor this to tuple + assert!(result.is_err()); assert_eq!(1, counter.load(Ordering::Relaxed)); } #[test] fn can_count_the_tests() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("first", || Ok(()) as Result<(),()>); ctx.it("second", || Ok(()) as Result<(),()>); ctx.it("third", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); - let result = runner.result(); + let results = runner.run(); - assert!(result.is_ok()); - if let Ok(report) = result { + assert!(results.is_ok()); + if let Ok(report) = results { assert_eq!(3, report.total_tests); } } #[test] fn can_count_succes() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("first", || Ok(()) as Result<(),()>); ctx.it("second", || Ok(()) as Result<(),()>); ctx.it("third", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); - let result = runner.result(); + let result = runner.run(); assert!(result.is_ok()); if let Ok(report) = result { @@ -502,13 +507,12 @@ mod tests { #[test] fn can_count_errors() { - let mut runner = describe("a root", |ctx| { + let runner = describe("a root", |ctx| { ctx.it("first", || Err(()) as Result<(),()>); ctx.it("second", || Err(()) as Result<(),()>); ctx.it("third", || Ok(()) as Result<(),()>); }); - runner.run().unwrap(); - let result = runner.result(); + let result = runner.run(); assert!(result.is_err()); if let Err(report) = result { From f1c2b4f1dcdbefbb9ef52fd7f8b8cca0dddda2d4 Mon Sep 17 00:00:00 2001 From: Thomas Wickham Date: Sun, 31 Jul 2016 15:25:49 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=20doc=20=F0=9F=93=9D=20+=20describe=20take?= =?UTF-8?q?s=20now=20a=20FnOnce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context.rs | 4 ++-- src/runner.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/context.rs b/src/context.rs index ed53de47..47878022 100644 --- a/src/context.rs +++ b/src/context.rs @@ -102,8 +102,8 @@ impl<'a> Context<'a> { /// ctx.it("should run last", || Ok(()) as Result<(),()>); /// }); /// ``` - pub fn describe(&mut self, name: &'a str, mut body: F) - where F: 'a + Send + Sync + FnMut(&mut Context<'a>) -> () + pub fn describe(&mut self, name: &'a str, body: F) + where F: 'a + Send + Sync + FnOnce(&mut Context<'a>) -> () { let mut child = Context::default(); diff --git a/src/runner.rs b/src/runner.rs index 6cc2bad7..23bb1bc2 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -84,6 +84,9 @@ impl<'a> Runner<'a> { use std::mem; let mut result = example_result::SUCCESS_RES; + // As Runner::run consomes the runner, we can deconstruct the context tree as we travel + // among it. + // This effectively takes ownership of the context node and replace it with an empty one. let ctx = mem::replace(child_ctx, Context::default()); let tests = ctx.tests; From 84299f5c0068706ecfc05848a8f7ea2a8cfb1eef Mon Sep 17 00:00:00 2001 From: Thomas Wickham Date: Sun, 31 Jul 2016 16:18:55 +0200 Subject: [PATCH 3/4] refactor runner: simplify run_test --- src/runner.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 23bb1bc2..ba06183d 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -56,24 +56,15 @@ pub struct TestReport { impl<'a> Runner<'a> { #[cfg_attr(feature="clippy", allow(redundant_closure))] - fn run_test(test_name: &str, - mut test_function: Box, - handlers: &mut Handlers) + fn run_test(mut test_function: Box) -> ExampleResult { use std::panic::{catch_unwind, AssertUnwindSafe}; use example_result; - handlers.broadcast(&Event::StartTest(String::from(test_name))); let res = catch_unwind(AssertUnwindSafe(|| test_function())); - let res = match res { - Ok(res) => res, - // if test panicked, it means that it failed - Err(_) => example_result::FAILED_RES - }; - handlers.broadcast(&Event::EndTest(res)); - res + res.unwrap_or_else(|_| example_result::FAILED_RES) } fn run_and_recurse(report: &mut TestReport, @@ -101,7 +92,10 @@ impl<'a> Runner<'a> { } let res = match test_function { Testable::Test(name, test_function) => { - Runner::run_test(&name, test_function, handlers) + handlers.broadcast(&Event::StartTest(name)); + let res = Runner::run_test(test_function); + handlers.broadcast(&Event::EndTest(res)); + res } Testable::Describe(ref name, ref mut desc) => { handlers.broadcast(&Event::StartDescribe(name.clone())); From e6a9a51539962556796344034f08f1f6b02389b1 Mon Sep 17 00:00:00 2001 From: Thomas Wickham Date: Sun, 31 Jul 2016 16:58:52 +0200 Subject: [PATCH 4/4] rm now useless Runner#result --- src/runner.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index ba06183d..0754c0e2 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -135,10 +135,6 @@ impl<'a> Runner<'a> { result } - pub fn result(&self) -> RunnerResult { - self.report.unwrap_or_else(|| Ok(TestReport::default())) - } - pub fn add_event_handler(&mut self, handler: &'a mut H) { self.handlers.handlers.push(handler) }