From ab7aa68c5bb3ef3635ae1182c7ee0d9e8fbea837 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 15 May 2023 11:01:33 +0200 Subject: [PATCH 1/8] Fix lints --- crates/matrix-sdk/src/sliding_sync/room.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk/src/sliding_sync/room.rs b/crates/matrix-sdk/src/sliding_sync/room.rs index 53e73a1b3cb..af7b106e9b3 100644 --- a/crates/matrix-sdk/src/sliding_sync/room.rs +++ b/crates/matrix-sdk/src/sliding_sync/room.rs @@ -234,7 +234,7 @@ impl SlidingSyncRoom { *self.inner.state.write().unwrap() = state; } - fn timeline_queue(&self) -> std::sync::RwLockReadGuard> { + fn timeline_queue(&self) -> std::sync::RwLockReadGuard<'_, Vector> { self.inner.timeline_queue.read().unwrap() } } @@ -452,9 +452,9 @@ mod tests { test_room_name { name() = None; receives room_response!({"name": "gordon"}); - _ = Some("gordon".to_string()); + _ = Some("gordon".to_owned()); receives nothing; - _ = Some("gordon".to_string()); + _ = Some("gordon".to_owned()); } test_room_is_dm { @@ -521,7 +521,7 @@ mod tests { new_room(room_id!("!foo:bar.org"), room_response!({"prev_batch": "t111_222_333"})) .await; - assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_string())); + assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned())); } // Some value when updating. @@ -531,10 +531,10 @@ mod tests { assert_eq!(room.inner.prev_batch(), None); room.update(room_response!({"prev_batch": "t111_222_333"}), vec![]); - assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_string())); + assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned())); room.update(room_response!({}), vec![]); - assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_string())); + assert_eq!(room.inner.prev_batch(), Some("t111_222_333".to_owned())); } } From ee87ec7b46ccbbe99ee6492c600c2cc3b2486968 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 15 May 2023 12:29:36 +0200 Subject: [PATCH 2/8] Improve logs for pagination --- crates/matrix-sdk/src/room/common.rs | 20 +++++++++++++++++--- crates/matrix-sdk/src/room/timeline/mod.rs | 6 +++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk/src/room/common.rs b/crates/matrix-sdk/src/room/common.rs index 06384b93883..291305985a7 100644 --- a/crates/matrix-sdk/src/room/common.rs +++ b/crates/matrix-sdk/src/room/common.rs @@ -1,10 +1,11 @@ -use std::{borrow::Borrow, collections::BTreeMap, ops::Deref, sync::Arc}; +use std::{borrow::Borrow, collections::BTreeMap, fmt, ops::Deref, sync::Arc}; use matrix_sdk_base::{ deserialized_responses::{MembersResponse, TimelineEvent}, store::StateStoreExt, RoomMemberships, StateChanges, }; +use matrix_sdk_common::debug::DebugStructExt; #[cfg(feature = "e2e-encryption")] use ruma::events::{ room::encrypted::OriginalSyncRoomEncryptedEvent, AnySyncMessageLikeEvent, AnySyncTimelineEvent, @@ -46,7 +47,7 @@ use ruma::{ }; use serde::de::DeserializeOwned; use tokio::sync::Mutex; -use tracing::debug; +use tracing::{debug, instrument}; #[cfg(feature = "experimental-timeline")] use super::timeline::Timeline; @@ -201,6 +202,7 @@ impl Common { /// assert!(room.messages(options).await.is_ok()); /// # }); /// ``` + #[instrument(skip_all, fields(room_id = ?self.inner.room_id(), ?options))] pub async fn messages(&self, options: MessagesOptions) -> Result { let room_id = self.inner.room_id(); let request = options.into_request(room_id); @@ -1076,7 +1078,6 @@ impl Common { /// See that method and /// /// for details. -#[derive(Debug)] #[non_exhaustive] pub struct MessagesOptions { /// The token to start returning events from. @@ -1152,3 +1153,16 @@ impl MessagesOptions { }) } } + +impl fmt::Debug for MessagesOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { from, to, dir, limit, filter } = self; + + let mut s = f.debug_struct("MessagesOptions"); + s.maybe_field("from", from).maybe_field("to", to).field("dir", dir).field("limit", limit); + if !filter.is_empty() { + s.field("filter", filter); + } + s.finish() + } +} diff --git a/crates/matrix-sdk/src/room/timeline/mod.rs b/crates/matrix-sdk/src/room/timeline/mod.rs index 5a0f4a1b680..54133189345 100644 --- a/crates/matrix-sdk/src/room/timeline/mod.rs +++ b/crates/matrix-sdk/src/room/timeline/mod.rs @@ -109,8 +109,8 @@ impl Timeline { } /// Add more events to the start of the timeline. - #[instrument(skip_all, fields(initial_pagination_size, room_id = ?self.room().room_id()))] - pub async fn paginate_backwards(&self, mut opts: PaginationOptions<'_>) -> Result<()> { + #[instrument(skip_all, fields(room_id = ?self.room().room_id(), ?options))] + pub async fn paginate_backwards(&self, mut options: PaginationOptions<'_>) -> Result<()> { let mut start_lock = self.start_token.lock().await; if start_lock.is_none() && self.inner.items().await.front().map_or(false, |item| item.is_timeline_start()) @@ -124,7 +124,7 @@ impl Timeline { let mut from = start_lock.clone(); let mut outcome = PaginationOutcome::new(); - while let Some(limit) = opts.next_event_limit(outcome) { + while let Some(limit) = options.next_event_limit(outcome) { let messages = self .room() .messages(assign!(MessagesOptions::backward(), { From 3449dad89b733c3aa9a6186feae66747d7171868 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 9 May 2023 16:58:18 +0100 Subject: [PATCH 3/8] feat(bindings): expose getting member by id Allow for retrieving a single room member by their ID. --- bindings/matrix-sdk-ffi/src/room.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index e518fc4f0df..39aaab48fcb 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -187,12 +187,22 @@ impl Room { }) } + pub fn member(&self, user_id: String) -> Result, ClientError> { + let room = self.room.clone(); + let user_id = user_id; + RUNTIME.block_on(async move { + let user_id = UserId::parse(&*user_id).context("Invalid user id.")?; + let member = room.get_member(&user_id).await?.context("No user found")?; + Ok(Arc::new(RoomMember::new(member))) + }) + } + pub fn member_avatar_url(&self, user_id: String) -> Result, ClientError> { let room = self.room.clone(); let user_id = user_id; RUNTIME.block_on(async move { - let user_id = <&UserId>::try_from(&*user_id).context("Invalid user id.")?; - let member = room.get_member(user_id).await?.context("No user found")?; + let user_id = UserId::parse(&*user_id).context("Invalid user id.")?; + let member = room.get_member(&user_id).await?.context("No user found")?; let avatar_url_string = member.avatar_url().map(|m| m.to_string()); Ok(avatar_url_string) }) @@ -202,8 +212,8 @@ impl Room { let room = self.room.clone(); let user_id = user_id; RUNTIME.block_on(async move { - let user_id = <&UserId>::try_from(&*user_id).context("Invalid user id.")?; - let member = room.get_member(user_id).await?.context("No user found")?; + let user_id = UserId::parse(&*user_id).context("Invalid user id.")?; + let member = room.get_member(&user_id).await?.context("No user found")?; let avatar_url_string = member.display_name().map(|m| m.to_owned()); Ok(avatar_url_string) }) From 18954a6ba57a6bfb5a42a58e65e5935a79cd40fa Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Mon, 15 May 2023 15:02:01 +0100 Subject: [PATCH 4/8] crypto-js: fix body of `SignatureUploadRequest` (#1917) Currently, the `body` of a `SignatureUploadRequest` includes a spurious `signed_keys: {...}` property in which the actual content is wrapped. Fix that. --- bindings/matrix-sdk-crypto-js/CHANGELOG.md | 1 + bindings/matrix-sdk-crypto-js/src/requests.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bindings/matrix-sdk-crypto-js/CHANGELOG.md b/bindings/matrix-sdk-crypto-js/CHANGELOG.md index 92f008f94d6..4a5dc9d6a39 100644 --- a/bindings/matrix-sdk-crypto-js/CHANGELOG.md +++ b/bindings/matrix-sdk-crypto-js/CHANGELOG.md @@ -2,6 +2,7 @@ - Extend `OlmDevice.markRequestAsSent` to accept responses to `SigningKeysUploadRequest`s. +- Fix the body of `SignatureUploadRequest`s to match the spec. # v0.1.0-alpha.8 diff --git a/bindings/matrix-sdk-crypto-js/src/requests.rs b/bindings/matrix-sdk-crypto-js/src/requests.rs index 608e8e3f584..aeba6ffc24c 100644 --- a/bindings/matrix-sdk-crypto-js/src/requests.rs +++ b/bindings/matrix-sdk-crypto-js/src/requests.rs @@ -194,19 +194,19 @@ pub struct SignatureUploadRequest { #[wasm_bindgen(readonly)] pub id: Option, - /// A JSON-encoded string containing the rest of the payload: `signed_keys`. + /// A JSON-encoded string containing the payload of the request /// /// It represents the body of the HTTP request. - #[wasm_bindgen(readonly)] - pub body: JsString, + #[wasm_bindgen(readonly, js_name = "body")] + pub signed_keys: JsString, } #[wasm_bindgen] impl SignatureUploadRequest { /// Create a new `SignatureUploadRequest`. #[wasm_bindgen(constructor)] - pub fn new(id: JsString, body: JsString) -> SignatureUploadRequest { - Self { id: Some(id), body } + pub fn new(id: JsString, signed_keys: JsString) -> SignatureUploadRequest { + Self { id: Some(id), signed_keys } } /// Get its request type. @@ -417,7 +417,7 @@ request!(KeysUploadRequest from OriginalKeysUploadRequest groups device_keys, on request!(KeysQueryRequest from OriginalKeysQueryRequest groups timeout, device_keys, token); request!(KeysClaimRequest from OriginalKeysClaimRequest groups timeout, one_time_keys); request!(ToDeviceRequest from OriginalToDeviceRequest extracts event_type: string, txn_id: string and groups messages); -request!(SignatureUploadRequest from OriginalSignatureUploadRequest groups signed_keys); +request!(SignatureUploadRequest from OriginalSignatureUploadRequest extracts signed_keys: json); request!(RoomMessageRequest from OriginalRoomMessageRequest extracts room_id: string, txn_id: string, event_type: event_type, content: json); request!(KeysBackupRequest from OriginalKeysBackupRequest groups rooms); From 3928259bb5b45d6fb201b0b8a0859c9aa29f2f90 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 11 May 2023 17:36:29 +0200 Subject: [PATCH 5/8] bench: add restore session benchmark Signed-off-by: Benjamin Bouvier --- Cargo.lock | 2 + benchmarks/Cargo.toml | 8 +- benchmarks/benches/store_bench.rs | 124 ++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 benchmarks/benches/store_bench.rs diff --git a/Cargo.lock b/Cargo.lock index bfd602696e0..8931283dfef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -537,6 +537,8 @@ name = "benchmarks" version = "1.0.0" dependencies = [ "criterion", + "matrix-sdk", + "matrix-sdk-base", "matrix-sdk-crypto", "matrix-sdk-sled", "matrix-sdk-sqlite", diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index fe065d358e0..a39272db690 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -9,10 +9,12 @@ publish = false [dependencies] criterion = { version = "0.4.0", features = ["async", "async_tokio", "html_reports"] } +matrix-sdk-base = { path = "../crates/matrix-sdk-base" } matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.6.0"} matrix-sdk-sqlite = { path = "../crates/matrix-sdk-sqlite", version = "0.1.0", default-features = false, features = ["crypto-store"] } -matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", default-features = false, features = ["crypto-store"] } +matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", features = ["crypto-store"] } matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.6.0"} +matrix-sdk = { path = "../crates/matrix-sdk" } ruma = { workspace = true } serde_json = { workspace = true } tempfile = "3.3.0" @@ -24,3 +26,7 @@ pprof = { version = "0.11.0", features = ["flamegraph", "criterion"] } [[bench]] name = "crypto_bench" harness = false + +[[bench]] +name = "store_bench" +harness = false diff --git a/benchmarks/benches/store_bench.rs b/benchmarks/benches/store_bench.rs new file mode 100644 index 00000000000..203c8c423f6 --- /dev/null +++ b/benchmarks/benches/store_bench.rs @@ -0,0 +1,124 @@ +use criterion::*; +use matrix_sdk::{config::StoreConfig, Client, RoomInfo, RoomState, Session, StateChanges}; +use matrix_sdk_base::{store::MemoryStore, StateStore as _}; +use matrix_sdk_sled::SledStateStore; +use matrix_sdk_sqlite::SqliteStateStore; +use ruma::{device_id, user_id, RoomId}; +use tokio::runtime::Builder; + +fn criterion() -> Criterion { + #[cfg(target_os = "linux")] + let criterion = Criterion::default().with_profiler(pprof::criterion::PProfProfiler::new( + 100, + pprof::criterion::Output::Flamegraph(None), + )); + + #[cfg(not(target_os = "linux"))] + let criterion = Criterion::default(); + + criterion +} + +/// Number of joined rooms in the benchmark. +const NUM_JOINED_ROOMS: usize = 10000; + +/// Number of stripped rooms in the benchmark. +const NUM_STRIPPED_JOINED_ROOMS: usize = 10000; + +pub fn restore_session(c: &mut Criterion) { + let runtime = Builder::new_multi_thread().build().expect("Can't create runtime"); + + // Create a fake list of changes, and a session to recover from. + let mut changes = StateChanges::default(); + + for i in 0..NUM_JOINED_ROOMS { + let room_id = RoomId::parse(format!("!room{i}:example.com")).unwrap().to_owned(); + changes.add_room(RoomInfo::new(&room_id, RoomState::Joined)); + } + + for i in 0..NUM_STRIPPED_JOINED_ROOMS { + let room_id = RoomId::parse(format!("!strippedroom{i}:example.com")).unwrap().to_owned(); + changes.add_stripped_room(RoomInfo::new(&room_id, RoomState::Joined)); + } + + let session = Session { + access_token: "OHEY".to_owned(), + refresh_token: None, + user_id: user_id!("@somebody:example.com").to_owned(), + device_id: device_id!("DEVICE_ID").to_owned(), + }; + + // Start the benchmark. + + let mut group = c.benchmark_group("Client reload"); + group.throughput(Throughput::Elements(100)); + + const NAME: &str = "restore a session"; + + // Memory + let mem_store = MemoryStore::new(); + runtime.block_on(mem_store.save_changes(&changes)).expect("initial filling of mem failed"); + + group.bench_with_input(BenchmarkId::new("memory store", NAME), &mem_store, |b, store| { + b.to_async(&runtime).iter(|| async { + let client = Client::builder() + .homeserver_url("https://matrix.example.com") + .store_config(StoreConfig::new().state_store(store.clone())) + .build() + .await + .expect("Can't build client"); + client.restore_session(session.clone()).await.expect("couldn't restore session"); + }) + }); + + // Sled + let sled_path = tempfile::tempdir().unwrap().path().to_path_buf(); + let sled_store = + SledStateStore::builder().path(sled_path).build().expect("Can't create sled store"); + runtime.block_on(sled_store.save_changes(&changes)).expect("initial filling of sled failed"); + + group.bench_with_input(BenchmarkId::new("sled store", NAME), &sled_store, |b, store| { + b.to_async(&runtime).iter(|| async { + let client = Client::builder() + .homeserver_url("https://matrix.example.com") + .store_config(StoreConfig::new().state_store(store.clone())) + .build() + .await + .expect("Can't build client"); + client.restore_session(session.clone()).await.expect("couldn't restore session"); + }) + }); + + // Sqlite + let sqlite_dir = tempfile::tempdir().unwrap(); + let sqlite_store = runtime.block_on(SqliteStateStore::open(sqlite_dir.path(), None)).unwrap(); + runtime + .block_on(sqlite_store.save_changes(&changes)) + .expect("initial filling of sqlite failed"); + + group.bench_with_input(BenchmarkId::new("sqlite store", NAME), &sqlite_store, |b, store| { + b.to_async(&runtime).iter(|| async { + let client = Client::builder() + .homeserver_url("https://matrix.example.com") + .store_config(StoreConfig::new().state_store(store.clone())) + .build() + .await + .expect("Can't build client"); + client.restore_session(session.clone()).await.expect("couldn't restore session"); + }) + }); + + { + let _guard = runtime.enter(); + drop(sqlite_store); + } + + group.finish() +} + +criterion_group! { + name = benches; + config = criterion(); + targets = restore_session +} +criterion_main!(benches); From b6302aca5c8b8123181b41dfbf4fe58db76dcb40 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 15 May 2023 16:53:32 +0200 Subject: [PATCH 6/8] bench: add benchmarks for encrypted stores too Signed-off-by: Benjamin Bouvier --- benchmarks/benches/store_bench.rs | 105 ++++++++++++++++++------------ 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/benchmarks/benches/store_bench.rs b/benchmarks/benches/store_bench.rs index 203c8c423f6..7bced77ede3 100644 --- a/benchmarks/benches/store_bench.rs +++ b/benchmarks/benches/store_bench.rs @@ -71,46 +71,71 @@ pub fn restore_session(c: &mut Criterion) { }) }); - // Sled - let sled_path = tempfile::tempdir().unwrap().path().to_path_buf(); - let sled_store = - SledStateStore::builder().path(sled_path).build().expect("Can't create sled store"); - runtime.block_on(sled_store.save_changes(&changes)).expect("initial filling of sled failed"); - - group.bench_with_input(BenchmarkId::new("sled store", NAME), &sled_store, |b, store| { - b.to_async(&runtime).iter(|| async { - let client = Client::builder() - .homeserver_url("https://matrix.example.com") - .store_config(StoreConfig::new().state_store(store.clone())) - .build() - .await - .expect("Can't build client"); - client.restore_session(session.clone()).await.expect("couldn't restore session"); - }) - }); - - // Sqlite - let sqlite_dir = tempfile::tempdir().unwrap(); - let sqlite_store = runtime.block_on(SqliteStateStore::open(sqlite_dir.path(), None)).unwrap(); - runtime - .block_on(sqlite_store.save_changes(&changes)) - .expect("initial filling of sqlite failed"); - - group.bench_with_input(BenchmarkId::new("sqlite store", NAME), &sqlite_store, |b, store| { - b.to_async(&runtime).iter(|| async { - let client = Client::builder() - .homeserver_url("https://matrix.example.com") - .store_config(StoreConfig::new().state_store(store.clone())) - .build() - .await - .expect("Can't build client"); - client.restore_session(session.clone()).await.expect("couldn't restore session"); - }) - }); - - { - let _guard = runtime.enter(); - drop(sqlite_store); + for encryption_password in [None, Some("hunter2")] { + let encrypted_suffix = if encryption_password.is_some() { "encrypted" } else { "clear" }; + + // Sled + let sled_path = tempfile::tempdir().unwrap().path().to_path_buf(); + let mut sled_store_builder = SledStateStore::builder().path(sled_path); + if let Some(password) = encryption_password { + sled_store_builder = sled_store_builder.passphrase(password.to_owned()); + } + let sled_store = sled_store_builder.build().expect("Can't create sled store"); + runtime + .block_on(sled_store.save_changes(&changes)) + .expect("initial filling of sled failed"); + + group.bench_with_input( + BenchmarkId::new(format!("sled store {encrypted_suffix}"), NAME), + &sled_store, + |b, store| { + b.to_async(&runtime).iter(|| async { + let client = Client::builder() + .homeserver_url("https://matrix.example.com") + .store_config(StoreConfig::new().state_store(store.clone())) + .build() + .await + .expect("Can't build client"); + client + .restore_session(session.clone()) + .await + .expect("couldn't restore session"); + }) + }, + ); + + // Sqlite + let sqlite_dir = tempfile::tempdir().unwrap(); + let sqlite_store = runtime + .block_on(SqliteStateStore::open(sqlite_dir.path(), encryption_password)) + .unwrap(); + runtime + .block_on(sqlite_store.save_changes(&changes)) + .expect("initial filling of sqlite failed"); + + group.bench_with_input( + BenchmarkId::new(format!("sqlite store {encrypted_suffix}"), NAME), + &sqlite_store, + |b, store| { + b.to_async(&runtime).iter(|| async { + let client = Client::builder() + .homeserver_url("https://matrix.example.com") + .store_config(StoreConfig::new().state_store(store.clone())) + .build() + .await + .expect("Can't build client"); + client + .restore_session(session.clone()) + .await + .expect("couldn't restore session"); + }) + }, + ); + + { + let _guard = runtime.enter(); + drop(sqlite_store); + } } group.finish() From 549edeb73b4d2c32843eab0de642bad28c9b3d08 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 15 May 2023 15:24:49 +0200 Subject: [PATCH 7/8] sdk: Instrument sliding response handling task --- crates/matrix-sdk/src/sliding_sync/mod.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk/src/sliding_sync/mod.rs b/crates/matrix-sdk/src/sliding_sync/mod.rs index 21fffc98390..c0df3c70137 100644 --- a/crates/matrix-sdk/src/sliding_sync/mod.rs +++ b/crates/matrix-sdk/src/sliding_sync/mod.rs @@ -493,7 +493,7 @@ impl SlidingSync { // Spawn a new future to ensure that the code inside this future cannot be // cancelled if this method is cancelled. - spawn(async move { + let fut = async move { debug!("Sliding Sync response handling starts"); // In case the task running this future is detached, we must @@ -503,14 +503,19 @@ impl SlidingSync { match &response.txn_id { None => { - error!(stream_id, "Sliding Sync has received an unexpected response: `txn_id` must match `stream_id`; it's missing"); + error!( + stream_id, + "Sliding Sync has received an unexpected response: \ + `txn_id` must match `stream_id`; it's missing" + ); } Some(txn_id) if txn_id != &stream_id => { error!( stream_id, txn_id, - "Sliding Sync has received an unexpected response: `txn_id` must match `stream_id`; they differ" + "Sliding Sync has received an unexpected response: \ + `txn_id` must match `stream_id`; they differ" ); } @@ -537,7 +542,8 @@ impl SlidingSync { debug!("Sliding Sync response has been fully handled"); Ok(Some(updates)) - }).await.unwrap() + }; + spawn(fut.instrument(Span::current())).await.unwrap() } /// Create a _new_ Sliding Sync stream. From c404e378a265ebc3292f9e332c3d6eb9d356ac29 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 15 May 2023 15:33:22 +0200 Subject: [PATCH 8/8] test: sliding sync list fields reloaded from the cache are observable in streams Signed-off-by: Benjamin Bouvier --- crates/matrix-sdk/src/sliding_sync/cache.rs | 27 ++++++++++++++++++- .../src/sliding_sync/list/builder.rs | 6 ++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/matrix-sdk/src/sliding_sync/cache.rs b/crates/matrix-sdk/src/sliding_sync/cache.rs index 2fb4dae947f..40768998f53 100644 --- a/crates/matrix-sdk/src/sliding_sync/cache.rs +++ b/crates/matrix-sdk/src/sliding_sync/cache.rs @@ -210,7 +210,10 @@ pub(super) async fn restore_sliding_sync_state( #[cfg(test)] mod tests { + use std::sync::{Arc, RwLock}; + use futures::executor::block_on; + use futures_util::StreamExt; use url::Url; use super::*; @@ -274,11 +277,20 @@ mod tests { // Create a new `SlidingSync`, and it should be read from the cache. { + let max_number_of_room_stream = Arc::new(RwLock::new(None)); + let cloned_stream = max_number_of_room_stream.clone(); let sliding_sync = client .sliding_sync() .await .storage_key(Some("hello".to_owned())) - .add_list(SlidingSyncList::builder("list_foo")) + .add_list(SlidingSyncList::builder("list_foo").once_built(move |list| { + // In the `once_built()` handler, nothing has been read from the cache yet. + assert_eq!(list.maximum_number_of_rooms(), None); + + let mut stream = cloned_stream.write().unwrap(); + *stream = Some(list.maximum_number_of_rooms_stream()); + list + })) .build() .await?; @@ -290,6 +302,19 @@ mod tests { assert_eq!(list_foo.maximum_number_of_rooms(), Some(42)); } + // The maximum number of rooms reloaded from the cache should have been + // published. + { + let mut stream = max_number_of_room_stream + .write() + .unwrap() + .take() + .expect("stream must be set"); + let initial_max_number_of_rooms = + stream.next().await.expect("stream must have emitted something"); + assert_eq!(initial_max_number_of_rooms, Some(42)); + } + // Clean the cache. clean_storage(&client, "hello", &sliding_sync.inner.lists.read().unwrap()).await; } diff --git a/crates/matrix-sdk/src/sliding_sync/list/builder.rs b/crates/matrix-sdk/src/sliding_sync/list/builder.rs index d31233650fb..37cedfa267c 100644 --- a/crates/matrix-sdk/src/sliding_sync/list/builder.rs +++ b/crates/matrix-sdk/src/sliding_sync/list/builder.rs @@ -75,7 +75,11 @@ impl SlidingSyncListBuilder { } } - /// foo + /// Runs a callback once the list has been built. + /// + /// If the list was cached, then the cached fields won't be available in + /// this callback. Use the streams to get published versions of the + /// cached fields, once they've been set. pub fn once_built(mut self, callback: C) -> Self where C: Fn(SlidingSyncList) -> SlidingSyncList + Send + Sync + 'static,