@@ -2227,6 +2227,290 @@ mod tests {
2227
2227
Ok ( ( ) )
2228
2228
}
2229
2229
2230
+ #[ test]
2231
+ fn test_insert_items_at_last_chunk ( ) -> Result < ( ) , Error > {
2232
+ use super :: Update :: * ;
2233
+
2234
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2235
+
2236
+ // Ignore initial update.
2237
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2238
+
2239
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' ] ) ;
2240
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' , 'f' ] ) ;
2241
+ assert_eq ! (
2242
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2243
+ & [
2244
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'a' , 'b' , 'c' ] } ,
2245
+ NewItemsChunk {
2246
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2247
+ new: ChunkIdentifier ( 1 ) ,
2248
+ next: None ,
2249
+ } ,
2250
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) , items: vec![ 'd' , 'e' , 'f' ] } ,
2251
+ ]
2252
+ ) ;
2253
+
2254
+ // Insert inside the last chunk.
2255
+ let position_of_e = linked_chunk. item_position ( |item| * item == 'e' ) . unwrap ( ) ;
2256
+
2257
+ // Insert 4 elements, so that it overflows the chunk capacity. It's important to
2258
+ // see whether chunks are correctly updated and linked.
2259
+ linked_chunk. insert_items_at ( [ 'w' , 'x' , 'y' , 'z' ] , position_of_e) ?;
2260
+
2261
+ assert_items_eq ! (
2262
+ linked_chunk,
2263
+ [ 'a' , 'b' , 'c' ] [ 'd' , 'w' , 'x' ] [ 'y' , 'z' , 'e' ] [ 'f' ]
2264
+ ) ;
2265
+ assert_eq ! ( linked_chunk. num_items( ) , 10 ) ;
2266
+ assert_eq ! (
2267
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2268
+ & [
2269
+ DetachLastItems { at: Position ( ChunkIdentifier ( 1 ) , 1 ) } ,
2270
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 1 ) , items: vec![ 'w' , 'x' ] } ,
2271
+ NewItemsChunk {
2272
+ previous: Some ( ChunkIdentifier ( 1 ) ) ,
2273
+ new: ChunkIdentifier ( 2 ) ,
2274
+ next: None ,
2275
+ } ,
2276
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 0 ) , items: vec![ 'y' , 'z' ] } ,
2277
+ StartReattachItems ,
2278
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 2 ) , items: vec![ 'e' ] } ,
2279
+ NewItemsChunk {
2280
+ previous: Some ( ChunkIdentifier ( 2 ) ) ,
2281
+ new: ChunkIdentifier ( 3 ) ,
2282
+ next: None ,
2283
+ } ,
2284
+ PushItems { at: Position ( ChunkIdentifier ( 3 ) , 0 ) , items: vec![ 'f' ] } ,
2285
+ EndReattachItems ,
2286
+ ]
2287
+ ) ;
2288
+
2289
+ Ok ( ( ) )
2290
+ }
2291
+
2292
+ #[ test]
2293
+ fn test_insert_items_at_first_chunk ( ) -> Result < ( ) , Error > {
2294
+ use super :: Update :: * ;
2295
+
2296
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2297
+
2298
+ // Ignore initial update.
2299
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2300
+
2301
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' ] ) ;
2302
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' , 'f' ] ) ;
2303
+ assert_eq ! (
2304
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2305
+ & [
2306
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'a' , 'b' , 'c' ] } ,
2307
+ NewItemsChunk {
2308
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2309
+ new: ChunkIdentifier ( 1 ) ,
2310
+ next: None ,
2311
+ } ,
2312
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) , items: vec![ 'd' , 'e' , 'f' ] } ,
2313
+ ]
2314
+ ) ;
2315
+
2316
+ // Insert inside the first chunk.
2317
+ let position_of_a = linked_chunk. item_position ( |item| * item == 'a' ) . unwrap ( ) ;
2318
+ linked_chunk. insert_items_at ( [ 'l' , 'm' , 'n' , 'o' ] , position_of_a) ?;
2319
+
2320
+ assert_items_eq ! (
2321
+ linked_chunk,
2322
+ [ 'l' , 'm' , 'n' ] [ 'o' , 'a' , 'b' ] [ 'c' ] [ 'd' , 'e' , 'f' ]
2323
+ ) ;
2324
+ assert_eq ! ( linked_chunk. num_items( ) , 10 ) ;
2325
+ assert_eq ! (
2326
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2327
+ & [
2328
+ DetachLastItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) } ,
2329
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'l' , 'm' , 'n' ] } ,
2330
+ NewItemsChunk {
2331
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2332
+ new: ChunkIdentifier ( 2 ) ,
2333
+ next: Some ( ChunkIdentifier ( 1 ) ) ,
2334
+ } ,
2335
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 0 ) , items: vec![ 'o' ] } ,
2336
+ StartReattachItems ,
2337
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 1 ) , items: vec![ 'a' , 'b' ] } ,
2338
+ NewItemsChunk {
2339
+ previous: Some ( ChunkIdentifier ( 2 ) ) ,
2340
+ new: ChunkIdentifier ( 3 ) ,
2341
+ next: Some ( ChunkIdentifier ( 1 ) ) ,
2342
+ } ,
2343
+ PushItems { at: Position ( ChunkIdentifier ( 3 ) , 0 ) , items: vec![ 'c' ] } ,
2344
+ EndReattachItems ,
2345
+ ]
2346
+ ) ;
2347
+
2348
+ Ok ( ( ) )
2349
+ }
2350
+
2351
+ #[ test]
2352
+ fn test_insert_items_at_middle_chunk ( ) -> Result < ( ) , Error > {
2353
+ use super :: Update :: * ;
2354
+
2355
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2356
+
2357
+ // Ignore initial update.
2358
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2359
+
2360
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' ] ) ;
2361
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' , 'f' ] [ 'g' , 'h' ] ) ;
2362
+ assert_eq ! (
2363
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2364
+ & [
2365
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'a' , 'b' , 'c' ] } ,
2366
+ NewItemsChunk {
2367
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2368
+ new: ChunkIdentifier ( 1 ) ,
2369
+ next: None ,
2370
+ } ,
2371
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) , items: vec![ 'd' , 'e' , 'f' ] } ,
2372
+ NewItemsChunk {
2373
+ previous: Some ( ChunkIdentifier ( 1 ) ) ,
2374
+ new: ChunkIdentifier ( 2 ) ,
2375
+ next: None ,
2376
+ } ,
2377
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 0 ) , items: vec![ 'g' , 'h' ] } ,
2378
+ ]
2379
+ ) ;
2380
+
2381
+ let position_of_d = linked_chunk. item_position ( |item| * item == 'd' ) . unwrap ( ) ;
2382
+ linked_chunk. insert_items_at ( [ 'r' , 's' ] , position_of_d) ?;
2383
+
2384
+ assert_items_eq ! (
2385
+ linked_chunk,
2386
+ [ 'a' , 'b' , 'c' ] [ 'r' , 's' , 'd' ] [ 'e' , 'f' ] [ 'g' , 'h' ]
2387
+ ) ;
2388
+ assert_eq ! ( linked_chunk. num_items( ) , 10 ) ;
2389
+ assert_eq ! (
2390
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2391
+ & [
2392
+ DetachLastItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) } ,
2393
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) , items: vec![ 'r' , 's' ] } ,
2394
+ StartReattachItems ,
2395
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 2 ) , items: vec![ 'd' ] } ,
2396
+ NewItemsChunk {
2397
+ previous: Some ( ChunkIdentifier ( 1 ) ) ,
2398
+ new: ChunkIdentifier ( 3 ) ,
2399
+ next: Some ( ChunkIdentifier ( 2 ) ) ,
2400
+ } ,
2401
+ PushItems { at: Position ( ChunkIdentifier ( 3 ) , 0 ) , items: vec![ 'e' , 'f' ] } ,
2402
+ EndReattachItems ,
2403
+ ]
2404
+ ) ;
2405
+
2406
+ Ok ( ( ) )
2407
+ }
2408
+
2409
+ #[ test]
2410
+ fn test_insert_items_at_end_of_chunk ( ) -> Result < ( ) , Error > {
2411
+ use super :: Update :: * ;
2412
+
2413
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2414
+
2415
+ // Ignore initial update.
2416
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2417
+
2418
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' , 'd' , 'e' ] ) ;
2419
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ 'd' , 'e' ] ) ;
2420
+ assert_eq ! (
2421
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2422
+ & [
2423
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'a' , 'b' , 'c' ] } ,
2424
+ NewItemsChunk {
2425
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2426
+ new: ChunkIdentifier ( 1 ) ,
2427
+ next: None ,
2428
+ } ,
2429
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 0 ) , items: vec![ 'd' , 'e' ] } ,
2430
+ ]
2431
+ ) ;
2432
+
2433
+ // Insert at the end of a chunk.
2434
+ let position_of_e = linked_chunk. item_position ( |item| * item == 'e' ) . unwrap ( ) ;
2435
+ let position_after_e =
2436
+ Position ( position_of_e. chunk_identifier ( ) , position_of_e. index ( ) + 1 ) ;
2437
+
2438
+ linked_chunk. insert_items_at ( [ 'p' , 'q' ] , position_after_e) ?;
2439
+ assert_items_eq ! (
2440
+ linked_chunk,
2441
+ [ 'a' , 'b' , 'c' ] [ 'd' , 'e' , 'p' ] [ 'q' ]
2442
+ ) ;
2443
+ assert_eq ! (
2444
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2445
+ & [
2446
+ PushItems { at: Position ( ChunkIdentifier ( 1 ) , 2 ) , items: vec![ 'p' ] } ,
2447
+ NewItemsChunk {
2448
+ previous: Some ( ChunkIdentifier ( 1 ) ) ,
2449
+ new: ChunkIdentifier ( 2 ) ,
2450
+ next: None
2451
+ } ,
2452
+ PushItems { at: Position ( ChunkIdentifier ( 2 ) , 0 ) , items: vec![ 'q' ] }
2453
+ ]
2454
+ ) ;
2455
+ assert_eq ! ( linked_chunk. num_items( ) , 7 ) ;
2456
+
2457
+ Ok ( ( ) )
2458
+ }
2459
+
2460
+ #[ test]
2461
+ fn test_insert_items_at_errs ( ) -> Result < ( ) , Error > {
2462
+ use super :: Update :: * ;
2463
+
2464
+ let mut linked_chunk = LinkedChunk :: < 3 , char , ( ) > :: new_with_update_history ( ) ;
2465
+
2466
+ // Ignore initial update.
2467
+ let _ = linked_chunk. updates ( ) . unwrap ( ) . take ( ) ;
2468
+
2469
+ linked_chunk. push_items_back ( [ 'a' , 'b' , 'c' ] ) ;
2470
+ linked_chunk. push_gap_back ( ( ) ) ;
2471
+ assert_items_eq ! ( linked_chunk, [ 'a' , 'b' , 'c' ] [ -] ) ;
2472
+ assert_eq ! (
2473
+ linked_chunk. updates( ) . unwrap( ) . take( ) ,
2474
+ & [
2475
+ PushItems { at: Position ( ChunkIdentifier ( 0 ) , 0 ) , items: vec![ 'a' , 'b' , 'c' ] } ,
2476
+ NewGapChunk {
2477
+ previous: Some ( ChunkIdentifier ( 0 ) ) ,
2478
+ new: ChunkIdentifier ( 1 ) ,
2479
+ next: None ,
2480
+ gap: ( ) ,
2481
+ } ,
2482
+ ]
2483
+ ) ;
2484
+
2485
+ // Insert in a chunk that does not exist.
2486
+ {
2487
+ assert_matches ! (
2488
+ linked_chunk. insert_items_at( [ 'u' , 'v' ] , Position ( ChunkIdentifier ( 128 ) , 0 ) ) ,
2489
+ Err ( Error :: InvalidChunkIdentifier { identifier: ChunkIdentifier ( 128 ) } )
2490
+ ) ;
2491
+ assert ! ( linked_chunk. updates( ) . unwrap( ) . take( ) . is_empty( ) ) ;
2492
+ }
2493
+
2494
+ // Insert in a chunk that exists, but at an item that does not exist.
2495
+ {
2496
+ assert_matches ! (
2497
+ linked_chunk. insert_items_at( [ 'u' , 'v' ] , Position ( ChunkIdentifier ( 0 ) , 128 ) ) ,
2498
+ Err ( Error :: InvalidItemIndex { index: 128 } )
2499
+ ) ;
2500
+ assert ! ( linked_chunk. updates( ) . unwrap( ) . take( ) . is_empty( ) ) ;
2501
+ }
2502
+
2503
+ // Insert in a gap.
2504
+ {
2505
+ assert_matches ! (
2506
+ linked_chunk. insert_items_at( [ 'u' , 'v' ] , Position ( ChunkIdentifier ( 1 ) , 0 ) ) ,
2507
+ Err ( Error :: ChunkIsAGap { identifier: ChunkIdentifier ( 1 ) } )
2508
+ ) ;
2509
+ }
2510
+
2511
+ Ok ( ( ) )
2512
+ }
2513
+
2230
2514
#[ test]
2231
2515
fn test_remove_item_at ( ) -> Result < ( ) , Error > {
2232
2516
use super :: Update :: * ;
0 commit comments