diff --git a/Cargo.toml b/Cargo.toml index 5ed494d2..90f41ffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,5 +64,4 @@ serde_json = { version = "1.0.49" } tracing-subscriber = "0.2.3" tempfile = "3.1.0" mockito = "0.23.3" -serde = "1.0.105" -ansi_term = "0.12.1" +# serde = "1.0.105" diff --git a/src/async_client.rs b/src/async_client.rs index 4944df91..308054cd 100644 --- a/src/async_client.rs +++ b/src/async_client.rs @@ -806,21 +806,11 @@ mod test { use crate::identifiers::{RoomId, UserId}; use crate::test_builder::EventBuilder; - use crate::{assert_eq_, async_assert}; use std::convert::TryFrom; #[tokio::test] async fn client_runner() { - // TODO make this actually test something - - async_assert! { - async fn test_client_homeserver<'a>(cli: &'a AsyncClient) -> Result<(), String> { - assert_eq_!(cli.homeserver(), &Url::parse(&mockito::server_url()).unwrap()); - Ok(()) - } - } - let session = crate::Session { access_token: "12345".to_owned(), user_id: UserId::try_from("@example:localhost").unwrap(), @@ -832,31 +822,26 @@ mod test { let rid = RoomId::try_from("!roomid:room.com").unwrap(); let uid = UserId::try_from("@example:localhost").unwrap(); - let bld = EventBuilder::default(); - let runner = bld + let mut bld = EventBuilder::default() .add_room_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember) .add_room_event_from_file( "./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels, ) - .build_client_runner(rid, uid) - .set_client(client) - .add_client_assert(test_client_homeserver); + .build_client_runner(rid, uid); - runner.run_test().await; + let cli = bld.set_client(client).to_client().await; + + assert_eq!( + cli.homeserver(), + &Url::parse(&mockito::server_url()).unwrap() + ); } #[tokio::test] async fn mock_runner() { use std::convert::TryFrom; - async_assert! { - async fn test_mock_homeserver<'a>(cli: &'a AsyncClient) -> Result<(), String> { - assert_eq_!(cli.homeserver(), &url::Url::parse(&mockito::server_url()).unwrap()); - Ok(()) - } - } - let session = crate::Session { access_token: "12345".to_owned(), user_id: UserId::try_from("@example:localhost").unwrap(), @@ -866,8 +851,7 @@ mod test { let homeserver = url::Url::parse(&mockito::server_url()).unwrap(); let client = AsyncClient::new(homeserver, Some(session)).unwrap(); - let bld = EventBuilder::default(); - let runner = bld + let mut bld = EventBuilder::default() .add_room_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember) .add_room_event_from_file( "./tests/data/events/power_levels.json", @@ -876,10 +860,13 @@ mod test { .build_mock_runner( "GET", mockito::Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()), - ) - .set_client(client) - .add_client_assert(test_mock_homeserver); + ); - runner.run_test().await; + let cli = bld.set_client(client).to_client().await.unwrap(); + + assert_eq!( + cli.homeserver(), + &Url::parse(&mockito::server_url()).unwrap() + ); } } diff --git a/src/models/room.rs b/src/models/room.rs index cfe40178..f307ec93 100644 --- a/src/models/room.rs +++ b/src/models/room.rs @@ -389,7 +389,6 @@ mod test { use crate::events::room::member::MembershipState; use crate::identifiers::UserId; use crate::test_builder::EventBuilder; - use crate::{assert_, assert_eq_}; use crate::{AsyncClient, Session, SyncSettings}; use mockito::{mock, Matcher}; @@ -439,40 +438,31 @@ mod test { assert!(room.deref().power_levels.is_some()) } - #[tokio::test] - async fn room_events() { - fn test_room_users(room: &Room) -> Result<(), String> { - assert_eq_!(room.members.len(), 1); - Ok(()) - } - - fn test_room_power(room: &Room) -> Result<(), String> { - assert_!(room.power_levels.is_some()); - assert_eq_!( - room.power_levels.as_ref().unwrap().kick, - js_int::Int::new(50).unwrap() - ); - let admin = room - .members - .get(&UserId::try_from("@example:localhost").unwrap()) - .unwrap(); - assert_eq_!(admin.power_level.unwrap(), js_int::Int::new(100).unwrap()); - Ok(()) - } - + #[test] + fn room_events() { let rid = RoomId::try_from("!roomid:room.com").unwrap(); let uid = UserId::try_from("@example:localhost").unwrap(); - let bld = EventBuilder::default(); - let runner = bld + + let mut bld = EventBuilder::default() .add_room_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember) .add_room_event_from_file( "./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels, ) - .build_room_runner(&rid, &uid) - .add_room_assert(test_room_power) - .add_room_assert(test_room_users); + .build_room_runner(&rid, &uid); - runner.run_test().await; + let room = bld.to_room(); + + assert_eq!(room.members.len(), 1); + assert!(room.power_levels.is_some()); + assert_eq!( + room.power_levels.as_ref().unwrap().kick, + js_int::Int::new(50).unwrap() + ); + let admin = room + .members + .get(&UserId::try_from("@example:localhost").unwrap()) + .unwrap(); + assert_eq!(admin.power_level.unwrap(), js_int::Int::new(100).unwrap()); } } diff --git a/src/models/room_member.rs b/src/models/room_member.rs index ce905cf5..e1211112 100644 --- a/src/models/room_member.rs +++ b/src/models/room_member.rs @@ -189,78 +189,57 @@ mod test { use crate::events::room::member::MembershipState; use crate::identifiers::{RoomId, UserId}; use crate::test_builder::EventBuilder; - use crate::{assert_, assert_eq_, Room}; use js_int::{Int, UInt}; use std::convert::TryFrom; - #[tokio::test] - async fn room_member_events() { - fn test_room_member(room: &Room) -> Result<(), String> { - let member = room - .members - .get(&UserId::try_from("@example:localhost").unwrap()) - .unwrap(); - assert_eq_!(member.membership, MembershipState::Join); - assert_eq_!(member.power_level, Int::new(100)); - println!("{:#?}", room); - Ok(()) - } - + #[test] + fn room_member_events() { let rid = RoomId::try_from("!roomid:room.com").unwrap(); let uid = UserId::try_from("@example:localhost").unwrap(); - let bld = EventBuilder::default(); - let runner = bld + let mut bld = EventBuilder::default() .add_room_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember) .add_room_event_from_file( "./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels, ) - .build_room_runner(&rid, &uid) - .add_room_assert(test_room_member); + .build_room_runner(&rid, &uid); + let room = bld.to_room(); - runner.run_test().await; + let member = room + .members + .get(&UserId::try_from("@example:localhost").unwrap()) + .unwrap(); + assert_eq!(member.membership, MembershipState::Join); + assert_eq!(member.power_level, Int::new(100)); } - #[tokio::test] - async fn member_presence_events() { - fn test_room_member(room: &Room) -> Result<(), String> { - let member = room - .members - .get(&UserId::try_from("@example:localhost").unwrap()) - .unwrap(); - assert_eq_!(member.membership, MembershipState::Join); - assert_eq_!(member.power_level, Int::new(100)); - println!("{:#?}", room); - Ok(()) - } - - fn test_presence(room: &Room) -> Result<(), String> { - let member = room - .members - .get(&UserId::try_from("@example:localhost").unwrap()) - .unwrap(); - assert_!(member.avatar_url.is_some()); - assert_eq_!(member.last_active_ago, UInt::new(1)); - assert_eq_!(member.presence, Some(PresenceState::Online)); - Ok(()) - } - + #[test] + fn member_presence_events() { let rid = RoomId::try_from("!roomid:room.com").unwrap(); let uid = UserId::try_from("@example:localhost").unwrap(); - let bld = EventBuilder::default(); - let runner = bld + let mut bld = EventBuilder::default() .add_room_event_from_file("./tests/data/events/member.json", RoomEvent::RoomMember) .add_room_event_from_file( "./tests/data/events/power_levels.json", RoomEvent::RoomPowerLevels, ) .add_presence_event_from_file("./tests/data/events/presence.json") - .build_room_runner(&rid, &uid) - .add_room_assert(test_presence) - .add_room_assert(test_room_member); + .build_room_runner(&rid, &uid); - runner.run_test().await; + let room = bld.to_room(); + + let member = room + .members + .get(&UserId::try_from("@example:localhost").unwrap()) + .unwrap(); + + assert_eq!(member.membership, MembershipState::Join); + assert_eq!(member.power_level, Int::new(100)); + + assert!(member.avatar_url.is_some()); + assert_eq!(member.last_active_ago, UInt::new(1)); + assert_eq!(member.presence, Some(PresenceState::Online)); } } diff --git a/src/test_builder.rs b/src/test_builder.rs index aada6502..4849cfb1 100644 --- a/src/test_builder.rs +++ b/src/test_builder.rs @@ -15,176 +15,14 @@ use crate::events::{ use crate::identifiers::{RoomId, UserId}; use crate::AsyncClient; -use ansi_term::Colour; use mockito::{self, mock, Mock}; use crate::models::Room; -/// `assert` to use in `TestRunner`. -/// -/// This returns an `Err` on failure, instead of panicking. -#[macro_export] -macro_rules! assert_ { - ($truth:expr) => { - if !$truth { - return Err(format!( - r#"assertion failed: `(left == right)` - expression: `{:?}` - failed at {}"#, - stringify!($truth), - file!(), - )); - } - }; -} - -/// `assert_eq` to use in `TestRunner. -/// -/// This returns an `Err` on failure, instead of panicking. -#[macro_export] -macro_rules! assert_eq_ { - ($left:expr, $right:expr) => ({ - match (&$left, &$right) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - return Err(format!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}` - failed at {}:{}"#, - &*left_val, - &*right_val, - file!(), - line!() - )) - } - } - } - }); - ($left:expr, $right:expr,) => ({ - $crate::assert_eq!($left, $right) - }); - ($left:expr, $right:expr, $($arg:tt)+) => ({ - match (&($left), &($right)) { - (left_val, right_val) => { - if !(*left_val == *right_val) { - return Err(format!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}` : {} - failed at {}:{}"#, - &*left_val, - &*right_val, - $crate::format_args!($($arg)+), - file!(), - line!(), - )) - } - } - } - }); -} - -/// `assert_ne` to use in `TestRunner. -/// -/// This returns an `Err` on failure, instead of panicking. -#[macro_export] -macro_rules! assert_ne_ { - ($left:expr, $right:expr) => ({ - match (&$left, &$right) { - (left_val, right_val) => { - if (*left_val == *right_val) { - return Err(format!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}` - failed at {}:{}"#, - &*left_val, - &*right_val, - file!(), - line!() - )) - } - } - } - }); - ($left:expr, $right:expr,) => ({ - $crate::assert_eq!($left, $right) - }); - ($left:expr, $right:expr, $($arg:tt)+) => ({ - match (&($left), &($right)) { - (left_val, right_val) => { - if (*left_val == *right_val) { - return Err(format!(r#"assertion failed: `(left == right)` - left: `{:?}`, - right: `{:?}` : {} - failed at {}:{}"#, - &*left_val, - &*right_val, - $crate::format_args!($($arg)+), - file!(), - line!(), - )) - } - } - } - }); -} - -/// Convenience macro for declaring an `async` assert function to store in the `TestRunner`. -/// -/// Declares an async function that can be stored in a struct. -/// -/// # Examples -/// ```rust -/// # use matrix_sdk::AsyncClient; -/// # use url::Url; -/// async_assert!{ -/// async fn foo(cli: &AsyncClient) -> Result<(), String> { -/// assert_eq_!(cli.homeserver(), &url::Url::parse("matrix.org").unwrap()); -/// Ok(()) -/// } -/// } -/// ``` -#[macro_export] -macro_rules! async_assert { - ( - $( #[$attr:meta] )* - $pub:vis async fn $fname:ident<$lt:lifetime> ( $($args:tt)* ) $(-> $Ret:ty)? { - $($body:tt)* - } - ) => ( - $( #[$attr] )* - #[allow(unused_parens)] - $pub fn $fname<$lt> ( $($args)* ) - -> ::std::pin::Pin<::std::boxed::Box - + ::std::marker::Send + $lt>> - { - ::std::boxed::Box::pin(async move { $($body)* }) - } - ); - ( - $( #[$attr:meta] )* - $pub:vis async fn $fname:ident ( $($args:tt)* ) $(-> $Ret:ty)? { - $($body:tt)* - } - ) => ( - $( #[$attr] )* - #[allow(unused_parens)] - $pub fn $fname ( $($args)* ) - -> ::std::pin::Pin<::std::boxed::Box<(dyn ::std::future::Future - + ::std::marker::Send)>> - { - ::std::boxed::Box::pin(async move { $($body)* }) - } - ) -} - -type DynFuture<'lt, T> = ::std::pin::Pin>>; -pub type AsyncAssert = fn(&AsyncClient) -> DynFuture>; - +/// Easily create events to stream into either a Client or a `Room` for testing. #[derive(Default)] pub struct EventBuilder { /// The events that determine the state of a `Room`. - /// - /// When testing the models `RoomEvent`s are needed. room_events: Vec, /// The presence events that determine the presence state of a `RoomMember`. presence_events: Vec, @@ -204,17 +42,11 @@ pub struct RoomTestRunner { /// The account data events that determine the state of a `Room`. account_data: Vec, /// The events that determine the state of a `Room`. - /// - /// When testing the models `RoomEvent`s are needed. room_events: Vec, /// The presence events that determine the presence state of a `RoomMember`. presence_events: Vec, /// The state events that determine the state of a `Room`. state_events: Vec, - /// A `Vec` of callbacks that should assert something about the room. - /// - /// The callback should use the provided `assert_`, `assert_*_` macros. - room_assertions: Vec Result<(), String>>, } pub struct ClientTestRunner { @@ -229,19 +61,13 @@ pub struct ClientTestRunner { /// The account data events that determine the state of a `Room`. account_data: Vec, /// The events that determine the state of a `Room`. - /// - /// When testing the models `RoomEvent`s are needed. room_events: Vec, /// The presence events that determine the presence state of a `RoomMember`. presence_events: Vec, /// The state events that determine the state of a `Room`. state_events: Vec, - /// A `Vec` of callbacks that should assert something about the client. - /// - /// The callback should use the provided `assert_`, `assert_*_` macros. - client_assertions: Vec, } -// the compiler complains that the event Vec's are never used they are. + #[allow(dead_code)] pub struct MockTestRunner { /// Used when testing the whole client @@ -251,17 +77,11 @@ pub struct MockTestRunner { /// The account data events that determine the state of a `Room`. account_data: Vec, /// The events that determine the state of a `Room`. - /// - /// When testing the models `RoomEvent`s are needed. room_events: Vec, /// The presence events that determine the presence state of a `RoomMember`. presence_events: Vec, /// The state events that determine the state of a `Room`. state_events: Vec, - /// A `Vec` of callbacks that should assert something about the client. - /// - /// The callback should use the provided `assert_`, `assert_*_` macros. - client_assertions: Vec, /// `mokito::Mock` mock: Option, } @@ -269,16 +89,6 @@ pub struct MockTestRunner { #[allow(dead_code)] #[allow(unused_mut)] impl EventBuilder { - /// Creates an `IncomingResponse` to hold events for a sync. - pub fn create_sync_response(mut self) -> Self { - self - } - - /// Just throw events at the client, not part of a specific response. - pub fn create_event_stream(mut self) -> Self { - self - } - /// Add an event to the room events `Vec`. pub fn add_ephemeral_from_file>( mut self, @@ -420,7 +230,6 @@ impl EventBuilder { room_events: Vec::new(), presence_events: Vec::new(), state_events: Vec::new(), - client_assertions: Vec::new(), mock, } } @@ -438,7 +247,6 @@ impl EventBuilder { room_events: self.room_events, presence_events: self.presence_events, state_events: self.state_events, - client_assertions: Vec::new(), } } @@ -454,27 +262,22 @@ impl EventBuilder { room_events: self.room_events, presence_events: self.presence_events, state_events: self.state_events, - room_assertions: Vec::new(), } } } impl RoomTestRunner { /// Set `Room` - pub fn set_room(mut self, room: Room) -> Self { + pub fn set_room(&mut self, room: Room) -> &mut Self { self.room = Some(room); self } - pub fn add_room_assert(mut self, assert: fn(&Room) -> Result<(), String>) -> Self { - self.room_assertions.push(assert); - self - } - - fn run_room_tests(&mut self) -> Result<(), Vec> { - let mut errs = Vec::new(); - let room = self.room.as_mut().unwrap(); - + fn stream_room_events(&mut self) { + let room = self + .room + .as_mut() + .expect("`Room` must be set use `RoomTestRunner::set_room`"); for event in &self.account_data { match event { // Event::IgnoredUserList(iu) => room.handle_ignored_users(iu), @@ -502,52 +305,29 @@ impl RoomTestRunner { for event in &self.state_events { room.receive_state_event(event); } - - for assert in &mut self.room_assertions { - if let Err(e) = assert(&room) { - errs.push(e); - } - } - if errs.is_empty() { - Ok(()) - } else { - Err(errs) - } } - pub async fn run_test(mut self) { - let (count, errs) = if let Some(_) = &self.room { - (self.room_assertions.len(), self.run_room_tests()) - } else { - panic!("must have either AsyncClient or Room") - }; - - if let Err(errs) = errs { - let err_str = errs.join(&format!("\n\n")); - eprintln!("{}\n{}", Colour::Red.paint("Error: "), err_str); - if !errs.is_empty() { - panic!("{} tests failed", errs.len()); - } else { - println!("{}. {} passed", Colour::Green.paint("Ok"), count); - } - } + pub fn to_room(&mut self) -> &mut Room { + self.stream_room_events(); + self.room.as_mut().unwrap() } } impl ClientTestRunner { - pub fn set_client(mut self, client: AsyncClient) -> Self { + pub fn set_client(&mut self, client: AsyncClient) -> &mut Self { self.client = Some(client); self } - pub fn add_client_assert(mut self, assert: AsyncAssert) -> Self { - self.client_assertions.push(assert); - self - } + async fn stream_client_events(&mut self) { + let mut cli = self + .client + .as_ref() + .expect("`AsyncClient` must be set use `ClientTestRunner::set_client`") + .base_client + .write() + .await; - async fn run_client_tests(&mut self) -> Result<(), Vec> { - let mut errs = Vec::new(); - let mut cli = self.client.as_ref().unwrap().base_client.write().await; let room_id = &self.room_user_id.0; for event in &self.account_data { @@ -573,40 +353,16 @@ impl ClientTestRunner { for event in &self.state_events { cli.receive_joined_state_event(room_id, event).await; } - - for assert in &mut self.client_assertions { - if let Err(e) = assert(self.client.as_ref().unwrap()).await { - errs.push(e); - } - } - if errs.is_empty() { - Ok(()) - } else { - Err(errs) - } } - pub async fn run_test(mut self) { - let (count, errs) = if let Some(_) = &self.client { - (self.client_assertions.len(), self.run_client_tests().await) - } else { - panic!("must have either AsyncClient or Room") - }; - - if let Err(errs) = errs { - let err_str = errs.join(&format!("\n\n")); - eprintln!("{}\n{}", Colour::Red.paint("Error: "), err_str); - if !errs.is_empty() { - panic!("{} tests failed", errs.len()); - } else { - println!("{}. {} passed", Colour::Green.paint("Ok"), count); - } - } + pub async fn to_client(&mut self) -> &mut AsyncClient { + self.stream_client_events().await; + self.client.as_mut().unwrap() } } impl MockTestRunner { - pub fn set_client(mut self, client: AsyncClient) -> Self { + pub fn set_client(&mut self, client: AsyncClient) -> &mut Self { self.client = Some(client); self } @@ -616,48 +372,13 @@ impl MockTestRunner { self } - pub fn add_client_assert(mut self, assert: AsyncAssert) -> Self { - self.client_assertions.push(assert); - self - } - - async fn run_mock_tests(&mut self) -> Result<(), Vec> { - let mut errs = Vec::new(); + pub async fn to_client(&mut self) -> Result<&mut AsyncClient, crate::Error> { self.client .as_mut() .unwrap() .sync(crate::SyncSettings::default()) - .await - .map(|_r| ()) - .map_err(|e| vec![e.to_string()])?; + .await?; - for assert in &mut self.client_assertions { - if let Err(e) = assert(self.client.as_ref().unwrap()).await { - errs.push(e); - } - } - if errs.is_empty() { - Ok(()) - } else { - Err(errs) - } - } - - pub async fn run_test(mut self) { - let (count, errs) = if let Some(_) = &self.mock { - (self.client_assertions.len(), self.run_mock_tests().await) - } else { - panic!("must have either AsyncClient or Room") - }; - - if let Err(errs) = errs { - let err_str = errs.join(&format!("\n\n")); - eprintln!("{}\n{}", Colour::Red.paint("Error: "), err_str); - if !errs.is_empty() { - panic!("{} tests failed", errs.len()); - } else { - println!("{}. {} passed", Colour::Green.paint("Ok"), count); - } - } + Ok(self.client.as_mut().unwrap()) } }