From b2ecb115c0ec83bb76c070027f32f8f79e56fe71 Mon Sep 17 00:00:00 2001 From: Matteo Settenvini Date: Tue, 2 May 2023 16:54:26 +0200 Subject: [PATCH] Add mocking check support --- Cargo.lock | 117 +++++++++++++++++++++++++++++++++++ grpc/broadcaster/Cargo.toml | 3 + grpc/broadcaster/src/main.rs | 66 ++++++++++++++------ 3 files changed, 167 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af92aff..fb9944b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -122,6 +131,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "either" version = "1.8.1" @@ -164,12 +185,27 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "futures" version = "0.3.28" @@ -276,6 +312,7 @@ version = "0.1.0" dependencies = [ "async-stream", "futures", + "mockall", "prost", "tokio", "tokio-stream", @@ -492,12 +529,54 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -558,6 +637,36 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettyplease" version = "0.1.25" @@ -685,6 +794,8 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -780,6 +891,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "tokio" version = "1.28.0" diff --git a/grpc/broadcaster/Cargo.toml b/grpc/broadcaster/Cargo.toml index 67b9759..6f403c2 100644 --- a/grpc/broadcaster/Cargo.toml +++ b/grpc/broadcaster/Cargo.toml @@ -17,5 +17,8 @@ tokio-stream = { version = "0.1", features = ["sync"] } tonic = { version = "0.9" } prost = { version = "0.11" } +[dev-dependencies] +mockall = "0.11" + [build-dependencies] tonic-build = { version = "0.9" } diff --git a/grpc/broadcaster/src/main.rs b/grpc/broadcaster/src/main.rs index d2837da..ff07dfd 100644 --- a/grpc/broadcaster/src/main.rs +++ b/grpc/broadcaster/src/main.rs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2023 Matteo Settenvini // SPDX-License-Identifier: AGPL-3.0-or-later +use tonic::transport::server::TcpIncoming; + mod pb { tonic::include_proto!("package"); } @@ -14,7 +16,6 @@ use { std::pin::Pin, std::sync::{Arc, Mutex, Weak}, tokio::sync::broadcast, - tokio::task::JoinHandle, tokio_stream::wrappers::BroadcastStream, tonic::{Request, Response, Status, Streaming}, }; @@ -24,18 +25,22 @@ const RENDEZVOUS: &'static str = "[::1]:10000"; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { - let server_handle = run_server().await; - Ok(server_handle.await.unwrap()?) + let url = RENDEZVOUS.parse()?; + let incoming = tonic::transport::server::TcpIncoming::new(url, true, None) + .expect("Cannot bind server socket"); + run_server(incoming).await?; + Ok(()) } -async fn run_server() -> JoinHandle> { +fn run_server( + incoming: TcpIncoming, +) -> impl std::future::Future> { use tonic::transport::Server; - let addr = RENDEZVOUS.parse().unwrap(); let service = MessageService::default(); let svc_adapter = ServiceServer::new(service); let builder = Server::builder().add_service(svc_adapter); - tokio::spawn(builder.serve(addr)) + builder.serve_with_incoming(incoming) } #[derive(Default, Debug)] @@ -94,24 +99,32 @@ mod test { super::pb::service_client::ServiceClient, super::run_server, super::{Message, RENDEZVOUS}, + mockall::automock, tonic::Request, }; + #[automock] + trait MessageChecker { + fn check_contents(&self, msg: &Message); + } + #[tokio::test] async fn bidi_streaming() -> Result<(), Box> { - let _server_handle = run_server().await; + let url = RENDEZVOUS.parse()?; + let incoming = tonic::transport::server::TcpIncoming::new(url, true, None) + .expect("Cannot bind server socket"); + tokio::spawn(run_server(incoming)); - // FIXME: avoid sleep waiting for server to start - tokio::time::sleep(std::time::Duration::from_millis(200)).await; + const N_MESSAGES: usize = 20; + const CLIENT_IDS: &[&str] = &["client AAA", "client BBB", "client CCC"]; - async fn client(id: &str) { - let mut client = ServiceClient::connect(format!("http://{}", RENDEZVOUS)) - .await - .unwrap(); + async fn client(id: &str, mock: &MockMessageChecker) { + let addr = format!("http://{}", RENDEZVOUS); + let mut client = ServiceClient::connect(addr).await.unwrap(); let client_id = String::from(id); let outbound = async_stream::stream! { - for i in 1..21 { + for i in 1..(N_MESSAGES+1) { tokio::task::yield_now().await; let message = Message { contents: format!("{}: {}", client_id, i), @@ -123,16 +136,31 @@ mod test { let response = client.broadcaster(Request::new(outbound)).await.unwrap(); let mut inbound = response.into_inner(); - while let Some(msg) = inbound.message().await.unwrap() { println!("{} received {:?}", id, msg); + mock.check_contents(&msg); } } - let c1 = client("client AAA"); - let c2 = client("client BBB"); - let c3 = client("client CCC"); - let _ = tokio::join!(c1, c2, c3); + let mut mock = MockMessageChecker::new(); + + let mut clients = vec![]; + for client_id in CLIENT_IDS.iter() { + for i in 1..(N_MESSAGES + 1) { + let expected = format!("{}: {}", client_id, i); + mock.expect_check_contents() + .times(CLIENT_IDS.len()) + .withf(move |msg| msg.contents == expected) + .return_const(()); + } + } + + for client_id in CLIENT_IDS { + let c = client(client_id, &mock); + clients.push(c); + } + + let _ = futures::future::join_all(clients).await; println!("Exiting."); Ok(()) }