@@ -2227,6 +2227,290 @@ mod tests {
22272227 Ok ( ( ) )
22282228 }
22292229
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+
22302514 #[ test]
22312515 fn test_remove_item_at ( ) -> Result < ( ) , Error > {
22322516 use super :: Update :: * ;
0 commit comments