@@ -7,7 +7,12 @@ fileprivate extension CGFloat {
77 }
88}
99
10+ extension UUID : @retroactive Identifiable {
11+ public var id : UUID { self }
12+ }
13+
1014final class TextLayoutLineStorageTests : XCTestCase {
15+
1116 /// Creates a balanced height=3 tree useful for testing and debugging.
1217 /// - Returns: A new tree.
1318 fileprivate func createBalancedTree( ) -> TextLineStorage < TextLine > {
@@ -20,16 +25,16 @@ final class TextLayoutLineStorageTests: XCTestCase {
2025 return tree
2126 }
2227
28+ struct ChildData {
29+ let length : Int
30+ let count : Int
31+ let height : CGFloat
32+ }
33+
2334 /// Recursively checks that the given tree has the correct metadata everywhere.
2435 /// - Parameter tree: The tree to check.
25- fileprivate func assertTreeMetadataCorrect( _ tree: TextLineStorage < TextLine > ) throws {
26- struct ChildData {
27- let length : Int
28- let count : Int
29- let height : CGFloat
30- }
31-
32- func checkChildren( _ node: TextLineStorage < TextLine > . Node < TextLine > ? ) -> ChildData {
36+ fileprivate func assertTreeMetadataCorrect< T: Identifiable > ( _ tree: TextLineStorage < T > ) throws {
37+ func checkChildren( _ node: TextLineStorage < T > . Node < T > ? ) -> ChildData {
3338 guard let node else { return ChildData ( length: 0 , count: 0 , height: 0.0 ) }
3439 let leftSubtreeData = checkChildren ( node. left)
3540 let rightSubtreeData = checkChildren ( node. right)
@@ -272,4 +277,111 @@ final class TextLayoutLineStorageTests: XCTestCase {
272277 }
273278 }
274279 }
280+
281+ func test_transplantWithExistingLeftNodes( ) throws { // swiftlint:disable:this function_body_length
282+ typealias Storage = TextLineStorage < UUID >
283+ typealias Node = TextLineStorage < UUID > . Node
284+ // Test that when transplanting a node with no left nodes, with a node with left nodes, that
285+ // the resulting tree has valid 'left_' metadata
286+ // 1
287+ // / \
288+ // 7 2
289+ // /
290+ // 3 ← this will be moved, this test ensures 4 retains it's left subtree count
291+ // \
292+ // 4
293+ // | |
294+ // 5 6
295+
296+ let node5 = Node (
297+ length: 5 ,
298+ data: UUID ( ) ,
299+ leftSubtreeOffset: 0 ,
300+ leftSubtreeHeight: 0 ,
301+ leftSubtreeCount: 0 ,
302+ height: 1 ,
303+ left: nil ,
304+ right: nil ,
305+ parent: nil ,
306+ color: . black
307+ )
308+
309+ let node6 = Node (
310+ length: 6 ,
311+ data: UUID ( ) ,
312+ leftSubtreeOffset: 0 ,
313+ leftSubtreeHeight: 0 ,
314+ leftSubtreeCount: 0 ,
315+ height: 1 ,
316+ left: nil ,
317+ right: nil ,
318+ parent: nil ,
319+ color: . black
320+ )
321+
322+ let node4 = Node (
323+ length: 4 ,
324+ data: UUID ( ) ,
325+ leftSubtreeOffset: 5 ,
326+ leftSubtreeHeight: 1 ,
327+ leftSubtreeCount: 1 , // node5 is on the left
328+ height: 1 ,
329+ left: node5,
330+ right: node6,
331+ parent: nil ,
332+ color: . black
333+ )
334+ node5. parent = node4
335+ node6. parent = node4
336+
337+ let node3 = Node (
338+ length: 3 ,
339+ data: UUID ( ) ,
340+ leftSubtreeOffset: 0 ,
341+ leftSubtreeHeight: 0 ,
342+ leftSubtreeCount: 0 ,
343+ height: 1 ,
344+ left: nil ,
345+ right: node4,
346+ parent: nil ,
347+ color: . black
348+ )
349+ node4. parent = node3
350+
351+ let node2 = Node (
352+ length: 2 ,
353+ data: UUID ( ) ,
354+ leftSubtreeOffset: 18 ,
355+ leftSubtreeHeight: 4 ,
356+ leftSubtreeCount: 4 , // node3 is on the left
357+ height: 1 ,
358+ left: node3,
359+ right: nil ,
360+ parent: nil ,
361+ color: . black
362+ )
363+ node3. parent = node2
364+
365+ let node7 = Node ( length: 7 , data: UUID ( ) , height: 1 )
366+
367+ let node1 = Node (
368+ length: 1 ,
369+ data: UUID ( ) ,
370+ leftSubtreeOffset: 7 ,
371+ leftSubtreeHeight: 1 ,
372+ leftSubtreeCount: 1 ,
373+ height: 1 ,
374+ left: node7,
375+ right: node2,
376+ parent: nil ,
377+ color: . black
378+ )
379+ node2. parent = node1
380+
381+ let storage = Storage ( root: node1, count: 7 , length: 28 , height: 7 )
382+
383+ storage. delete ( lineAt: 7 ) // Delete the root
384+
385+ try assertTreeMetadataCorrect ( storage)
386+ }
275387}
0 commit comments