@@ -73,6 +73,66 @@ final class DiskPersistenceDatastoreIndexTests: XCTestCase {
7373 XCTAssertEqual ( result, expectedIndex, file: file, line: line)
7474 }
7575
76+ func assertInsertionCursor(
77+ proposedEntry: UInt8 ,
78+ pages: [ [ DatastorePageEntryBlock ] ] ,
79+ pageIndex: Int ? ,
80+ blockIndex: Int ? ,
81+ file: StaticString = #filePath,
82+ line: UInt = #line
83+ ) async throws {
84+ let persistence = DiskPersistence ( readOnlyURL: temporaryStoreURL)
85+ let snapshot = Snapshot (
86+ id: . init( rawValue: " Snapshot " ) ,
87+ persistence: persistence
88+ )
89+ let datastore = DiskPersistence . Datastore (
90+ id: . init( rawValue: " Datastore " ) ,
91+ snapshot: snapshot
92+ )
93+ let index = DiskPersistence . Datastore. Index (
94+ datastore: datastore,
95+ id: . primary( manifest: . init( rawValue: " Index " ) ) ,
96+ manifest: DatastoreIndexManifest (
97+ id: . init( rawValue: " Index " ) ,
98+ orderedPages: pages. enumerated ( ) . map { ( index, _) in
99+ . existing( . init( rawValue: " Page \( index) " ) )
100+ }
101+ )
102+ )
103+
104+ var pageLookup : [ DatastorePageIdentifier : DiskPersistence < ReadOnly > . Datastore . Page ] = [ : ]
105+ var pageInfos : [ DatastoreIndexManifest . PageInfo ] = [ ]
106+
107+ for (index, blocks) in pages. enumerated ( ) {
108+ let pageID = DatastorePageIdentifier ( rawValue: " Page \( index) " )
109+ let page = DiskPersistence< ReadOnly> . Datastore. Page (
110+ datastore: datastore,
111+ id: . init(
112+ index: . primary( manifest: . init( rawValue: " Index " ) ) ,
113+ page: pageID
114+ ) ,
115+ blocks: blocks
116+ )
117+ pageLookup [ pageID] = page
118+ pageInfos. append ( . existing( pageID) )
119+ }
120+
121+ let result = try await index. insertionCursor ( for: RangeBoundExpression . including ( proposedEntry) , in: pageInfos) { pageID in
122+ pageLookup [ pageID] !
123+ } comparator: { lhs, rhs in
124+ lhs. sortOrder ( comparedTo: rhs. headers [ 0 ] [ 0 ] , order: . ascending)
125+ }
126+
127+ XCTAssertTrue ( result. persistence === persistence, file: file, line: line)
128+ XCTAssertTrue ( result. datastore === datastore, file: file, line: line)
129+ XCTAssertEqual ( result. index, index, file: file, line: line)
130+ XCTAssertEqual ( result. insertAfter? . pageIndex, pageIndex, file: file, line: line)
131+ let pageID = await result. insertAfter? . page. id. page
132+ XCTAssertEqual ( pageID, pageIndex. map ( { DatastorePageIdentifier ( rawValue: " Page \( $0) " ) } ) , file: file, line: line)
133+ XCTAssertEqual ( result. insertAfter? . blockIndex, blockIndex, file: file, line: line)
134+ }
135+
76136 func testEmptyPagesSearch( ) async throws {
77137 try await assertPageSearch ( proposedEntry: 0 , pages: [ ] , expectedIndex: nil )
78138 try await assertPageSearch ( proposedEntry: 0 , pages: [ ] , expectedIndex: nil )
@@ -330,4 +390,24 @@ final class DiskPersistenceDatastoreIndexTests: XCTestCase {
330390 wait ( for: [ exp] , timeout: 200.0 )
331391 }
332392 }
393+
394+ func testNonContiguousInsertionCursor( ) async throws {
395+ let entry1 = DatastorePageEntry ( headers: [ [ 1 ] ] , content: [ 1 ] ) . blocks ( remainingPageSpace: 1024 , maxPageSpace: 1024 )
396+ let entry2 = DatastorePageEntry ( headers: [ [ 2 ] ] , content: [ 2 ] ) . blocks ( remainingPageSpace: 1024 , maxPageSpace: 1024 )
397+ let entry3 = DatastorePageEntry ( headers: [ [ 3 ] ] , content: Array ( repeating: 3 , count: 100 ) ) . blocks ( remainingPageSpace: 20 , maxPageSpace: 60 )
398+ let entry4 = DatastorePageEntry ( headers: [ [ 4 ] ] , content: [ 4 ] ) . blocks ( remainingPageSpace: 1024 , maxPageSpace: 1024 )
399+
400+ let pages = [
401+ [ entry1 [ 0 ] , entry2 [ 0 ] , entry3 [ 0 ] ] ,
402+ [ entry3 [ 1 ] ] ,
403+ [ entry3 [ 2 ] , entry4 [ 0 ] ]
404+ ]
405+
406+ try await assertInsertionCursor ( proposedEntry: 0 , pages: pages, pageIndex: nil , blockIndex: nil )
407+ try await assertInsertionCursor ( proposedEntry: 1 , pages: pages, pageIndex: nil , blockIndex: nil )
408+ try await assertInsertionCursor ( proposedEntry: 2 , pages: pages, pageIndex: 0 , blockIndex: 0 )
409+ try await assertInsertionCursor ( proposedEntry: 3 , pages: pages, pageIndex: 0 , blockIndex: 1 )
410+ try await assertInsertionCursor ( proposedEntry: 4 , pages: pages, pageIndex: 2 , blockIndex: 0 )
411+ try await assertInsertionCursor ( proposedEntry: 5 , pages: pages, pageIndex: 2 , blockIndex: 1 )
412+ }
333413}
0 commit comments