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