diff --git a/Cargo.toml b/Cargo.toml index da820677..050095b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,10 @@ members = [ "tower-layer", "tower-limit", "tower-load-shed", - "tower-mock", "tower-reconnect", "tower-retry", "tower-service", + "tower-test", "tower-timeout", "tower-util", ] diff --git a/README.md b/README.md index 8d28c29f..5ea8b8f6 100644 --- a/README.md +++ b/README.md @@ -45,15 +45,14 @@ crates. * [`tower-limit`]: Middleware limiting the number of requests that are processed ([docs][tlim-docs]). -* [`tower-mock`]: Testing utility for mocking a `Service`. This is useful for - testing middleware implementations ([docs][tm-docs]); - * [`tower-reconnect`]: Middleware that automatically reconnects the inner service when it becomes degraded ([docs][tre-docs]). * [`tower-retry`]: Middleware that retries requests based on a given `Policy` ([docs][tretry-docs]). +* [`tower-test`]: Testing utilies ([docs][ttst-docs]). + * [`tower-timeout`]: Middleware that applies a timeout to requests ([docs][tt-docs]). @@ -91,13 +90,14 @@ terms or conditions. [tf-docs]: https://tower-rs.github.io/tower/doc/tower_filter/index.html [`tower-limit`]: tower-limit [tlim-docs]: https://tower-rs.github.io/tower/doc/tower_limit/index.html -[`tower-mock`]: tower-mock -[tm-docs]: https://tower-rs.github.io/tower/doc/tower_mock/index.html [`tower-reconnect`]: tower-reconnect [tre-docs]: https://tower-rs.github.io/tower/doc/tower_reconnect/index.html [`tower-retry`]: tower-retry [tretry-docs]: https://tower-rs.github.io/tower/doc/tower_retry/index.html [`tower-timeout`]: tower-timeout +[`tower-test`]: tower-test +[ttst-docs]: https://tower-rs.github.io/tower/doc/tower_test/index.html +[`tower-rate-limit`]: tower-rate-limit [tt-docs]: https://tower-rs.github.io/tower/doc/tower_timeout/index.html [`tower-util`]: tower-util [tu-docs]: https://tower-rs.github.io/tower/doc/tower_util/index.html diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9cb8685f..37989740 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -20,10 +20,10 @@ jobs: - tower-layer - tower-limit - tower-load-shed - - tower-mock - tower-reconnect - tower-retry - tower-service + - tower-test - tower-timeout - tower-util - tower diff --git a/tower-buffer/Cargo.toml b/tower-buffer/Cargo.toml index c0134eba..d16e121d 100644 --- a/tower-buffer/Cargo.toml +++ b/tower-buffer/Cargo.toml @@ -12,4 +12,4 @@ tokio-executor = "0.1.7" tokio-sync = "0.1.0" [dev-dependencies] -tower-mock = { version = "0.1", path = "../tower-mock" } +tower-test = { version = "0.1", path = "../tower-test" } diff --git a/tower-buffer/tests/buffer.rs b/tower-buffer/tests/buffer.rs index 75af636d..8ea019f1 100644 --- a/tower-buffer/tests/buffer.rs +++ b/tower-buffer/tests/buffer.rs @@ -1,13 +1,15 @@ extern crate futures; extern crate tokio_executor; extern crate tower_buffer; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use futures::prelude::*; use tokio_executor::{SpawnError, TypedExecutor}; use tower_buffer::*; use tower_service::*; +use tower_test::mock; use std::cell::RefCell; use std::thread; @@ -18,9 +20,7 @@ fn req_and_res() { let response = service.call("hello"); - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello"); - request.respond("world"); + assert_request_eq!(handle, "hello").send_response("world"); assert_eq!(response.wait().unwrap(), "world"); } @@ -33,8 +33,7 @@ fn clears_canceled_requests() { let res1 = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); + let send_response1 = assert_request_eq!(handle, "hello"); // don't respond yet, new requests will get buffered @@ -47,15 +46,14 @@ fn clears_canceled_requests() { drop(res2); - req1.respond("world"); + send_response1.send_response("world"); assert_eq!(res1.wait().unwrap(), "world"); // res2 was dropped, so it should have been canceled in the buffer handle.allow(1); - let req3 = handle.next_request().unwrap(); - assert_eq!(*req3, "hello3"); - req3.respond("world3"); + assert_request_eq!(handle, "hello3").send_response("world3"); + assert_eq!(res3.wait().unwrap(), "world3"); } @@ -77,9 +75,7 @@ fn when_inner_is_not_ready() { handle.allow(1); - let req1 = handle.next_request().expect("next_request1"); - assert_eq!(*req1, "hello"); - req1.respond("world"); + assert_request_eq!(handle, "hello").send_response("world"); assert_eq!(res1.wait().expect("res1.wait"), "world"); } @@ -92,7 +88,7 @@ fn when_inner_fails() { // Make the service NotReady handle.allow(0); - handle.error("foobar"); + handle.send_error("foobar"); let mut res1 = service.call("hello"); @@ -112,7 +108,7 @@ fn when_inner_fails() { #[test] fn poll_ready_when_worker_is_dropped_early() { - let (service, _handle) = Mock::new(); + let (service, _handle) = mock::pair::<(), ()>(); // drop that worker right on the floor! let mut exec = ExecFn(drop); @@ -128,7 +124,7 @@ fn poll_ready_when_worker_is_dropped_early() { #[test] fn response_future_when_worker_is_dropped_early() { - let (service, mut handle) = Mock::new(); + let (service, mut handle) = mock::pair::<_, ()>(); // hold the worker in a cell until we want to drop it later let cell = RefCell::new(None); @@ -146,8 +142,8 @@ fn response_future_when_worker_is_dropped_early() { response.wait().expect_err("res.wait"); } -type Mock = tower_mock::Mock<&'static str, &'static str>; -type Handle = tower_mock::Handle<&'static str, &'static str>; +type Mock = mock::Mock<&'static str, &'static str>; +type Handle = mock::Handle<&'static str, &'static str>; struct Exec; @@ -177,7 +173,7 @@ where } fn new_service() -> (Buffer, Handle) { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); // bound is >0 here because clears_canceled_requests needs multiple outstanding requests let service = Buffer::with_executor(service, 10, &mut Exec).unwrap(); (service, handle) diff --git a/tower-filter/Cargo.toml b/tower-filter/Cargo.toml index de79f5f7..7df06930 100644 --- a/tower-filter/Cargo.toml +++ b/tower-filter/Cargo.toml @@ -10,4 +10,4 @@ tower-service = "0.2.0" tower-layer = { version = "0.1", path = "../tower-layer" } [dev-dependencies] -tower-mock = { version = "0.1", path = "../tower-mock" } +tower-test = { version = "0.1", path = "../tower-test" } diff --git a/tower-filter/tests/filter.rs b/tower-filter/tests/filter.rs index 32842c35..c7a84dd0 100644 --- a/tower-filter/tests/filter.rs +++ b/tower-filter/tests/filter.rs @@ -1,12 +1,14 @@ extern crate futures; extern crate tower_filter; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use futures::*; use tower_filter::error::Error; use tower_filter::Filter; use tower_service::*; +use tower_test::mock; use std::thread; @@ -17,12 +19,7 @@ fn passthrough_sync() { let th = thread::spawn(move || { // Receive the requests and respond for i in 0..10 { - let expect = format!("ping-{}", i); - let actual = handle.next_request().unwrap(); - - assert_eq!(actual.as_str(), expect.as_str()); - - actual.respond(format!("pong-{}", i)); + assert_request_eq!(handle, format!("ping-{}", i)).send_response(format!("pong-{}", i)); } }); @@ -53,15 +50,15 @@ fn rejected_sync() { assert!(response.is_err()); } -type Mock = tower_mock::Mock; -type Handle = tower_mock::Handle; +type Mock = mock::Mock; +type Handle = mock::Handle; fn new_service(f: F) -> (Filter, Handle) where F: Fn(&String) -> U, U: IntoFuture, { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); let service = Filter::new(service, f); (service, handle) } diff --git a/tower-limit/Cargo.toml b/tower-limit/Cargo.toml index 81747ac0..663b19c1 100644 --- a/tower-limit/Cargo.toml +++ b/tower-limit/Cargo.toml @@ -12,6 +12,6 @@ tokio-sync = "0.1.3" tokio-timer = "0.2.6" [dev-dependencies] -tower-mock = { version = "0.1", path = "../tower-mock" } +tower-test = { version = "0.1", path = "../tower-test" } tokio = "0.1" tokio-mock-task = "0.1.1" diff --git a/tower-limit/tests/concurrency.rs b/tower-limit/tests/concurrency.rs index c67c33ee..404dd4e3 100644 --- a/tower-limit/tests/concurrency.rs +++ b/tower-limit/tests/concurrency.rs @@ -1,31 +1,34 @@ extern crate futures; extern crate tokio_mock_task; extern crate tower_limit; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use tower_limit::concurrency::ConcurrencyLimit; use tower_service::Service; +use tower_test::mock; use futures::future::{poll_fn, Future}; use tokio_mock_task::MockTask; macro_rules! assert_ready { ($e:expr) => {{ + use futures::Async::*; match $e { - Ok(futures::Async::Ready(v)) => v, - Ok(_) => panic!("not ready"), - Err(e) => panic!("error = {:?}", e), + Ok(Ready(v)) => v, + Ok(NotReady) => panic!("not ready"), + Err(e) => panic!("err = {:?}", e), } }}; } macro_rules! assert_not_ready { ($e:expr) => {{ + use futures::Async::*; match $e { - Ok(futures::Async::NotReady) => {} - Ok(futures::Async::Ready(v)) => panic!("ready; value = {:?}", v), - Err(e) => panic!("error = {:?}", e), + Ok(NotReady) => {} + r => panic!("unexpected poll status = {:?}", r), } }}; } @@ -49,14 +52,10 @@ fn basic_service_limit_functionality_with_poll_ready() { assert!(!task.is_notified()); // The request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 1"); - request.respond("world 1"); + assert_request_eq!(handle, "hello 1").send_response("world 1"); // The next request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 2"); - request.respond("world 2"); + assert_request_eq!(handle, "hello 2").send_response("world 2"); // There are no more requests task.enter(|| { @@ -80,34 +79,36 @@ fn basic_service_limit_functionality_with_poll_ready() { assert_eq!(r2.wait().unwrap(), "world 2"); // The request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 3"); - request.respond("world 3"); + assert_request_eq!(handle, "hello 3").send_response("world 3"); assert_eq!(r3.wait().unwrap(), "world 3"); } #[test] -#[should_panic] fn basic_service_limit_functionality_without_poll_ready() { let mut task = MockTask::new(); let (mut service, mut handle) = new_service(2); + assert_ready!(service.poll_ready()); let r1 = service.call("hello 1"); + + assert_ready!(service.poll_ready()); let r2 = service.call("hello 2"); - let r3 = service.call("hello 3"); - r3.wait().unwrap_err(); + + task.enter(|| { + assert_not_ready!(service.poll_ready()); + }); // The request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 1"); - request.respond("world 1"); + assert_request_eq!(handle, "hello 1").send_response("world 1"); + + assert!(!task.is_notified()); // The next request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 2"); - request.respond("world 2"); + assert_request_eq!(handle, "hello 2").send_response("world 2"); + + assert!(!task.is_notified()); // There are no more requests task.enter(|| { @@ -116,41 +117,34 @@ fn basic_service_limit_functionality_without_poll_ready() { assert_eq!(r1.wait().unwrap(), "world 1"); + assert!(task.is_notified()); + // One more request can be sent + assert_ready!(service.poll_ready()); let r4 = service.call("hello 4"); - let r5 = service.call("hello 5"); - r5.wait().unwrap_err(); + task.enter(|| { + assert_not_ready!(service.poll_ready()); + }); assert_eq!(r2.wait().unwrap(), "world 2"); + assert!(task.is_notified()); // The request gets passed through - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello 4"); - request.respond("world 4"); + assert_request_eq!(handle, "hello 4").send_response("world 4"); assert_eq!(r4.wait().unwrap(), "world 4"); } #[test] -#[should_panic] fn request_without_capacity() { let mut task = MockTask::new(); - let (mut service, mut handle) = new_service(0); + let (mut service, _) = new_service(0); task.enter(|| { - assert!(service.poll_ready().unwrap().is_not_ready()); + assert_not_ready!(service.poll_ready()); }); - - let response = service.call("hello"); - - // There are no more requests - task.enter(|| { - assert!(handle.poll_request().unwrap().is_not_ready()); - }); - - response.wait().unwrap_err(); } #[test] @@ -173,8 +167,8 @@ fn reserve_capacity_without_sending_request() { // s1 sends the request, then s2 is able to get capacity let r1 = s1.call("hello"); - let request = handle.next_request().unwrap(); - request.respond("world"); + + assert_request_eq!(handle, "hello").send_response("world"); task.enter(|| { assert!(s2.poll_ready().unwrap().is_not_ready()); @@ -196,20 +190,17 @@ fn service_drop_frees_capacity() { let mut s2 = s1.clone(); // Reserve capacity in s1 - task.enter(|| { - assert!(s1.poll_ready().unwrap().is_ready()); - }); + assert_ready!(s1.poll_ready()); // Service 2 cannot get capacity task.enter(|| { - assert!(s2.poll_ready().unwrap().is_not_ready()); + assert_not_ready!(s2.poll_ready()); }); drop(s1); - task.enter(|| { - assert!(s2.poll_ready().unwrap().is_ready()); - }); + assert!(task.is_notified()); + assert_ready!(s2.poll_ready()); } #[test] @@ -222,13 +213,13 @@ fn response_error_releases_capacity() { // Reserve capacity in s1 task.enter(|| { - assert!(s1.poll_ready().unwrap().is_ready()); + assert_ready!(s1.poll_ready()); }); // s1 sends the request, then s2 is able to get capacity let r1 = s1.call("hello"); - let request = handle.next_request().unwrap(); - request.error("boom"); + + assert_request_eq!(handle, "hello").send_error("boom"); r1.wait().unwrap_err(); @@ -247,14 +238,14 @@ fn response_future_drop_releases_capacity() { // Reserve capacity in s1 task.enter(|| { - assert!(s1.poll_ready().unwrap().is_ready()); + assert_ready!(s1.poll_ready()); }); // s1 sends the request, then s2 is able to get capacity let r1 = s1.call("hello"); task.enter(|| { - assert!(s2.poll_ready().unwrap().is_not_ready()); + assert_not_ready!(s2.poll_ready()); }); drop(r1); @@ -291,11 +282,11 @@ fn multi_waiters() { assert!(task3.is_notified()); } -type Mock = tower_mock::Mock<&'static str, &'static str>; -type Handle = tower_mock::Handle<&'static str, &'static str>; +type Mock = mock::Mock<&'static str, &'static str>; +type Handle = mock::Handle<&'static str, &'static str>; fn new_service(max: usize) -> (ConcurrencyLimit, Handle) { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); let service = ConcurrencyLimit::new(service, max); (service, handle) } diff --git a/tower-limit/tests/rate.rs b/tower-limit/tests/rate.rs index abf30a83..1d056069 100644 --- a/tower-limit/tests/rate.rs +++ b/tower-limit/tests/rate.rs @@ -2,32 +2,53 @@ extern crate futures; extern crate tokio; extern crate tokio_timer; extern crate tower_limit; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use futures::future; use tower_limit::rate::*; use tower_service::*; +use tower_test::mock; use std::time::{Duration, Instant}; +macro_rules! assert_ready { + ($e:expr) => {{ + use futures::Async::*; + match $e { + Ok(Ready(v)) => v, + Ok(NotReady) => panic!("not ready"), + Err(e) => panic!("err = {:?}", e), + } + }}; +} + +macro_rules! assert_not_ready { + ($e:expr) => {{ + use futures::Async::*; + match $e { + Ok(NotReady) => {} + r => panic!("unexpected poll status = {:?}", r), + } + }}; +} + #[test] fn reaching_capacity() { let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap(); let (mut service, mut handle) = new_service(Rate::new(1, from_millis(100))); - assert!(service.poll_ready().unwrap().is_ready()); + assert_ready!(service.poll_ready()); let response = service.call("hello"); - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello"); - request.respond("world"); + assert_request_eq!(handle, "hello").send_response("world"); let response = rt.block_on(response); assert_eq!(response.unwrap(), "world"); rt.block_on(future::lazy(|| { - assert!(service.poll_ready().unwrap().is_not_ready()); + assert_not_ready!(service.poll_ready()); Ok::<_, ()>(()) })) .unwrap(); @@ -42,24 +63,22 @@ fn reaching_capacity() { .unwrap(); let poll_ready = rt.block_on(future::lazy(|| service.poll_ready())); - assert!(poll_ready.unwrap().is_ready()); + assert_ready!(poll_ready); // Send a second request let response = service.call("two"); - let request = handle.next_request().unwrap(); - assert_eq!(*request, "two"); - request.respond("done"); + assert_request_eq!(handle, "two").send_response("done"); let response = rt.block_on(response); assert_eq!(response.unwrap(), "done"); } -type Mock = tower_mock::Mock<&'static str, &'static str>; -type Handle = tower_mock::Handle<&'static str, &'static str>; +type Mock = mock::Mock<&'static str, &'static str>; +type Handle = mock::Handle<&'static str, &'static str>; fn new_service(rate: Rate) -> (RateLimit, Handle) { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); let service = RateLimit::new(service, rate); (service, handle) } diff --git a/tower-load-shed/Cargo.toml b/tower-load-shed/Cargo.toml index 67b9ef3a..fb10c976 100644 --- a/tower-load-shed/Cargo.toml +++ b/tower-load-shed/Cargo.toml @@ -11,4 +11,4 @@ tower-layer = { version = "0.1.0", path = "../tower-layer" } [dev-dependencies] tokio-mock-task = "0.1.1" -tower-mock = { version = "0.1", path = "../tower-mock" } +tower-test = { version = "0.1", path = "../tower-test" } diff --git a/tower-load-shed/tests/load-shed.rs b/tower-load-shed/tests/load-shed.rs index b7d04d69..31113d6c 100644 --- a/tower-load-shed/tests/load-shed.rs +++ b/tower-load-shed/tests/load-shed.rs @@ -1,11 +1,13 @@ extern crate futures; extern crate tower_load_shed; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use futures::Future; use tower_load_shed::LoadShed; use tower_service::Service; +use tower_test::mock; #[test] fn when_ready() { @@ -20,10 +22,7 @@ fn when_ready() { let response = service.call("hello"); - let request = handle.next_request().unwrap(); - assert_eq!(*request, "hello"); - request.respond("world"); - + assert_request_eq!(handle, "hello").send_response("world"); assert_eq!(response.wait().unwrap(), "world"); } @@ -46,11 +45,11 @@ fn when_not_ready() { assert!(err.is::()); } -type Mock = tower_mock::Mock<&'static str, &'static str>; -type Handle = tower_mock::Handle<&'static str, &'static str>; +type Mock = mock::Mock<&'static str, &'static str>; +type Handle = mock::Handle<&'static str, &'static str>; fn new_service() -> (LoadShed, Handle) { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); let service = LoadShed::new(service); (service, handle) } diff --git a/tower-retry/Cargo.toml b/tower-retry/Cargo.toml index fbc88896..fc418d9b 100644 --- a/tower-retry/Cargo.toml +++ b/tower-retry/Cargo.toml @@ -11,5 +11,5 @@ tower-layer = { version = "0.1", path = "../tower-layer" } tokio-timer = "0.2.4" [dev-dependencies] -tower-mock = { version = "0.1", path = "../tower-mock" } +tower-test = { version = "0.1", path = "../tower-test" } tokio-executor = "0.1.2" diff --git a/tower-retry/tests/retry.rs b/tower-retry/tests/retry.rs index d03e52d7..3f334c84 100644 --- a/tower-retry/tests/retry.rs +++ b/tower-retry/tests/retry.rs @@ -1,11 +1,13 @@ extern crate futures; -extern crate tower_mock; extern crate tower_retry; extern crate tower_service; +#[macro_use] +extern crate tower_test; use futures::{future, Future}; use tower_retry::Policy; use tower_service::Service; +use tower_test::mock; #[test] fn retry_errors() { @@ -14,15 +16,11 @@ fn retry_errors() { assert!(service.poll_ready().unwrap().is_ready()); let mut fut = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); - req1.error("retry me"); + assert_request_eq!(handle, "hello").send_error("retry me"); assert_not_ready(&mut fut); - let req2 = handle.next_request().unwrap(); - assert_eq!(*req2, "hello"); - req2.respond("world"); + assert_request_eq!(handle, "hello").send_response("world"); assert_eq!(fut.wait().unwrap(), "world"); } @@ -34,22 +32,13 @@ fn retry_limit() { assert!(service.poll_ready().unwrap().is_ready()); let mut fut = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); - req1.error("retry 1"); - + assert_request_eq!(handle, "hello").send_error("retry 1"); assert_not_ready(&mut fut); - let req2 = handle.next_request().unwrap(); - assert_eq!(*req2, "hello"); - req2.error("retry 2"); - + assert_request_eq!(handle, "hello").send_error("retry 2"); assert_not_ready(&mut fut); - let req3 = handle.next_request().unwrap(); - assert_eq!(*req3, "hello"); - req3.error("retry 3"); - + assert_request_eq!(handle, "hello").send_error("retry 3"); assert_eq!(fut.wait().unwrap_err().to_string(), "retry 3"); } @@ -60,15 +49,10 @@ fn retry_error_inspection() { assert!(service.poll_ready().unwrap().is_ready()); let mut fut = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); - req1.error("retry 1"); - + assert_request_eq!(handle, "hello").send_error("retry 1"); assert_not_ready(&mut fut); - let req2 = handle.next_request().unwrap(); - assert_eq!(*req2, "hello"); - req2.error("reject"); + assert_request_eq!(handle, "hello").send_error("reject"); assert_eq!(fut.wait().unwrap_err().to_string(), "reject"); } @@ -79,10 +63,7 @@ fn retry_cannot_clone_request() { assert!(service.poll_ready().unwrap().is_ready()); let fut = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); - req1.error("retry 1"); - + assert_request_eq!(handle, "hello").send_error("retry 1"); assert_eq!(fut.wait().unwrap_err().to_string(), "retry 1"); } @@ -95,10 +76,7 @@ fn success_with_cannot_clone() { assert!(service.poll_ready().unwrap().is_ready()); let fut = service.call("hello"); - let req1 = handle.next_request().unwrap(); - assert_eq!(*req1, "hello"); - req1.respond("world"); - + assert_request_eq!(handle, "hello").send_response("world"); assert_eq!(fut.wait().unwrap(), "world"); } @@ -106,8 +84,8 @@ type Req = &'static str; type Res = &'static str; type InnerError = &'static str; type Error = Box<::std::error::Error + Send + Sync>; -type Mock = tower_mock::Mock; -type Handle = tower_mock::Handle; +type Mock = mock::Mock; +type Handle = mock::Handle; #[derive(Clone)] struct RetryErrors; @@ -182,7 +160,7 @@ impl Policy for CannotClone { fn new_service + Clone>( policy: P, ) -> (tower_retry::Retry, Handle) { - let (service, handle) = Mock::new(); + let (service, handle) = mock::pair(); let service = tower_retry::Retry::new(policy, service); (service, handle) } diff --git a/tower-mock/Cargo.toml b/tower-test/Cargo.toml similarity index 65% rename from tower-mock/Cargo.toml rename to tower-test/Cargo.toml index 36933185..30b0f395 100644 --- a/tower-mock/Cargo.toml +++ b/tower-test/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tower-mock" +name = "tower-test" version = "0.1.0" authors = ["Carl Lerche "] publish = false @@ -8,3 +8,6 @@ publish = false futures = "0.1" tokio-sync = "0.1.3" tower-service = "0.2.0" + +[dev-dependencies] +tower = { version = "0.1.0", path = "../tower" } diff --git a/tower-mock/README.md b/tower-test/README.md similarity index 100% rename from tower-mock/README.md rename to tower-test/README.md diff --git a/tower-test/src/lib.rs b/tower-test/src/lib.rs new file mode 100644 index 00000000..f94244fc --- /dev/null +++ b/tower-test/src/lib.rs @@ -0,0 +1,8 @@ +//! Mock `Service` that can be used in tests. + +extern crate futures; +extern crate tokio_sync; +extern crate tower_service; + +mod macros; +pub mod mock; diff --git a/tower-test/src/macros.rs b/tower-test/src/macros.rs new file mode 100644 index 00000000..4654237a --- /dev/null +++ b/tower-test/src/macros.rs @@ -0,0 +1,41 @@ +/// Asserts that the mock handle receives a new request equal to the given +/// value. +/// +/// On success, the [`SendResponse`] handle for the matched request is returned, +/// allowing the caller to respond to the request. On failure, the macro panics. +/// +/// # Examples +/// +/// ```rust +/// extern crate tower; +/// #[macro_use] +/// extern crate tower_test; +/// +/// use tower::Service; +/// use tower_test::mock; +/// +/// # fn main() { +/// let (mut mock, mut handle) = mock::pair(); +/// +/// assert!(mock.poll_ready().is_ok()); +/// let _response = mock.call("hello"); +/// +/// assert_request_eq!(handle, "hello") +/// .send_response("world"); +/// # } +/// ``` +#[macro_export] +macro_rules! assert_request_eq { + ($mock_handle:expr, $expect:expr) => { + assert_request_eq!($mock_handle, $expect,) + }; + ($mock_handle:expr, $expect:expr, $($arg:tt)*) => {{ + let (actual, send_response) = match $mock_handle.next_request() { + Some(r) => r, + None => panic!("expected a request but none was received."), + }; + + assert_eq!(actual, $expect, $($arg)*); + send_response + }}; +} diff --git a/tower-mock/src/error.rs b/tower-test/src/mock/error.rs similarity index 100% rename from tower-mock/src/error.rs rename to tower-test/src/mock/error.rs diff --git a/tower-mock/src/future.rs b/tower-test/src/mock/future.rs similarity index 96% rename from tower-mock/src/future.rs rename to tower-test/src/mock/future.rs index 0061196c..58ff18f7 100644 --- a/tower-mock/src/future.rs +++ b/tower-test/src/mock/future.rs @@ -1,6 +1,6 @@ //! Future types -use error::{self, Error}; +use super::error::{self, Error}; use futures::{Async, Future, Poll}; use tokio_sync::oneshot; diff --git a/tower-mock/src/lib.rs b/tower-test/src/mock/mod.rs similarity index 77% rename from tower-mock/src/lib.rs rename to tower-test/src/mock/mod.rs index 8cafae65..1a7c39b1 100644 --- a/tower-mock/src/lib.rs +++ b/tower-test/src/mock/mod.rs @@ -1,14 +1,10 @@ //! Mock `Service` that can be used in tests. -extern crate futures; -extern crate tokio_sync; -extern crate tower_service; - pub mod error; pub mod future; -use error::Error; -use future::ResponseFuture; +use self::error::Error; +use self::future::ResponseFuture; use futures::task::{self, Task}; use futures::{Async, Future, Poll, Stream}; use tokio_sync::{mpsc, oneshot}; @@ -16,7 +12,7 @@ use tower_service::Service; use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use std::{ops, u64}; +use std::u64; /// A mock service #[derive(Debug)] @@ -34,15 +30,11 @@ pub struct Handle { state: Arc>, } -#[derive(Debug)] -pub struct Request { - request: T, - respond: Respond, -} +type Request = (T, SendResponse); -/// Respond to a request received by `Mock`. +/// Send a response in reply to a received request. #[derive(Debug)] -pub struct Respond { +pub struct SendResponse { tx: oneshot::Sender>, } @@ -67,27 +59,23 @@ struct State { type Tx = mpsc::UnboundedSender>; type Rx = mpsc::UnboundedReceiver>; -// ===== impl Mock ===== +/// Create a new `Mock` and `Handle` pair. +pub fn pair() -> (Mock, Handle) { + let (tx, rx) = mpsc::unbounded_channel(); + let tx = Mutex::new(tx); -impl Mock { - /// Create a new `Mock` and `Handle` pair. - pub fn new() -> (Self, Handle) { - let (tx, rx) = mpsc::unbounded_channel(); - let tx = Mutex::new(tx); + let state = Arc::new(Mutex::new(State::new())); - let state = Arc::new(Mutex::new(State::new())); + let mock = Mock { + id: 0, + tx, + state: state.clone(), + can_send: false, + }; - let mock = Mock { - id: 0, - tx, - state: state.clone(), - can_send: false, - }; + let handle = Handle { rx, state }; - let handle = Handle { rx, state }; - - (mock, handle) - } + (mock, handle) } impl Service for Mock { @@ -148,13 +136,9 @@ impl Service for Mock { } let (tx, rx) = oneshot::channel(); + let send_response = SendResponse { tx }; - let request = Request { - request, - respond: Respond { tx }, - }; - - match self.tx.lock().unwrap().try_send(request) { + match self.tx.lock().unwrap().try_send((request, send_response)) { Ok(_) => {} Err(_) => { // TODO: Can this be reached @@ -234,7 +218,7 @@ impl Handle { } /// Make the next poll_ method error with the given error. - pub fn error>(&mut self, e: E) { + pub fn send_error>(&mut self, e: E) { let mut state = self.state.lock().unwrap(); state.err_with = Some(e.into()); @@ -265,40 +249,15 @@ impl Drop for Handle { } } -// ===== impl Request ===== +// ===== impl SendResponse ===== -impl Request { - /// Split the request and respond handle - pub fn into_parts(self) -> (T, Respond) { - (self.request, self.respond) - } - - pub fn respond(self, response: U) { - self.respond.respond(response) - } - - pub fn error>(self, err: E) { - self.respond.error(err) - } -} - -impl ops::Deref for Request { - type Target = T; - - fn deref(&self) -> &T { - &self.request - } -} - -// ===== impl Respond ===== - -impl Respond { - pub fn respond(self, response: T) { +impl SendResponse { + pub fn send_response(self, response: T) { // TODO: Should the result be dropped? let _ = self.tx.send(Ok(response)); } - pub fn error>(self, err: E) { + pub fn send_error>(self, err: E) { // TODO: Should the result be dropped? let _ = self.tx.send(Err(err.into())); } diff --git a/tower-mock/tests/mock.rs b/tower-test/tests/mock.rs similarity index 81% rename from tower-mock/tests/mock.rs rename to tower-test/tests/mock.rs index f130379d..fc983557 100644 --- a/tower-mock/tests/mock.rs +++ b/tower-test/tests/mock.rs @@ -1,8 +1,10 @@ extern crate futures; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; use tower_service::Service; +use tower_test::mock; use futures::Future; @@ -20,9 +22,7 @@ fn single_request_ready() { let mut response = mock.call("hello?".into()); // Get the request from the handle - let request = handle.next_request().unwrap(); - - assert_eq!(request.as_str(), "hello?"); + let send_response = assert_request_eq!(handle, "hello?"); // Response is not ready with_task(|| { @@ -30,7 +30,7 @@ fn single_request_ready() { }); // Send the response - request.respond("yes?".into()); + send_response.send_response("yes?".into()); assert_eq!(response.wait().unwrap().as_str(), "yes?"); } @@ -51,11 +51,11 @@ fn backpressure() { mock.call("hello?".into()); } -type Mock = tower_mock::Mock; -type Handle = tower_mock::Handle; +type Mock = mock::Mock; +type Handle = mock::Handle; fn new_mock() -> (Mock, Handle) { - Mock::new() + mock::pair() } // Helper to run some code within context of a task diff --git a/tower-util/Cargo.toml b/tower-util/Cargo.toml index c6ac944b..7311234f 100644 --- a/tower-util/Cargo.toml +++ b/tower-util/Cargo.toml @@ -30,4 +30,4 @@ tower-layer = { version = "0.1.0", path = "../tower-layer" } [dev-dependencies] tokio-mock-task = "0.1" tower = { version = "0.1.0", path = "../tower" } -tower-mock = { version = "0.1.0", path = "../tower-mock" } +tower-test = { version = "0.1.0", path = "../tower-test" } diff --git a/tower-util/tests/call_all.rs b/tower-util/tests/call_all.rs index 2b962c4e..f8c3d6da 100644 --- a/tower-util/tests/call_all.rs +++ b/tower-util/tests/call_all.rs @@ -1,8 +1,9 @@ extern crate futures; extern crate tokio_mock_task; extern crate tower; -extern crate tower_mock; extern crate tower_service; +#[macro_use] +extern crate tower_test; extern crate tower_util; use futures::future::{ok, FutureResult}; @@ -11,8 +12,8 @@ use futures::{Async, Poll, Stream}; use std::cell::Cell; use std::rc::Rc; use tower::ServiceExt; -use tower_mock::*; use tower_service::*; +use tower_test::mock; type Error = Box<::std::error::Error + Send + Sync>; @@ -123,26 +124,23 @@ fn ordered() { #[test] fn unordered() { - let (mock, mut handle) = Mock::<_, &'static str>::new(); + let (mock, mut handle) = mock::pair::<_, &'static str>(); let mut task = tokio_mock_task::MockTask::new(); let requests = stream::iter_ok::<_, Error>(&["one", "two"]); let mut svc = mock.call_all(requests).unordered(); assert_not_ready!(task.enter(|| svc.poll())); - let (req1, resp1) = handle.next_request().unwrap().into_parts(); - let (req2, resp2) = handle.next_request().unwrap().into_parts(); + let resp1 = assert_request_eq!(handle, &"one"); + let resp2 = assert_request_eq!(handle, &"two"); - assert_eq!(req1, &"one"); - assert_eq!(req2, &"two"); - - resp2.respond("resp 1"); + resp2.send_response("resp 1"); let v = assert_ready!(task.enter(|| svc.poll())); assert_eq!(v, Some("resp 1")); assert_not_ready!(task.enter(|| svc.poll())); - resp1.respond("resp 2"); + resp1.send_response("resp 2"); let v = assert_ready!(task.enter(|| svc.poll())); assert_eq!(v, Some("resp 2"));