Move over EvTracker to a normal event sender

I considered removing it from the context by default, but the
migration test really wants to have the tracker initialised from the
very first event and not after the context is initialised.  It is
easier for now to leave it hardcoded instead of adding an API to
explicitly require enabling it via the builder.
This commit is contained in:
Floris Bruynooghe
2021-12-15 22:14:08 +01:00
parent 147f5c1e0d
commit 9a02a58273
4 changed files with 68 additions and 63 deletions

View File

@@ -2029,7 +2029,7 @@ CCCB 5AA9 F6E1 141C 9431
alice1 alice1
.evtracker .evtracker
.get_matching(|e| e == EventType::SelfavatarChanged) .get_matching(|e| matches!(e, EventType::SelfavatarChanged))
.await; .await;
// Bob sends a message so that Alice can encrypt to him. // Bob sends a message so that Alice can encrypt to him.
@@ -2059,7 +2059,7 @@ CCCB 5AA9 F6E1 141C 9431
assert!(alice2.get_config(Config::Selfavatar).await?.is_some()); assert!(alice2.get_config(Config::Selfavatar).await?.is_some());
alice2 alice2
.evtracker .evtracker
.get_matching(|e| e == EventType::SelfavatarChanged) .get_matching(|e| matches!(e, EventType::SelfavatarChanged))
.await; .await;
Ok(()) Ok(())

View File

@@ -4684,15 +4684,19 @@ Second thread."#;
let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?; let chat = chat::Chat::load_from_db(&t, msg.chat_id).await?;
assert!(chat.is_contact_request()); assert!(chat.is_contact_request());
let duration = std::time::Duration::from_secs(1);
loop { loop {
let event = async_std::future::timeout(duration, t.evtracker.recv()).await??; let event = t
.evtracker
if let EventType::IncomingMsg { chat_id, msg_id } = &event { .get_matching(|evt| matches!(evt, EventType::IncomingMsg { .. }))
assert_eq!(msg.chat_id, *chat_id); .await;
assert_eq!(msg.id, *msg_id); match event {
EventType::IncomingMsg { chat_id, msg_id } => {
assert_eq!(msg.chat_id, chat_id);
assert_eq!(msg.id, msg_id);
return Ok(()); return Ok(());
} }
_ => unreachable!(),
}
} }
} }

View File

