Skip to main content

matrix_sdk/latest_events/
mod.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Latest Events API provides a lazy, reactive and efficient way to compute
16//! the latest event for a room or a thread.
17//!
18//! The latest event represents the last displayable and relevant event a room
19//! or a thread has received. It is usually displayed in a _summary_, e.g.
20//! below the room title in a room list.
21//!
22//! The entry point is [`LatestEvents`]. It is preferable to get a reference to
23//! it from [`Client::latest_events`][crate::Client::latest_events], which
24//! already plugs everything to build it. [`LatestEvents`] is using the
25//! [`EventCache`] and the [`SendQueue`] to respectively get known remote events
26//! (i.e. synced from the server), or local events (i.e. ones being sent).
27//!
28//! ## Laziness
29//!
30//! [`LatestEvents`] is lazy, it means that, despite [`LatestEvents`] is
31//! listening to all [`EventCache`] or [`SendQueue`] updates, it will only do
32//! something if one is expected to get the latest event for a particular room
33//! or a particular thread. Concretely, it means that until
34//! [`LatestEvents::listen_to_room`] is called for a particular room, no latest
35//! event will ever be computed for that room (and similarly with
36//! [`LatestEvents::listen_to_thread`]).
37//!
38//! If one is no longer interested to get the latest event for a particular room
39//! or thread, the [`LatestEvents::forget_room`] and
40//! [`LatestEvents::forget_thread`] methods must be used.
41//!
42//! ## Reactive
43//!
44//! [`LatestEvents`] is designed to be reactive. Using
45//! [`LatestEvents::listen_and_subscribe_to_room`] will provide a
46//! [`Subscriber`], which brings all the tooling to get the current value or the
47//! future values with a stream.
48
49mod error;
50mod latest_event;
51mod room_latest_events;
52
53use std::{
54    collections::HashMap,
55    ops::{ControlFlow, DerefMut, Not},
56    sync::Arc,
57};
58
59pub use error::LatestEventsError;
60use eyeball::{AsyncLock, Subscriber};
61#[cfg(feature = "testing")]
62#[doc(hidden)]
63pub use latest_event::bench_new_remote;
64pub(crate) use latest_event::filter_timeline_event;
65use latest_event::{LatestEvent, With};
66pub use latest_event::{LatestEventValue, LocalLatestEventValue, RemoteLatestEventValue};
67use matrix_sdk_base::{RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, timer};
68use matrix_sdk_common::executor::{AbortOnDrop, JoinHandleExt as _, spawn};
69use room_latest_events::{RoomLatestEvents, RoomLatestEventsWriteGuard};
70use ruma::{EventId, OwnedRoomId, RoomId};
71use tokio::{
72    select,
73    sync::{RwLock, RwLockReadGuard, RwLockWriteGuard, broadcast, mpsc},
74};
75use tracing::{info, warn};
76
77use crate::{
78    client::WeakClient,
79    event_cache::{EventCache, RoomEventCacheGenericUpdate},
80    room::WeakRoom,
81    send_queue::{RoomSendQueueUpdate, SendQueue, SendQueueUpdate},
82};
83
84/// The entry point to fetch the [`LatestEventValue`] for rooms or threads.
85#[derive(Clone, Debug)]
86pub struct LatestEvents {
87    state: Arc<LatestEventsState>,
88}
89
90/// The state of [`LatestEvents`].
91#[derive(Debug)]
92struct LatestEventsState {
93    /// All the registered rooms, i.e. rooms the latest events are computed for.
94    registered_rooms: Arc<RegisteredRooms>,
95
96    /// The task handle of the [`listen_to_updates_task`].
97    _listen_task_handle: AbortOnDrop<()>,
98
99    /// The task handle of the [`compute_latest_events_task`].
100    _computation_task_handle: AbortOnDrop<()>,
101}
102
103impl LatestEvents {
104    /// Create a new [`LatestEvents`].
105    pub(crate) fn new(
106        weak_client: WeakClient,
107        event_cache: EventCache,
108        send_queue: SendQueue,
109        room_info_updates: broadcast::Receiver<RoomInfoNotableUpdate>,
110    ) -> Self {
111        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
112
113        let registered_rooms =
114            Arc::new(RegisteredRooms::new(weak_client, &event_cache, &latest_event_queue_sender));
115
116        // The task listening to the event cache, the send queue, and the room
117        // infos updates.
118        //
119        // Note: we spawn the send queue observer here, instead of holding onto
120        // the `SendQueue` in the `listen_to_updates_task`, which would
121        // lead to a `Client` leak (since it's a field of
122        // the `SendQueue`).
123        let send_queue_generic_updates_subscriber = send_queue.subscribe();
124        let listen_task_handle = spawn(listen_to_updates_task(
125            registered_rooms.clone(),
126            event_cache,
127            send_queue_generic_updates_subscriber,
128            room_info_updates,
129            latest_event_queue_sender,
130        ))
131        .abort_on_drop();
132
133        // The task computing the new latest events.
134        let computation_task_handle = spawn(compute_latest_events_task(
135            registered_rooms.clone(),
136            latest_event_queue_receiver,
137        ))
138        .abort_on_drop();
139
140        Self {
141            state: Arc::new(LatestEventsState {
142                registered_rooms,
143                _listen_task_handle: listen_task_handle,
144                _computation_task_handle: computation_task_handle,
145            }),
146        }
147    }
148
149    /// Start listening to updates (if not already) for a particular room.
150    ///
151    /// It returns `true` if the room exists, `false` otherwise.
152    pub async fn listen_to_room(&self, room_id: &RoomId) -> Result<bool, LatestEventsError> {
153        Ok(self.state.registered_rooms.for_room(room_id).await?.is_some())
154    }
155
156    /// Check whether the system listens to a particular room.
157    ///
158    /// Note: It's a test only method.
159    #[cfg(test)]
160    pub async fn is_listening_to_room(&self, room_id: &RoomId) -> bool {
161        self.state.registered_rooms.rooms.read().await.contains_key(room_id)
162    }
163
164    /// Start listening to updates (if not already) for a particular room, and
165    /// return a [`Subscriber`] to get the current and future
166    /// [`LatestEventValue`]s.
167    ///
168    /// It returns `Some` if the room exists, `None` otherwise.
169    pub async fn listen_and_subscribe_to_room(
170        &self,
171        room_id: &RoomId,
172    ) -> Result<Option<Subscriber<LatestEventValue, AsyncLock>>, LatestEventsError> {
173        let Some(room_latest_events) = self.state.registered_rooms.for_room(room_id).await? else {
174            return Ok(None);
175        };
176
177        let room_latest_events = room_latest_events.read().await;
178        let latest_event = room_latest_events.for_room();
179
180        Ok(Some(latest_event.subscribe().await))
181    }
182
183    /// Start listening to updates (if not already) for a particular room and a
184    /// particular thread in this room.
185    ///
186    /// It returns `true` if the room and the thread exists, `false` otherwise.
187    pub async fn listen_to_thread(
188        &self,
189        room_id: &RoomId,
190        thread_id: &EventId,
191    ) -> Result<bool, LatestEventsError> {
192        Ok(self.state.registered_rooms.for_thread(room_id, thread_id).await?.is_some())
193    }
194
195    /// Start listening to updates (if not already) for a particular room and a
196    /// particular thread in this room, and return a [`Subscriber`] to get the
197    /// current and future [`LatestEventValue`]s.
198    ///
199    /// It returns `Some` if the room and the thread exists, `None` otherwise.
200    pub async fn listen_and_subscribe_to_thread(
201        &self,
202        room_id: &RoomId,
203        thread_id: &EventId,
204    ) -> Result<Option<Subscriber<LatestEventValue, AsyncLock>>, LatestEventsError> {
205        let Some(room_latest_events) =
206            self.state.registered_rooms.for_thread(room_id, thread_id).await?
207        else {
208            return Ok(None);
209        };
210
211        let room_latest_events = room_latest_events.read().await;
212        let latest_event = room_latest_events
213            .for_thread(thread_id)
214            .expect("The `LatestEvent` for the thread must have been created");
215
216        Ok(Some(latest_event.subscribe().await))
217    }
218
219    /// Forget a room.
220    ///
221    /// It means that [`LatestEvents`] will stop listening to updates for the
222    /// `LatestEvent`s of the room and all its threads.
223    ///
224    /// If [`LatestEvents`] is not listening for `room_id`, nothing happens.
225    pub async fn forget_room(&self, room_id: &RoomId) {
226        self.state.registered_rooms.forget_room(room_id).await;
227    }
228
229    /// Forget a thread.
230    ///
231    /// It means that [`LatestEvents`] will stop listening to updates for the
232    /// `LatestEvent` of the thread.
233    ///
234    /// If [`LatestEvents`] is not listening for `room_id` or `thread_id`,
235    /// nothing happens.
236    pub async fn forget_thread(&self, room_id: &RoomId, thread_id: &EventId) {
237        self.state.registered_rooms.forget_thread(room_id, thread_id).await;
238    }
239}
240
241#[derive(Debug)]
242struct RegisteredRooms {
243    /// All the registered [`RoomLatestEvents`].
244    rooms: RwLock<HashMap<OwnedRoomId, RoomLatestEvents>>,
245
246    /// The (weak) client.
247    weak_client: WeakClient,
248
249    /// The event cache.
250    event_cache: EventCache,
251
252    /// The sender part of the channel used by [`compute_latest_events_task`].
253    ///
254    /// This is used to _trigger_ a computation of a `LatestEventValue` if the
255    /// restored value is `None`.
256    latest_event_queue_sender: mpsc::UnboundedSender<LatestEventQueueUpdate>,
257}
258
259impl RegisteredRooms {
260    fn new(
261        weak_client: WeakClient,
262        event_cache: &EventCache,
263        latest_event_queue_sender: &mpsc::UnboundedSender<LatestEventQueueUpdate>,
264    ) -> Self {
265        Self {
266            rooms: RwLock::new(HashMap::default()),
267            weak_client,
268            event_cache: event_cache.clone(),
269            latest_event_queue_sender: latest_event_queue_sender.clone(),
270        }
271    }
272
273    /// Get a read lock guard to a [`RoomLatestEvents`] given a room ID and an
274    /// optional thread ID.
275    ///
276    /// The [`RoomLatestEvents`], and the associated [`LatestEvent`], will be
277    /// created if missing. It means that write lock is taken if necessary, but
278    /// it's always downgraded to a read lock at the end.
279    async fn room_latest_event(
280        &self,
281        room_id: &RoomId,
282        thread_id: Option<&EventId>,
283    ) -> Result<Option<RwLockReadGuard<'_, RoomLatestEvents>>, LatestEventsError> {
284        fn create_and_insert_room_latest_events(
285            room_id: &RoomId,
286            rooms: &mut HashMap<OwnedRoomId, RoomLatestEvents>,
287            weak_client: &WeakClient,
288            event_cache: &EventCache,
289            latest_event_queue_sender: &mpsc::UnboundedSender<LatestEventQueueUpdate>,
290        ) {
291            let (room_latest_events, is_latest_event_value_none) =
292                With::unzip(RoomLatestEvents::new(
293                    WeakRoom::new(weak_client.clone(), room_id.to_owned()),
294                    event_cache,
295                ));
296
297            // Insert the new `RoomLatestEvents`.
298            rooms.insert(room_id.to_owned(), room_latest_events);
299
300            // If the `LatestEventValue` restored by `RoomLatestEvents` is of
301            // kind `None`, let's try to re-compute it without waiting on the
302            // Event Cache (so the sync usually) or the Send Queue. Maybe the
303            // system has migrated to a new version and the `LatestEventValue`
304            // has been erased, while it is still possible to compute a correct
305            // value.
306            if is_latest_event_value_none {
307                let _ = latest_event_queue_sender
308                    .send(LatestEventQueueUpdate::EventCache { room_id: room_id.to_owned() });
309            }
310        }
311
312        Ok(match thread_id {
313            // Get the room latest event with the aim of fetching the latest
314            // event for a particular thread.
315            //
316            // We need to take a write lock immediately, in case the thead
317            // latest event doesn't exist.
318            Some(thread_id) => {
319                let mut rooms = self.rooms.write().await;
320
321                // The `RoomLatestEvents` doesn't exist. Let's create and insert
322                // it.
323                if rooms.contains_key(room_id).not() {
324                    create_and_insert_room_latest_events(
325                        room_id,
326                        rooms.deref_mut(),
327                        &self.weak_client,
328                        &self.event_cache,
329                        &self.latest_event_queue_sender,
330                    );
331                }
332
333                if let Some(room_latest_event) = rooms.get(room_id) {
334                    let mut room_latest_event = room_latest_event.write().await;
335
336                    // In `RoomLatestEvents`, the `LatestEvent` for this thread
337                    // doesn't exist. Let's create and insert it.
338                    if room_latest_event.has_thread(thread_id).not() {
339                        room_latest_event.create_and_insert_latest_event_for_thread(thread_id);
340                    }
341                }
342
343                RwLockWriteGuard::try_downgrade_map(rooms, |rooms| rooms.get(room_id)).ok()
344            }
345
346            // Get the room latest event with the aim of fetching the latest
347            // event for a particular room.
348            None => {
349                match RwLockReadGuard::try_map(self.rooms.read().await, |rooms| rooms.get(room_id))
350                    .ok()
351                {
352                    value @ Some(_) => value,
353                    None => {
354                        let _timer = timer!(
355                            tracing::Level::DEBUG,
356                            format!("Creating `RoomLatestEvents` for {room_id:?}"),
357                        );
358
359                        let mut rooms = self.rooms.write().await;
360
361                        if rooms.contains_key(room_id).not() {
362                            create_and_insert_room_latest_events(
363                                room_id,
364                                rooms.deref_mut(),
365                                &self.weak_client,
366                                &self.event_cache,
367                                &self.latest_event_queue_sender,
368                            );
369                        }
370
371                        RwLockWriteGuard::try_downgrade_map(rooms, |rooms| rooms.get(room_id)).ok()
372                    }
373                }
374            }
375        })
376    }
377
378    /// Start listening to updates (if not already) for a particular room.
379    ///
380    /// It returns `None` if the room doesn't exist.
381    pub async fn for_room(
382        &self,
383        room_id: &RoomId,
384    ) -> Result<Option<RwLockReadGuard<'_, RoomLatestEvents>>, LatestEventsError> {
385        self.room_latest_event(room_id, None).await
386    }
387
388    /// Start listening to updates (if not already) for a particular room.
389    ///
390    /// It returns `None` if the room or the thread doesn't exist.
391    pub async fn for_thread(
392        &self,
393        room_id: &RoomId,
394        thread_id: &EventId,
395    ) -> Result<Option<RwLockReadGuard<'_, RoomLatestEvents>>, LatestEventsError> {
396        self.room_latest_event(room_id, Some(thread_id)).await
397    }
398
399    /// Forget a room.
400    ///
401    /// It means that [`LatestEvents`] will stop listening to updates for the
402    /// `LatestEvent`s of the room and all its threads.
403    ///
404    /// If [`LatestEvents`] is not listening for `room_id`, nothing happens.
405    pub async fn forget_room(&self, room_id: &RoomId) {
406        {
407            let mut rooms = self.rooms.write().await;
408
409            // Remove the whole `RoomLatestEvents`.
410            rooms.remove(room_id);
411        }
412    }
413
414    /// Forget a thread.
415    ///
416    /// It means that [`LatestEvents`] will stop listening to updates for the
417    /// `LatestEvent` of the thread.
418    ///
419    /// If [`LatestEvents`] is not listening for `room_id` or `thread_id`,
420    /// nothing happens.
421    pub async fn forget_thread(&self, room_id: &RoomId, thread_id: &EventId) {
422        let rooms = self.rooms.read().await;
423
424        // If the `RoomLatestEvents`, remove the `LatestEvent` in `per_thread`.
425        if let Some(room_latest_event) = rooms.get(room_id) {
426            let mut room_latest_event = room_latest_event.write().await;
427
428            // Release the lock on `self.rooms`.
429            drop(rooms);
430
431            room_latest_event.forget_thread(thread_id);
432        }
433    }
434}
435
436/// Represents the kind of updates the [`compute_latest_events_task`] will have
437/// to deal with.
438#[derive(Debug)]
439enum LatestEventQueueUpdate {
440    /// An update from the [`EventCache`] happened.
441    EventCache {
442        /// The ID of the room that has triggered the update.
443        room_id: OwnedRoomId,
444    },
445
446    /// An update from the [`SendQueue`] happened.
447    SendQueue {
448        /// The ID of the room that has triggered the update.
449        room_id: OwnedRoomId,
450
451        /// The update itself.
452        update: RoomSendQueueUpdate,
453    },
454
455    /// An update from the [`RoomInfo`] happened.
456    ///
457    /// [`RoomInfo`]: crate::RoomInfo
458    RoomInfo {
459        /// The ID of the room that has triggered the update.
460        room_id: OwnedRoomId,
461
462        /// The notable update reasons.
463        reasons: RoomInfoNotableUpdateReasons,
464    },
465}
466
467/// The task responsible to listen to the [`EventCache`], the [`SendQueue`] and
468/// the [`RoomInfoNotableUpdate`].
469///
470/// When an update is received and is considered relevant, a message is sent to
471/// the [`compute_latest_events_task`] to compute a new [`LatestEvent`].
472///
473/// When an update is considered relevant, a message is sent over the
474/// `latest_event_queue_sender` channel. See [`compute_latest_events_task`].
475async fn listen_to_updates_task(
476    registered_rooms: Arc<RegisteredRooms>,
477    event_cache: EventCache,
478    mut send_queue_generic_updates_subscriber: broadcast::Receiver<SendQueueUpdate>,
479    room_info_updates: broadcast::Receiver<RoomInfoNotableUpdate>,
480    latest_event_queue_sender: mpsc::UnboundedSender<LatestEventQueueUpdate>,
481) {
482    let mut event_cache_generic_updates_subscriber =
483        event_cache.subscribe_to_room_generic_updates();
484    let mut room_info_updates_subscriber = room_info_updates.resubscribe();
485
486    loop {
487        if listen_to_updates(
488            &registered_rooms.rooms,
489            &mut event_cache_generic_updates_subscriber,
490            &mut send_queue_generic_updates_subscriber,
491            &mut room_info_updates_subscriber,
492            &latest_event_queue_sender,
493        )
494        .await
495        .is_break()
496        {
497            warn!("`listen_to_updates_task` has stopped");
498
499            break;
500        }
501    }
502}
503
504/// The core of [`listen_to_updates_task`].
505///
506/// Having this function detached from its task is helpful for testing and for
507/// state isolation.
508async fn listen_to_updates(
509    registered_rooms: &RwLock<HashMap<OwnedRoomId, RoomLatestEvents>>,
510    event_cache_generic_updates_subscriber: &mut broadcast::Receiver<RoomEventCacheGenericUpdate>,
511    send_queue_generic_updates_subscriber: &mut broadcast::Receiver<SendQueueUpdate>,
512    room_info_updates_subscriber: &mut broadcast::Receiver<RoomInfoNotableUpdate>,
513    latest_event_queue_sender: &mpsc::UnboundedSender<LatestEventQueueUpdate>,
514) -> ControlFlow<()> {
515    select! {
516        room_event_cache_generic_update = event_cache_generic_updates_subscriber.recv() => {
517            if let Ok(RoomEventCacheGenericUpdate { room_id }) = room_event_cache_generic_update {
518                if registered_rooms.read().await.contains_key(&room_id) {
519                    let _ = latest_event_queue_sender.send(LatestEventQueueUpdate::EventCache {
520                        room_id
521                    });
522                }
523            } else {
524                warn!("`event_cache_generic_updates` channel has been closed");
525
526                return ControlFlow::Break(());
527            }
528        }
529
530        send_queue_generic_update = send_queue_generic_updates_subscriber.recv() => {
531            if let Ok(SendQueueUpdate { room_id, update }) = send_queue_generic_update {
532                if registered_rooms.read().await.contains_key(&room_id) {
533                    let _ = latest_event_queue_sender.send(LatestEventQueueUpdate::SendQueue {
534                        room_id,
535                        update
536                    });
537                }
538            } else {
539                warn!("`send_queue_generic_updates` channel has been closed");
540
541                return ControlFlow::Break(());
542            }
543        }
544
545        room_info_update = room_info_updates_subscriber.recv() => {
546            if let Ok(RoomInfoNotableUpdate { room_id, reasons }) = room_info_update {
547                // Filter the update reasons we are interested by.
548                if
549                    // Be careful: the `LATEST_EVENT` reason alone must always
550                    // be ignored! Otherwise it can create a loop.
551                    reasons == RoomInfoNotableUpdateReasons::LATEST_EVENT ||
552
553                    // We are interested by `MEMBERSHIP` so that it captures
554                    // when the user is invited, is joining, is knocking, or is
555                    // leaving a room.
556                    !reasons.contains(RoomInfoNotableUpdateReasons::MEMBERSHIP)
557                {
558                    return ControlFlow::Continue(())
559                }
560
561                if registered_rooms.read().await.contains_key(&room_id) {
562                    let _ = latest_event_queue_sender.send(LatestEventQueueUpdate::RoomInfo {
563                        room_id,
564                        reasons,
565                    });
566                }
567            } else {
568                warn!("`room_info_updates` channel has been closed");
569
570                return ControlFlow::Break(());
571            }
572        }
573    }
574
575    ControlFlow::Continue(())
576}
577
578/// The task responsible to compute new [`LatestEvent`] for a particular room or
579/// thread.
580///
581/// The messages are coming from [`listen_to_updates_task`].
582async fn compute_latest_events_task(
583    registered_rooms: Arc<RegisteredRooms>,
584    mut latest_event_queue_receiver: mpsc::UnboundedReceiver<LatestEventQueueUpdate>,
585) {
586    const BUFFER_SIZE: usize = 16;
587
588    let mut buffer = Vec::with_capacity(BUFFER_SIZE);
589
590    while latest_event_queue_receiver.recv_many(&mut buffer, BUFFER_SIZE).await > 0 {
591        compute_latest_events(&registered_rooms, &buffer).await;
592        buffer.clear();
593    }
594
595    warn!("`compute_latest_events_task` has stopped");
596}
597
598async fn compute_latest_events(
599    registered_rooms: &RegisteredRooms,
600    latest_event_queue_updates: &[LatestEventQueueUpdate],
601) {
602    async fn room_latest_events_write_guard(
603        registered_rooms: &RegisteredRooms,
604        room_id: &OwnedRoomId,
605    ) -> ControlFlow<RoomLatestEventsWriteGuard, ()> {
606        let rooms = registered_rooms.rooms.read().await;
607
608        if let Some(room_latest_events) = rooms.get(room_id) {
609            let room_latest_events = room_latest_events.write().await;
610
611            // Release the lock on `registered_rooms`. It is possible because
612            // `room_latest_events` is an owned lock guard.
613            drop(rooms);
614
615            ControlFlow::Break(room_latest_events)
616        } else {
617            info!(?room_id, "Failed to find the room");
618
619            ControlFlow::Continue(())
620        }
621    }
622
623    for latest_event_queue_update in latest_event_queue_updates {
624        match latest_event_queue_update {
625            LatestEventQueueUpdate::EventCache { room_id } => {
626                let ControlFlow::Break(mut room_latest_events) =
627                    room_latest_events_write_guard(registered_rooms, room_id).await
628                else {
629                    continue;
630                };
631
632                room_latest_events.update_with_event_cache().await;
633            }
634
635            LatestEventQueueUpdate::SendQueue { room_id, update } => {
636                let ControlFlow::Break(mut room_latest_events) =
637                    room_latest_events_write_guard(registered_rooms, room_id).await
638                else {
639                    continue;
640                };
641
642                room_latest_events.update_with_send_queue(update).await;
643            }
644
645            LatestEventQueueUpdate::RoomInfo { room_id, reasons } => {
646                let ControlFlow::Break(mut room_latest_events) =
647                    room_latest_events_write_guard(registered_rooms, room_id).await
648                else {
649                    continue;
650                };
651
652                room_latest_events.update_with_room_info(*reasons).await;
653            }
654        }
655    }
656}
657
658#[cfg(test)]
659fn local_room_message(body: &str) -> LocalLatestEventValue {
660    use matrix_sdk_base::store::SerializableEventContent;
661    use ruma::{
662        MilliSecondsSinceUnixEpoch,
663        events::{AnyMessageLikeEventContent, room::message::RoomMessageEventContent},
664    };
665
666    LocalLatestEventValue {
667        timestamp: MilliSecondsSinceUnixEpoch::now(),
668        content: SerializableEventContent::new(&AnyMessageLikeEventContent::RoomMessage(
669            RoomMessageEventContent::text_plain(body),
670        ))
671        .unwrap(),
672    }
673}
674
675#[cfg(all(test, not(target_family = "wasm")))]
676mod tests {
677    use std::{collections::HashMap, ops::Not, time::Duration};
678
679    use assert_matches::assert_matches;
680    use matrix_sdk_base::{
681        RoomState,
682        cross_process_lock::CrossProcessLockConfig,
683        deserialized_responses::TimelineEventKind,
684        event_cache::store::{EventCacheStore, MemoryStore},
685        linked_chunk::{ChunkIdentifier, LinkedChunkId, Position, Update},
686        store::StoreConfig,
687    };
688    use matrix_sdk_test::{
689        InvitedRoomBuilder, JoinedRoomBuilder, async_test, event_factory::EventFactory,
690    };
691    use ruma::{
692        MilliSecondsSinceUnixEpoch, OwnedTransactionId, event_id,
693        events::{
694            AnySyncMessageLikeEvent, AnySyncStateEvent, AnySyncTimelineEvent, SyncMessageLikeEvent,
695            room::member::{MembershipState, SyncRoomMemberEvent},
696        },
697        owned_event_id, owned_room_id, room_id, user_id,
698    };
699    use stream_assert::assert_pending;
700    use tokio::{
701        task::yield_now,
702        time::{sleep, timeout},
703    };
704
705    use super::{
706        LatestEventValue, RegisteredRooms, RemoteLatestEventValue, RoomEventCacheGenericUpdate,
707        RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, RoomLatestEvents, RoomSendQueueUpdate,
708        RwLock, SendQueueUpdate, WeakRoom, With, broadcast, listen_to_updates, mpsc,
709    };
710    use crate::{
711        latest_events::{LatestEventQueueUpdate, local_room_message},
712        test_utils::{client::MockClientBuilder, mocks::MatrixMockServer},
713    };
714
715    #[async_test]
716    async fn test_latest_events_are_lazy() {
717        let room_id_0 = room_id!("!r0");
718        let room_id_1 = room_id!("!r1");
719        let room_id_2 = room_id!("!r2");
720        let thread_id_1_0 = event_id!("$ev1.0");
721        let thread_id_2_0 = event_id!("$ev2.0");
722
723        let server = MatrixMockServer::new().await;
724        let client = server.client_builder().build().await;
725
726        client.base_client().get_or_create_room(room_id_0, RoomState::Joined);
727        client.base_client().get_or_create_room(room_id_1, RoomState::Joined);
728        client.base_client().get_or_create_room(room_id_2, RoomState::Joined);
729
730        let latest_events = client.latest_events().await.unwrap();
731
732        // Despite there are many rooms, zero `RoomLatestEvents` are created.
733        assert!(latest_events.state.registered_rooms.rooms.read().await.is_empty());
734
735        // Now let's listen to two rooms.
736        assert!(latest_events.listen_to_room(room_id_0).await.unwrap());
737        assert!(latest_events.listen_to_room(room_id_1).await.unwrap());
738
739        {
740            let rooms = latest_events.state.registered_rooms.rooms.read().await;
741            // There are two rooms…
742            assert_eq!(rooms.len(), 2);
743            // … which are room 0 and room 1.
744            assert!(rooms.contains_key(room_id_0));
745            assert!(rooms.contains_key(room_id_1));
746
747            // Room 0 contains zero thread latest events.
748            assert!(rooms.get(room_id_0).unwrap().read().await.per_thread().is_empty());
749            // Room 1 contains zero thread latest events.
750            assert!(rooms.get(room_id_1).unwrap().read().await.per_thread().is_empty());
751        }
752
753        // Now let's listen to one thread respectively for two rooms.
754        assert!(latest_events.listen_to_thread(room_id_1, thread_id_1_0).await.unwrap());
755        assert!(latest_events.listen_to_thread(room_id_2, thread_id_2_0).await.unwrap());
756
757        {
758            let rooms = latest_events.state.registered_rooms.rooms.read().await;
759            // There are now three rooms…
760            assert_eq!(rooms.len(), 3);
761            // … yup, room 2 is now created.
762            assert!(rooms.contains_key(room_id_0));
763            assert!(rooms.contains_key(room_id_1));
764            assert!(rooms.contains_key(room_id_2));
765
766            // Room 0 contains zero thread latest events.
767            assert!(rooms.get(room_id_0).unwrap().read().await.per_thread().is_empty());
768            // Room 1 contains one thread latest event…
769            let room_1 = rooms.get(room_id_1).unwrap().read().await;
770            assert_eq!(room_1.per_thread().len(), 1);
771            // … which is thread 1.0.
772            assert!(room_1.per_thread().contains_key(thread_id_1_0));
773            // Room 2 contains one thread latest event…
774            let room_2 = rooms.get(room_id_2).unwrap().read().await;
775            assert_eq!(room_2.per_thread().len(), 1);
776            // … which is thread 2.0.
777            assert!(room_2.per_thread().contains_key(thread_id_2_0));
778        }
779    }
780
781    #[async_test]
782    async fn test_forget_room() {
783        let room_id_0 = room_id!("!r0");
784        let room_id_1 = room_id!("!r1");
785
786        let server = MatrixMockServer::new().await;
787        let client = server.client_builder().build().await;
788
789        client.base_client().get_or_create_room(room_id_0, RoomState::Joined);
790        client.base_client().get_or_create_room(room_id_1, RoomState::Joined);
791
792        let latest_events = client.latest_events().await.unwrap();
793
794        // Now let's fetch one room.
795        assert!(latest_events.listen_to_room(room_id_0).await.unwrap());
796
797        {
798            let rooms = latest_events.state.registered_rooms.rooms.read().await;
799            // There are one room…
800            assert_eq!(rooms.len(), 1);
801            // … which is room 0.
802            assert!(rooms.contains_key(room_id_0));
803
804            // Room 0 contains zero thread latest events.
805            assert!(rooms.get(room_id_0).unwrap().read().await.per_thread().is_empty());
806        }
807
808        // Now let's forget about room 0.
809        latest_events.forget_room(room_id_0).await;
810
811        {
812            let rooms = latest_events.state.registered_rooms.rooms.read().await;
813            // There are now zero rooms.
814            assert!(rooms.is_empty());
815        }
816    }
817
818    #[async_test]
819    async fn test_forget_thread() {
820        let room_id_0 = room_id!("!r0");
821        let room_id_1 = room_id!("!r1");
822        let thread_id_0_0 = event_id!("$ev0.0");
823
824        let server = MatrixMockServer::new().await;
825        let client = server.client_builder().build().await;
826
827        client.base_client().get_or_create_room(room_id_0, RoomState::Joined);
828        client.base_client().get_or_create_room(room_id_1, RoomState::Joined);
829
830        let latest_events = client.latest_events().await.unwrap();
831
832        // Now let's fetch one thread .
833        assert!(latest_events.listen_to_thread(room_id_0, thread_id_0_0).await.unwrap());
834
835        {
836            let rooms = latest_events.state.registered_rooms.rooms.read().await;
837            // There is one room…
838            assert_eq!(rooms.len(), 1);
839            // … which is room 0.
840            assert!(rooms.contains_key(room_id_0));
841
842            // Room 0 contains one thread latest event…
843            let room_0 = rooms.get(room_id_0).unwrap().read().await;
844            assert_eq!(room_0.per_thread().len(), 1);
845            // … which is thread 0.0.
846            assert!(room_0.per_thread().contains_key(thread_id_0_0));
847        }
848
849        // Now let's forget about the thread.
850        latest_events.forget_thread(room_id_0, thread_id_0_0).await;
851
852        {
853            let rooms = latest_events.state.registered_rooms.rooms.read().await;
854            // There is still one room…
855            assert_eq!(rooms.len(), 1);
856            // … which is room 0.
857            assert!(rooms.contains_key(room_id_0));
858
859            // But the thread has been removed.
860            assert!(rooms.get(room_id_0).unwrap().read().await.per_thread().is_empty());
861        }
862    }
863
864    #[async_test]
865    async fn test_inputs_task_can_listen_to_room_event_cache() {
866        let room_id = owned_room_id!("!r0");
867
868        let server = MatrixMockServer::new().await;
869        let client = server.client_builder().build().await;
870        let weak_client = client.downgrade();
871        let weak_room = WeakRoom::new(weak_client, room_id.clone());
872
873        let event_cache = client.event_cache().unwrap();
874
875        let registered_rooms = RwLock::new(HashMap::new());
876        let (room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
877            broadcast::channel(1);
878        let (_send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
879            broadcast::channel(1);
880        let (_room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
881        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
882
883        // New event cache update, but the `LatestEvents` isn't listening to it.
884        {
885            room_event_cache_generic_update_sender
886                .send(RoomEventCacheGenericUpdate { room_id: room_id.clone() })
887                .unwrap();
888
889            // Run the task.
890            assert!(
891                listen_to_updates(
892                    &registered_rooms,
893                    &mut room_event_cache_generic_update_receiver,
894                    &mut send_queue_generic_update_receiver,
895                    &mut room_info_update_receiver,
896                    &latest_event_queue_sender,
897                )
898                .await
899                .is_continue()
900            );
901
902            // No latest event computation has been triggered.
903            assert!(latest_event_queue_receiver.is_empty());
904        }
905
906        // New event cache update, but this time, the `LatestEvents` is
907        // listening to it.
908        {
909            registered_rooms.write().await.insert(
910                room_id.clone(),
911                With::inner(RoomLatestEvents::new(weak_room, event_cache)),
912            );
913            room_event_cache_generic_update_sender
914                .send(RoomEventCacheGenericUpdate { room_id: room_id.clone() })
915                .unwrap();
916
917            assert!(
918                listen_to_updates(
919                    &registered_rooms,
920                    &mut room_event_cache_generic_update_receiver,
921                    &mut send_queue_generic_update_receiver,
922                    &mut room_info_update_receiver,
923                    &latest_event_queue_sender,
924                )
925                .await
926                .is_continue()
927            );
928
929            // A latest event computation has been triggered!
930            assert!(latest_event_queue_receiver.is_empty().not());
931        }
932    }
933
934    #[async_test]
935    async fn test_inputs_task_can_listen_to_send_queue() {
936        let room_id = owned_room_id!("!r0");
937
938        let server = MatrixMockServer::new().await;
939        let client = server.client_builder().build().await;
940        let weak_client = client.downgrade();
941        let weak_room = WeakRoom::new(weak_client, room_id.clone());
942
943        let event_cache = client.event_cache().unwrap();
944
945        let registered_rooms = RwLock::new(HashMap::new());
946
947        let (_room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
948            broadcast::channel(1);
949        let (send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
950            broadcast::channel(1);
951        let (_room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
952        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
953
954        // New send queue update, but the `LatestEvents` isn't listening to it.
955        {
956            send_queue_generic_update_sender
957                .send(SendQueueUpdate {
958                    room_id: room_id.clone(),
959                    update: RoomSendQueueUpdate::SentEvent {
960                        transaction_id: OwnedTransactionId::from("txnid0"),
961                        event_id: owned_event_id!("$ev0"),
962                    },
963                })
964                .unwrap();
965
966            // Run the task.
967            assert!(
968                listen_to_updates(
969                    &registered_rooms,
970                    &mut room_event_cache_generic_update_receiver,
971                    &mut send_queue_generic_update_receiver,
972                    &mut room_info_update_receiver,
973                    &latest_event_queue_sender,
974                )
975                .await
976                .is_continue()
977            );
978
979            // No latest event computation has been triggered.
980            assert!(latest_event_queue_receiver.is_empty());
981        }
982
983        // New send queue update, but this time, the `LatestEvents` is listening
984        // to it.
985        {
986            registered_rooms.write().await.insert(
987                room_id.clone(),
988                With::inner(RoomLatestEvents::new(weak_room, event_cache)),
989            );
990            send_queue_generic_update_sender
991                .send(SendQueueUpdate {
992                    room_id: room_id.clone(),
993                    update: RoomSendQueueUpdate::SentEvent {
994                        transaction_id: OwnedTransactionId::from("txnid1"),
995                        event_id: owned_event_id!("$ev1"),
996                    },
997                })
998                .unwrap();
999
1000            assert!(
1001                listen_to_updates(
1002                    &registered_rooms,
1003                    &mut room_event_cache_generic_update_receiver,
1004                    &mut send_queue_generic_update_receiver,
1005                    &mut room_info_update_receiver,
1006                    &latest_event_queue_sender,
1007                )
1008                .await
1009                .is_continue()
1010            );
1011
1012            // A latest event computation has been triggered!
1013            assert!(latest_event_queue_receiver.is_empty().not());
1014        }
1015    }
1016
1017    #[async_test]
1018    async fn test_inputs_task_can_listen_to_room_info() {
1019        let room_id = owned_room_id!("!r0");
1020
1021        let server = MatrixMockServer::new().await;
1022        let client = server.client_builder().build().await;
1023        let weak_client = client.downgrade();
1024        let weak_room = WeakRoom::new(weak_client, room_id.clone());
1025
1026        let event_cache = client.event_cache().unwrap();
1027
1028        let registered_rooms = RwLock::new(HashMap::new());
1029
1030        let (_room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
1031            broadcast::channel(1);
1032        let (_send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
1033            broadcast::channel(1);
1034        let (room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
1035        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
1036
1037        // New room info update, but the `LatestEvents` isn't listening to it.
1038        {
1039            room_info_update_sender
1040                .send(RoomInfoNotableUpdate {
1041                    room_id: room_id.clone(),
1042                    reasons: RoomInfoNotableUpdateReasons::MEMBERSHIP,
1043                })
1044                .unwrap();
1045
1046            // Run the task.
1047            assert!(
1048                listen_to_updates(
1049                    &registered_rooms,
1050                    &mut room_event_cache_generic_update_receiver,
1051                    &mut send_queue_generic_update_receiver,
1052                    &mut room_info_update_receiver,
1053                    &latest_event_queue_sender,
1054                )
1055                .await
1056                .is_continue()
1057            );
1058
1059            // No latest event computation has been triggered.
1060            assert!(latest_event_queue_receiver.is_empty());
1061        }
1062
1063        // New room info update, but this time, the `LatestEvents` is listening
1064        // to it.
1065        {
1066            registered_rooms.write().await.insert(
1067                room_id.clone(),
1068                With::inner(RoomLatestEvents::new(weak_room, event_cache)),
1069            );
1070            room_info_update_sender
1071                .send(RoomInfoNotableUpdate {
1072                    room_id: room_id.clone(),
1073                    reasons: RoomInfoNotableUpdateReasons::MEMBERSHIP,
1074                })
1075                .unwrap();
1076
1077            assert!(
1078                listen_to_updates(
1079                    &registered_rooms,
1080                    &mut room_event_cache_generic_update_receiver,
1081                    &mut send_queue_generic_update_receiver,
1082                    &mut room_info_update_receiver,
1083                    &latest_event_queue_sender,
1084                )
1085                .await
1086                .is_continue()
1087            );
1088
1089            // A latest event computation has been triggered!
1090            assert!(latest_event_queue_receiver.is_empty().not());
1091        }
1092    }
1093
1094    #[async_test]
1095    async fn test_inputs_task_can_listen_to_specific_room_info_update_reasons() {
1096        let room_id = owned_room_id!("!r0");
1097
1098        let server = MatrixMockServer::new().await;
1099        let client = server.client_builder().build().await;
1100        let weak_client = client.downgrade();
1101        let weak_room = WeakRoom::new(weak_client, room_id.clone());
1102
1103        let event_cache = client.event_cache().unwrap();
1104
1105        let registered_rooms = RwLock::new(HashMap::new());
1106
1107        let (_room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
1108            broadcast::channel(1);
1109        let (_send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
1110            broadcast::channel(1);
1111        let (room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
1112        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
1113
1114        registered_rooms
1115            .write()
1116            .await
1117            .insert(room_id.clone(), With::inner(RoomLatestEvents::new(weak_room, event_cache)));
1118
1119        // - `RoomInfoNotableUpdateReasons::LATEST_EVENT` is forbidden,
1120        //   otherwise it could create loops.
1121        // - Other reasons are ignored, except
1122        //   `RoomInfoNotableUpdateReasons::MEMBERSHIP`.
1123        for reason in {
1124            let mut all = RoomInfoNotableUpdateReasons::all();
1125            all.remove(RoomInfoNotableUpdateReasons::MEMBERSHIP);
1126
1127            all.iter()
1128        } {
1129            room_info_update_sender
1130                .send(RoomInfoNotableUpdate { room_id: room_id.clone(), reasons: reason })
1131                .unwrap();
1132
1133            assert!(
1134                listen_to_updates(
1135                    &registered_rooms,
1136                    &mut room_event_cache_generic_update_receiver,
1137                    &mut send_queue_generic_update_receiver,
1138                    &mut room_info_update_receiver,
1139                    &latest_event_queue_sender,
1140                )
1141                .await
1142                .is_continue()
1143            );
1144
1145            // No latest event computation has been triggered.
1146            assert!(latest_event_queue_receiver.is_empty());
1147        }
1148
1149        // `RoomInfoNotableUpdateReason::MEMBERSHIP` is accepted.
1150        {
1151            room_info_update_sender
1152                .send(RoomInfoNotableUpdate {
1153                    room_id: room_id.clone(),
1154                    reasons: RoomInfoNotableUpdateReasons::MEMBERSHIP,
1155                })
1156                .unwrap();
1157
1158            assert!(
1159                listen_to_updates(
1160                    &registered_rooms,
1161                    &mut room_event_cache_generic_update_receiver,
1162                    &mut send_queue_generic_update_receiver,
1163                    &mut room_info_update_receiver,
1164                    &latest_event_queue_sender,
1165                )
1166                .await
1167                .is_continue()
1168            );
1169
1170            // A latest event computation has been triggered!
1171            assert!(latest_event_queue_receiver.is_empty().not());
1172        }
1173    }
1174
1175    #[async_test]
1176    async fn test_inputs_task_stops_when_event_cache_channel_is_closed() {
1177        let registered_rooms = RwLock::new(HashMap::new());
1178        let (room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
1179            broadcast::channel(1);
1180        let (_send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
1181            broadcast::channel(1);
1182        let (_room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
1183        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
1184
1185        // Drop the sender to close the channel.
1186        drop(room_event_cache_generic_update_sender);
1187
1188        // Run the task.
1189        assert!(
1190            listen_to_updates(
1191                &registered_rooms,
1192                &mut room_event_cache_generic_update_receiver,
1193                &mut send_queue_generic_update_receiver,
1194                &mut room_info_update_receiver,
1195                &latest_event_queue_sender,
1196            )
1197            .await
1198            // It breaks!
1199            .is_break()
1200        );
1201
1202        assert!(latest_event_queue_receiver.is_empty());
1203    }
1204
1205    #[async_test]
1206    async fn test_inputs_task_stops_when_send_queue_channel_is_closed() {
1207        let registered_rooms = RwLock::new(HashMap::new());
1208        let (_room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
1209            broadcast::channel(1);
1210        let (send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
1211            broadcast::channel(1);
1212        let (_room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
1213        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
1214
1215        // Drop the sender to close the channel.
1216        drop(send_queue_generic_update_sender);
1217
1218        // Run the task.
1219        assert!(
1220            listen_to_updates(
1221                &registered_rooms,
1222                &mut room_event_cache_generic_update_receiver,
1223                &mut send_queue_generic_update_receiver,
1224                &mut room_info_update_receiver,
1225                &latest_event_queue_sender,
1226            )
1227            .await
1228            // It breaks!
1229            .is_break()
1230        );
1231
1232        assert!(latest_event_queue_receiver.is_empty());
1233    }
1234
1235    #[async_test]
1236    async fn test_inputs_task_stops_when_room_info_updates_are_closed() {
1237        let registered_rooms = RwLock::new(HashMap::new());
1238        let (_room_event_cache_generic_update_sender, mut room_event_cache_generic_update_receiver) =
1239            broadcast::channel(1);
1240        let (_send_queue_generic_update_sender, mut send_queue_generic_update_receiver) =
1241            broadcast::channel(1);
1242        let (room_info_update_sender, mut room_info_update_receiver) = broadcast::channel(1);
1243        let (latest_event_queue_sender, latest_event_queue_receiver) = mpsc::unbounded_channel();
1244
1245        // Drop the sender to close the channel.
1246        drop(room_info_update_sender);
1247
1248        // Run the task.
1249        assert!(
1250            listen_to_updates(
1251                &registered_rooms,
1252                &mut room_event_cache_generic_update_receiver,
1253                &mut send_queue_generic_update_receiver,
1254                &mut room_info_update_receiver,
1255                &latest_event_queue_sender,
1256            )
1257            .await
1258            // It breaks!
1259            .is_break()
1260        );
1261
1262        assert!(latest_event_queue_receiver.is_empty());
1263    }
1264
1265    #[async_test]
1266    async fn test_latest_event_value_is_updated_via_event_cache() {
1267        let room_id = owned_room_id!("!r0");
1268        let user_id = user_id!("@mnt_io:matrix.org");
1269        let event_factory = EventFactory::new().sender(user_id).room(&room_id);
1270        let event_id_0 = event_id!("$ev0");
1271
1272        let server = MatrixMockServer::new().await;
1273        let client = server.client_builder().build().await;
1274
1275        // Create the room.
1276        client.base_client().get_or_create_room(&room_id, RoomState::Joined);
1277
1278        let latest_events = client.latest_events().await.unwrap();
1279
1280        // Subscribe to the latest event values for this room.
1281        let mut latest_event_stream =
1282            latest_events.listen_and_subscribe_to_room(&room_id).await.unwrap().unwrap();
1283
1284        // The stream is pending: no new latest event for the moment.
1285        assert_pending!(latest_event_stream);
1286
1287        // Update the event cache with a sync.
1288        server
1289            .sync_room(
1290                &client,
1291                JoinedRoomBuilder::new(&room_id)
1292                    .add_timeline_event(event_factory.text_msg("raclette !").event_id(event_id_0)),
1293            )
1294            .await;
1295
1296        // The event cache has received its update from the sync. It has emitted
1297        // a generic update, which has been received by `LatestEvents` tasks, up
1298        // to the `compute_latest_events` which has updated the latest event
1299        // value.
1300        assert_matches!(
1301            latest_event_stream.next().await,
1302            Some(LatestEventValue::Remote(RemoteLatestEventValue { kind: TimelineEventKind::PlainText { event }, .. })) => {
1303                assert_matches!(
1304                    event.deserialize().unwrap(),
1305                    AnySyncTimelineEvent::MessageLike(
1306                        AnySyncMessageLikeEvent::RoomMessage(
1307                            SyncMessageLikeEvent::Original(message_content)
1308                        )
1309                    ) => {
1310                        assert_eq!(message_content.content.body(), "raclette !");
1311                    }
1312                );
1313            }
1314        );
1315
1316        assert_pending!(latest_event_stream);
1317    }
1318
1319    #[async_test]
1320    async fn test_latest_event_value_is_initialized_by_the_event_cache_lazily() {
1321        let room_id = owned_room_id!("!r0");
1322        let user_id = user_id!("@mnt_io:matrix.org");
1323        let event_factory = EventFactory::new().sender(user_id).room(&room_id);
1324        let event_id_0 = event_id!("$ev0");
1325
1326        // Initialise the event cache store.
1327        let event_cache_store = MemoryStore::new();
1328        event_cache_store
1329            .handle_linked_chunk_updates(
1330                LinkedChunkId::Room(&room_id),
1331                vec![
1332                    Update::NewItemsChunk {
1333                        previous: None,
1334                        new: ChunkIdentifier::new(0),
1335                        next: None,
1336                    },
1337                    Update::PushItems {
1338                        at: Position::new(ChunkIdentifier::new(0), 0),
1339                        items: vec![event_factory.text_msg("hello").event_id(event_id_0).into()],
1340                    },
1341                ],
1342            )
1343            .await
1344            .unwrap();
1345
1346        let client = MockClientBuilder::new(None)
1347            .on_builder(move |builder| {
1348                builder.store_config(
1349                    StoreConfig::new(CrossProcessLockConfig::MultiProcess {
1350                        holder_name: "foo".to_owned(),
1351                    })
1352                    .event_cache_store(event_cache_store),
1353                )
1354            })
1355            .build()
1356            .await;
1357
1358        // Create the room.
1359        client.base_client().get_or_create_room(&room_id, RoomState::Joined);
1360
1361        let latest_events = client.latest_events().await.unwrap();
1362
1363        let mut latest_event_stream =
1364            latest_events.listen_and_subscribe_to_room(&room_id).await.unwrap().unwrap();
1365
1366        // We have a race if the system is busy. Initially, the latest event
1367        // value is `LatestEventValue::None`, then an Event Cache generic update
1368        // is broadcasted manually, computing a new `LatestEventValue`. So let's
1369        // wait on the system to finish this, and assert the final
1370        // `LatestEventValue`.
1371        yield_now().await;
1372        assert_matches!(latest_event_stream.next_now().await, LatestEventValue::Remote(_));
1373
1374        assert_pending!(latest_event_stream);
1375    }
1376
1377    /// This tests a part of
1378    /// [`test_latest_event_value_is_initialized_by_the_event_cache_lazily`].
1379    ///
1380    /// When `RegisteredRooms::room_latest_events` restores a
1381    /// `LatestEventValue::None` (via `RoomLatestEvents::new`), a
1382    /// `LatestEventQueueUpdate::EventCache` is broadcasted to compute a
1383    /// `LatestEventValue` from the Event Cache lazily.
1384    #[async_test]
1385    async fn test_latest_event_value_is_initialized_by_the_event_cache_lazily_inner() {
1386        let room_id_0 = owned_room_id!("!r0");
1387        let room_id_1 = owned_room_id!("!r1");
1388
1389        let server = MatrixMockServer::new().await;
1390        let client = server.client_builder().build().await;
1391
1392        // Create the rooms.
1393        let room_0 = client.base_client().get_or_create_room(&room_id_0, RoomState::Joined);
1394        let room_1 = client.base_client().get_or_create_room(&room_id_1, RoomState::Joined);
1395
1396        // Set up the rooms. `room_0` always has a `LatestEventValue::None` as
1397        // its the default value.
1398        let mut room_info_1 = room_0.clone_info();
1399        room_info_1.set_latest_event(LatestEventValue::LocalIsSending(local_room_message("foo")));
1400        room_1.update_room_info(|_| (room_info_1, Default::default())).await;
1401
1402        let weak_client = client.downgrade();
1403
1404        let event_cache = client.event_cache().unwrap();
1405
1406        let (latest_event_queue_sender, mut latest_event_queue_receiver) =
1407            mpsc::unbounded_channel();
1408
1409        let registered_rooms =
1410            RegisteredRooms::new(weak_client, event_cache, &latest_event_queue_sender);
1411
1412        // Room 0 has a `LatestEventValue::None`, a
1413        // `LatestEventQueueUpdate::EventCache` will be broadcasted.
1414        {
1415            let room_latest_events = registered_rooms.for_room(&room_id_0).await.unwrap().unwrap();
1416            assert_matches!(
1417                room_latest_events.read().await.for_room().get().await,
1418                LatestEventValue::None
1419            );
1420            assert_matches!(
1421                latest_event_queue_receiver.recv().await,
1422                Some(LatestEventQueueUpdate::EventCache { room_id }) => {
1423                    assert_eq!(room_id, room_id_0);
1424                }
1425            );
1426            assert!(latest_event_queue_receiver.is_empty());
1427        }
1428
1429        // Room 1 has a `LatestEventValue::Local*`, a
1430        // `LatestEventQueueUpdate::EventCache` will NOT be broadcasted.
1431        {
1432            let room_latest_events = registered_rooms.for_room(&room_id_1).await.unwrap().unwrap();
1433            assert_matches!(
1434                room_latest_events.read().await.for_room().get().await,
1435                LatestEventValue::LocalIsSending(_)
1436            );
1437            assert!(latest_event_queue_receiver.is_empty());
1438        }
1439    }
1440
1441    #[async_test]
1442    async fn test_latest_event_value_is_updated_via_room_infos_for_invites() {
1443        let room_id = owned_room_id!("!r0");
1444        let event_factory = EventFactory::new().room(&room_id);
1445        let event_id_0 = event_id!("$ev0");
1446        let event_id_1 = event_id!("$ev1");
1447
1448        let server = MatrixMockServer::new().await;
1449        let client = server.client_builder().build().await;
1450        let own_user_id = client.user_id().unwrap();
1451        let other_user_id = user_id!("@other:servername");
1452
1453        let latest_events = client.latest_events().await.unwrap();
1454
1455        // Subscribe to the latest event values for this room.
1456        let mut latest_event_stream =
1457            latest_events.listen_and_subscribe_to_room(&room_id).await.unwrap().unwrap();
1458
1459        // The stream is pending: no new latest event for the moment.
1460        assert_pending!(latest_event_stream);
1461
1462        let now = MilliSecondsSinceUnixEpoch::now().get();
1463
1464        // Update the room with a sync: the user is invited to a room.
1465        {
1466            server
1467                .sync_room(
1468                    &client,
1469                    InvitedRoomBuilder::new(&room_id).add_state_event(
1470                        event_factory
1471                            .member(other_user_id)
1472                            .invited(own_user_id)
1473                            .event_id(event_id_0),
1474                    ),
1475                )
1476                .await;
1477
1478            // The room has received its update from the sync. It has emitted a
1479            // room info update, which has been received by `LatestEvents`
1480            // tasks, up to the `compute_latest_events` which has updated the
1481            // latest event value.
1482            assert_matches!(
1483                latest_event_stream.next().await,
1484                Some(LatestEventValue::RemoteInvite { event_id, timestamp, inviter }) => {
1485                    // It's a stripped state event: they don't have an event ID.
1486                    assert!(event_id.is_none());
1487                    // It's a stripped state event: they don't have a timestamp (`origin_server_ts`), but `now` is normally used as a fallback.
1488                    assert!(timestamp.get() >= now);
1489                    assert_eq!(inviter.as_deref(), Some(other_user_id));
1490                }
1491            );
1492
1493            assert_pending!(latest_event_stream);
1494        };
1495
1496        // Update the room with a sync: the user is invited to the same room.
1497        {
1498            server
1499                .sync_room(
1500                    &client,
1501                    InvitedRoomBuilder::new(&room_id).add_state_event(
1502                        event_factory
1503                            .member(other_user_id)
1504                            .invited(own_user_id)
1505                            .event_id(event_id_0),
1506                    ),
1507                )
1508                .await;
1509
1510            // The room has received its update from the sync. It has emitted a
1511            // room info update, which has been received by `LatestEvents`
1512            // tasks, up to the `compute_latest_events` which has NOT updated
1513            // the latest event value because a previous `RemoteInvite` was
1514            // already computed.
1515            assert!(timeout(Duration::from_secs(1), latest_event_stream.next()).await.is_err());
1516
1517            assert_pending!(latest_event_stream);
1518        }
1519
1520        // Update the room with a sync: the user is joining the room.
1521        {
1522            let now = u64::from(now) + 10; // time flies
1523            server
1524                .sync_room(
1525                    &client,
1526                    JoinedRoomBuilder::new(&room_id).add_timeline_event(
1527                        event_factory
1528                            .member(own_user_id)
1529                            .membership(MembershipState::Join)
1530                            .event_id(event_id_1)
1531                            .server_ts(now),
1532                    ),
1533                )
1534                .await;
1535
1536            // The event cache has received its update from the sync. It has
1537            // emitted a generic update, which has been received by
1538            // `LatestEvents` tasks, up to the `compute_latest_events` which has
1539            // updated the latest event value.
1540            assert_matches!(
1541                latest_event_stream.next().await,
1542                Some(LatestEventValue::Remote(RemoteLatestEventValue { kind: TimelineEventKind::PlainText { event }, .. })) => {
1543                    assert_matches!(
1544                        event.deserialize().unwrap(),
1545                        AnySyncTimelineEvent::State(
1546                            AnySyncStateEvent::RoomMember(
1547                                SyncRoomMemberEvent::Original(event)
1548                            )
1549                        ) => {
1550                            assert_eq!(event.event_id, event_id_1);
1551                            assert_eq!(event.content.membership, MembershipState::Join);
1552                            assert_eq!(event.sender, own_user_id);
1553                            assert_eq!(event.state_key, own_user_id);
1554                            assert_eq!(u64::from(event.origin_server_ts.get()), now);
1555                        }
1556                    );
1557                }
1558            );
1559
1560            assert_pending!(latest_event_stream);
1561        }
1562    }
1563
1564    /// Test that the `LatestEvents` tasks don't hold onto a `Client`, which
1565    /// would prevent it from being dropped.
1566    #[async_test]
1567    async fn test_client_can_drop_latest_events_tasks() {
1568        let room_id = room_id!("!r0");
1569
1570        let server = MatrixMockServer::new().await;
1571        let client = server.client_builder().build().await;
1572
1573        // Syncing initializes the `LatestEvents`, and thus spawns its tasks.
1574        let room = server.sync_joined_room(&client, room_id).await;
1575        assert!(client.latest_events().await.unwrap().is_listening_to_room(room_id).await);
1576
1577        // A `Room` holds onto a `Client`.
1578        drop(room);
1579
1580        // Wait for anything to start up.
1581        yield_now().await;
1582
1583        // Get a weak reference to the client.
1584        let weak_client = client.downgrade();
1585
1586        // Dropping the client will drop the tasks.
1587        drop(client);
1588
1589        // Wait for anything to shutdown.
1590        sleep(Duration::from_secs(2)).await;
1591
1592        // The client has been dropped correctly.
1593        assert_eq!(weak_client.strong_count(), 0);
1594    }
1595}