@@ -22,8 +22,8 @@ use matrix_sdk_common::{
22
22
VerificationState ,
23
23
} ,
24
24
linked_chunk:: {
25
- ChunkContent , ChunkIdentifier as CId , LinkedChunk , LinkedChunkBuilderTest , Position ,
26
- RawChunk , Update ,
25
+ ChunkContent , ChunkIdentifier as CId , LinkedChunk , LinkedChunkBuilder ,
26
+ LinkedChunkBuilderTest , Position , RawChunk , Update ,
27
27
} ,
28
28
} ;
29
29
use matrix_sdk_test:: { event_factory:: EventFactory , ALICE , DEFAULT_TEST_ROOM_ID } ;
@@ -34,7 +34,7 @@ use ruma::{
34
34
35
35
use super :: { media:: IgnoreMediaRetentionPolicy , DynEventCacheStore } ;
36
36
use crate :: {
37
- event_cache:: { Event , Gap } ,
37
+ event_cache:: { store :: DEFAULT_CHUNK_CAPACITY , Event , Gap } ,
38
38
media:: { MediaFormat , MediaRequestParameters , MediaThumbnailSettings } ,
39
39
} ;
40
40
@@ -114,6 +114,10 @@ pub trait EventCacheStoreIntegrationTests {
114
114
/// the store.
115
115
async fn test_handle_updates_and_rebuild_linked_chunk ( & self ) ;
116
116
117
+ /// Test loading a linked chunk incrementally (chunk by chunk) from the
118
+ /// store.
119
+ async fn test_linked_chunk_incremental_loading ( & self ) ;
120
+
117
121
/// Test that rebuilding a linked chunk from an empty store doesn't return
118
122
/// anything.
119
123
async fn test_rebuild_empty_linked_chunk ( & self ) ;
@@ -341,7 +345,7 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore {
341
345
} ,
342
346
// another items chunk
343
347
Update :: NewItemsChunk { previous: Some ( CId :: new( 1 ) ) , new: CId :: new( 2 ) , next: None } ,
344
- // new items on 0
348
+ // new items on 2
345
349
Update :: PushItems {
346
350
at: Position :: new( CId :: new( 2 ) , 0 ) ,
347
351
items: vec![ make_test_event( room_id, "sup" ) ] ,
@@ -392,6 +396,220 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore {
392
396
assert ! ( chunks. next( ) . is_none( ) ) ;
393
397
}
394
398
399
+ async fn test_linked_chunk_incremental_loading ( & self ) {
400
+ let room_id = room_id ! ( "!r0:matrix.org" ) ;
401
+ let event = |msg : & str | make_test_event ( room_id, msg) ;
402
+
403
+ // Load the last chunk, but none exists yet.
404
+ {
405
+ let ( last_chunk, chunk_identifier_generator) =
406
+ self . load_last_chunk ( room_id) . await . unwrap ( ) ;
407
+
408
+ assert ! ( last_chunk. is_none( ) ) ;
409
+ assert_eq ! ( chunk_identifier_generator. current( ) , 0 ) ;
410
+ }
411
+
412
+ self . handle_linked_chunk_updates (
413
+ room_id,
414
+ vec ! [
415
+ // new chunk for items
416
+ Update :: NewItemsChunk { previous: None , new: CId :: new( 0 ) , next: None } ,
417
+ // new items on 0
418
+ Update :: PushItems {
419
+ at: Position :: new( CId :: new( 0 ) , 0 ) ,
420
+ items: vec![ event( "a" ) , event( "b" ) ] ,
421
+ } ,
422
+ // new chunk for a gap
423
+ Update :: NewGapChunk {
424
+ previous: Some ( CId :: new( 0 ) ) ,
425
+ new: CId :: new( 1 ) ,
426
+ next: None ,
427
+ gap: Gap { prev_token: "morbier" . to_owned( ) } ,
428
+ } ,
429
+ // new chunk for items
430
+ Update :: NewItemsChunk { previous: Some ( CId :: new( 1 ) ) , new: CId :: new( 2 ) , next: None } ,
431
+ // new items on 2
432
+ Update :: PushItems {
433
+ at: Position :: new( CId :: new( 2 ) , 0 ) ,
434
+ items: vec![ event( "c" ) , event( "d" ) , event( "e" ) ] ,
435
+ } ,
436
+ ] ,
437
+ )
438
+ . await
439
+ . unwrap ( ) ;
440
+
441
+ // Load the last chunk.
442
+ let mut linked_chunk = {
443
+ let ( last_chunk, chunk_identifier_generator) =
444
+ self . load_last_chunk ( room_id) . await . unwrap ( ) ;
445
+
446
+ assert_eq ! ( chunk_identifier_generator. current( ) , 2 ) ;
447
+
448
+ let linked_chunk = LinkedChunkBuilder :: from_last_chunk :: < DEFAULT_CHUNK_CAPACITY , _ , _ > (
449
+ last_chunk,
450
+ chunk_identifier_generator,
451
+ )
452
+ . unwrap ( ) // unwrap the `Result`
453
+ . unwrap ( ) ; // unwrap the `Option`
454
+
455
+ let mut rchunks = linked_chunk. rchunks ( ) ;
456
+
457
+ // A unique chunk.
458
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
459
+ assert_eq!( chunk. identifier( ) , 2 ) ;
460
+
461
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
462
+ assert_eq!( events. len( ) , 3 ) ;
463
+ check_test_event( & events[ 0 ] , "c" ) ;
464
+ check_test_event( & events[ 1 ] , "d" ) ;
465
+ check_test_event( & events[ 2 ] , "e" ) ;
466
+ } ) ;
467
+ } ) ;
468
+
469
+ assert ! ( rchunks. next( ) . is_none( ) ) ;
470
+
471
+ linked_chunk
472
+ } ;
473
+
474
+ // Load the previous chunk: this is a gap.
475
+ {
476
+ let first_chunk = linked_chunk. chunks ( ) . next ( ) . unwrap ( ) . identifier ( ) ;
477
+ let mut previous_chunk =
478
+ self . load_previous_chunk ( room_id, first_chunk) . await . unwrap ( ) . unwrap ( ) ;
479
+
480
+ // Pretend it's the first chunk.
481
+ previous_chunk. previous = None ;
482
+
483
+ let _ = LinkedChunkBuilder :: insert_new_first_chunk ( & mut linked_chunk, previous_chunk)
484
+ . unwrap ( ) ;
485
+
486
+ let mut rchunks = linked_chunk. rchunks ( ) ;
487
+
488
+ // The last chunk.
489
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
490
+ assert_eq!( chunk. identifier( ) , 2 ) ;
491
+
492
+ // Already asserted, but let's be sure nothing breaks.
493
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
494
+ assert_eq!( events. len( ) , 3 ) ;
495
+ check_test_event( & events[ 0 ] , "c" ) ;
496
+ check_test_event( & events[ 1 ] , "d" ) ;
497
+ check_test_event( & events[ 2 ] , "e" ) ;
498
+ } ) ;
499
+ } ) ;
500
+
501
+ // The new chunk.
502
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
503
+ assert_eq!( chunk. identifier( ) , 1 ) ;
504
+
505
+ assert_matches!( chunk. content( ) , ChunkContent :: Gap ( gap) => {
506
+ assert_eq!( gap. prev_token, "morbier" ) ;
507
+ } ) ;
508
+ } ) ;
509
+
510
+ assert ! ( rchunks. next( ) . is_none( ) ) ;
511
+ }
512
+
513
+ // Load the previous chunk: these are items.
514
+ {
515
+ let first_chunk = linked_chunk. chunks ( ) . next ( ) . unwrap ( ) . identifier ( ) ;
516
+ let mut previous_chunk =
517
+ self . load_previous_chunk ( room_id, first_chunk) . await . unwrap ( ) . unwrap ( ) ;
518
+
519
+ // Pretend it's the first chunk.
520
+ previous_chunk. previous = None ;
521
+
522
+ let _ = LinkedChunkBuilder :: insert_new_first_chunk ( & mut linked_chunk, previous_chunk)
523
+ . unwrap ( ) ;
524
+
525
+ let mut rchunks = linked_chunk. rchunks ( ) ;
526
+
527
+ // The last chunk.
528
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
529
+ assert_eq!( chunk. identifier( ) , 2 ) ;
530
+
531
+ // Already asserted, but let's be sure nothing breaks.
532
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
533
+ assert_eq!( events. len( ) , 3 ) ;
534
+ check_test_event( & events[ 0 ] , "c" ) ;
535
+ check_test_event( & events[ 1 ] , "d" ) ;
536
+ check_test_event( & events[ 2 ] , "e" ) ;
537
+ } ) ;
538
+ } ) ;
539
+
540
+ // Its previous chunk.
541
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
542
+ assert_eq!( chunk. identifier( ) , 1 ) ;
543
+
544
+ // Already asserted, but let's be sure nothing breaks.
545
+ assert_matches!( chunk. content( ) , ChunkContent :: Gap ( gap) => {
546
+ assert_eq!( gap. prev_token, "morbier" ) ;
547
+ } ) ;
548
+ } ) ;
549
+
550
+ // The new chunk.
551
+ assert_matches ! ( rchunks. next( ) , Some ( chunk) => {
552
+ assert_eq!( chunk. identifier( ) , 0 ) ;
553
+
554
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
555
+ assert_eq!( events. len( ) , 2 ) ;
556
+ check_test_event( & events[ 0 ] , "a" ) ;
557
+ check_test_event( & events[ 1 ] , "b" ) ;
558
+ } ) ;
559
+ } ) ;
560
+
561
+ assert ! ( rchunks. next( ) . is_none( ) ) ;
562
+ }
563
+
564
+ // Load the previous chunk: there is none.
565
+ {
566
+ let first_chunk = linked_chunk. chunks ( ) . next ( ) . unwrap ( ) . identifier ( ) ;
567
+ let previous_chunk = self . load_previous_chunk ( room_id, first_chunk) . await . unwrap ( ) ;
568
+
569
+ assert ! ( previous_chunk. is_none( ) ) ;
570
+ }
571
+
572
+ // One last check: a round of assert by using the forwards chunk iterator
573
+ // instead of the backwards chunk iterator.
574
+ {
575
+ let mut chunks = linked_chunk. chunks ( ) ;
576
+
577
+ // The first chunk.
578
+ assert_matches ! ( chunks. next( ) , Some ( chunk) => {
579
+ assert_eq!( chunk. identifier( ) , 0 ) ;
580
+
581
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
582
+ assert_eq!( events. len( ) , 2 ) ;
583
+ check_test_event( & events[ 0 ] , "a" ) ;
584
+ check_test_event( & events[ 1 ] , "b" ) ;
585
+ } ) ;
586
+ } ) ;
587
+
588
+ // The second chunk.
589
+ assert_matches ! ( chunks. next( ) , Some ( chunk) => {
590
+ assert_eq!( chunk. identifier( ) , 1 ) ;
591
+
592
+ assert_matches!( chunk. content( ) , ChunkContent :: Gap ( gap) => {
593
+ assert_eq!( gap. prev_token, "morbier" ) ;
594
+ } ) ;
595
+ } ) ;
596
+
597
+ // The third and last chunk.
598
+ assert_matches ! ( chunks. next( ) , Some ( chunk) => {
599
+ assert_eq!( chunk. identifier( ) , 2 ) ;
600
+
601
+ assert_matches!( chunk. content( ) , ChunkContent :: Items ( events) => {
602
+ assert_eq!( events. len( ) , 3 ) ;
603
+ check_test_event( & events[ 0 ] , "c" ) ;
604
+ check_test_event( & events[ 1 ] , "d" ) ;
605
+ check_test_event( & events[ 2 ] , "e" ) ;
606
+ } ) ;
607
+ } ) ;
608
+
609
+ assert ! ( chunks. next( ) . is_none( ) ) ;
610
+ }
611
+ }
612
+
395
613
async fn test_rebuild_empty_linked_chunk ( & self ) {
396
614
// When I rebuild a linked chunk from an empty store, it's empty.
397
615
let raw_parts = self . load_all_chunks ( & DEFAULT_TEST_ROOM_ID ) . await . unwrap ( ) ;
@@ -564,6 +782,13 @@ macro_rules! event_cache_store_integration_tests {
564
782
event_cache_store. test_handle_updates_and_rebuild_linked_chunk( ) . await ;
565
783
}
566
784
785
+ #[ async_test]
786
+ async fn test_linked_chunk_incremental_loading( ) {
787
+ let event_cache_store =
788
+ get_event_cache_store( ) . await . unwrap( ) . into_event_cache_store( ) ;
789
+ event_cache_store. test_linked_chunk_incremental_loading( ) . await ;
790
+ }
791
+
567
792
#[ async_test]
568
793
async fn test_rebuild_empty_linked_chunk( ) {
569
794
let event_cache_store =
0 commit comments