@@ -829,18 +829,25 @@ mod tests {
assert!(!disable_server_delete); assert!(!disable_server_delete);
assert!(!recode_avatar); assert!(!recode_avatar);
info!(&t, "test_migration_flags: XXX"); info!(&t, "test_migration_flags: XXX END MARKER");
loop { loop {
if let EventType::Info(info) = t.evtracker.recv().await.unwrap() { let evt = t
.evtracker
.get_matching(|evt| matches!(evt, EventType::Info(_)))
.await;
match evt {
EventType::Info(msg) => {
assert!( assert!(
!info.contains("[migration]"), !msg.contains("[migration]"),
"Migrations were run twice, you probably forgot to update the db version" "Migrations were run twice, you probably forgot to update the db version"
); );
if info.contains("test_migration_flags: XXX") { if msg.contains("test_migration_flags: XXX END MARKER") {
break; break;
} }
} }
_ => unreachable!(),
}
} }
Ok(()) Ok(())

View File

@@ -3,7 +3,6 @@
//! This private module is only compiled for test runs. //! This private module is only compiled for test runs.
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use std::panic; use std::panic;
use std::str::FromStr; use std::str::FromStr;
@@ -13,6 +12,7 @@ use std::time::{Duration, Instant};
use ansi_term::Color; use ansi_term::Color;
use async_std::channel::{self, Receiver, Sender}; use async_std::channel::{self, Receiver, Sender};
use async_std::path::PathBuf; use async_std::path::PathBuf;
use async_std::prelude::*;
use async_std::sync::{Arc, RwLock}; use async_std::sync::{Arc, RwLock};
use async_std::task; use async_std::task;
use chat::ChatItem; use chat::ChatItem;
@@ -106,10 +106,11 @@ impl TestContextBuilder {
/// ///
/// The temporary directory can be used to store the SQLite database, /// The temporary directory can be used to store the SQLite database,
/// see e.g. [test_context] which does this. /// see e.g. [test_context] which does this.
#[derive(Debug)]
pub struct TestContext { pub struct TestContext {
pub ctx: Context, pub ctx: Context,
pub dir: TempDir, pub dir: TempDir,
pub evtracker: EvTracker, pub evtracker: EventTracker,
/// Channels which should receive events from this context. /// Channels which should receive events from this context.
event_senders: Arc<RwLock<Vec<Sender<Event>>>>, event_senders: Arc<RwLock<Vec<Sender<Event>>>>,
/// Receives panics from sinks ("sink" means "event handler" here) /// Receives panics from sinks ("sink" means "event handler" here)
@@ -126,16 +127,6 @@ pub struct TestContext {
log_sink: Option<LogSink>, log_sink: Option<LogSink>,
} }
impl fmt::Debug for TestContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TestContext")
.field("ctx", &self.ctx)
.field("dir", &self.dir)
.field("event_sinks", &String::from("Vec<EventSink>"))
.finish()
}
}
impl TestContext { impl TestContext {
/// Returns the builder to have more control over creating the context. /// Returns the builder to have more control over creating the context.
pub fn builder() -> TestContextBuilder { pub fn builder() -> TestContextBuilder {
@@ -198,10 +189,10 @@ impl TestContext {
} }
}; };
let event_senders = Arc::new(RwLock::new(vec![log_sender])); let (evtracker_sender, evtracker_receiver) = channel::unbounded();
let event_senders = Arc::new(RwLock::new(vec![log_sender, evtracker_sender]));
let senders = Arc::clone(&event_senders); let senders = Arc::clone(&event_senders);
let (poison_sender, poison_receiver) = channel::bounded(1); let (poison_sender, poison_receiver) = channel::bounded(1);
let (evtracker_sender, evtracker_receiver) = channel::unbounded();
task::spawn(async move { task::spawn(async move {
// Make sure that the test fails if there is a panic on this thread here // Make sure that the test fails if there is a panic on this thread here
@@ -221,19 +212,18 @@ impl TestContext {
{ {
let sinks = senders.read().await; let sinks = senders.read().await;
for sender in sinks.iter() { for sender in sinks.iter() {
// Best effort, don't block because someone wanted to use a oneshot // Don't block because someone wanted to use a oneshot receiver, use
// receiver. // an unbounded channel if you want all events.
sender.try_send(event.clone()).ok(); sender.try_send(event.clone()).ok();
} }
} }
evtracker_sender.send(event.typ).await.ok();
} }
}); });
Self { Self {
ctx, ctx,
dir, dir,
evtracker: EvTracker(evtracker_receiver), evtracker: EventTracker(evtracker_receiver),
event_senders, event_senders,
poison_receiver, poison_receiver,
log_sink, log_sink,
@@ -573,6 +563,7 @@ impl Drop for TestContext {
/// ///
/// To use this create an instance using [`LogSink::create`] and then use the /// To use this create an instance using [`LogSink::create`] and then use the
/// [`TestContextBuilder::with_log_sink`]. /// [`TestContextBuilder::with_log_sink`].
#[derive(Debug)]
pub struct LogSink { pub struct LogSink {
events: Receiver<Event>, events: Receiver<Event>,
} }
@@ -662,40 +653,43 @@ pub fn bob_keypair() -> KeyPair {
} }
} }
pub struct EvTracker(Receiver<EventType>); /// Utility to help wait for and retrieve events.
///
/// This buffers the events in order they are emitted. This allows consuming events in
/// order while looking for the right events using the provided methods.
///
/// The methods only return [`EventType`] rather than the full [`Event`] since it can only
/// be attached to a single [`TestContext`] and therefore the context is already known as
/// you will be accessing it as [`TestContext::evtracker`].
#[derive(Debug)]
pub struct EventTracker(Receiver<Event>);
impl EvTracker { impl EventTracker {
pub async fn get_info_contains(&self, s: &str) -> EventType { /// Consumes emitted events returning the first matching one.
///
/// If no matching events are ready this will wait for new events to arrive and time out
/// after 10 seconds.
pub async fn get_matching<F: Fn(&EventType) -> bool>(&self, event_matcher: F) -> EventType {
async move {
loop { loop {
let event = self.0.recv().await.unwrap(); let event = self.0.recv().await.unwrap();
if let EventType::Info(i) = &event { if event_matcher(&event.typ) {
if i.contains(s) { return event.typ;
return event;
} }
} }
} }
} .timeout(Duration::from_secs(10))
pub async fn get_matching<F: Fn(EventType) -> bool>(&self, event_matcher: F) -> EventType {
const TIMEOUT: Duration = Duration::from_secs(20);
loop {
let event = async_std::future::timeout(TIMEOUT, self.recv())
.await .await
.unwrap() .expect("timeout waiting for event match")
.unwrap();
if event_matcher(event.clone()) {
return event;
}
}
}
} }
impl Deref for EvTracker { /// Consumes events looking for an [`EventType::Info`] with substring matching.
type Target = Receiver<EventType>; pub async fn get_info_contains(&self, s: &str) -> EventType {
fn deref(&self) -> &Self::Target { self.get_matching(|evt| match evt {
&self.0 EventType::Info(ref msg) => msg.contains(s),
_ => false,
})
.await
} }
} }