TM-SGNL-iOS/SignalServiceKit/tests/Util/InterleavingCompositeCursorTest.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

95 lines
2.4 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import XCTest
@testable import SignalServiceKit
final class InterleavingCompositeCursorTest: XCTestCase {
func testInterleaving() {
let cursors: [[Int?]] = [
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
]
let interleavingCursor = InterleavingCompositeCursor(cursors.map { ArrayCursor($0) })
XCTAssertEqual(
interleavingCursor.drain(),
[1, 2, 3, 4, 5, 6, 7, 8, 9]
)
}
func testInterleavingWithUnsortedCursors() {
let cursors: [[Int?]] = [
[9, 6, 3],
[2, 8, 5],
[7, 1, 4],
]
let interleavingCursor = InterleavingCompositeCursor(cursors.map { ArrayCursor($0) })
XCTAssertEqual(
interleavingCursor.drain(),
[2, 7, 1, 4, 8, 5, 9, 6, 3]
)
}
func testInterleavingWithInterruptedCursor() {
// A `nil` return will indicate that the cursor is done, and all values
// thereafter will not be asked for.
let cursors: [[Int?]] = [
[nil, 5, 8],
[3, 6, 9],
[1, nil, 7],
]
let interleavingCursor = InterleavingCompositeCursor(cursors.map { ArrayCursor($0) })
XCTAssertEqual(
interleavingCursor.drain(),
[1, 3, 6, 9]
)
}
}
private extension InterleavingCompositeCursor<ArrayCursor<Int>> {
convenience init(_ elements: [ArrayCursor<Int>]) {
try! self.init(
interleaving: elements,
nextElementComparator: { $0 < $1 }
)
}
}
private struct ArrayCursor<InterleavableElement: Comparable>: InterleavableCursor {
private var elements: [InterleavableElement?]
init(_ elements: [InterleavableElement?]) {
self.elements = elements
}
mutating func nextInterleavableElement() throws -> InterleavableElement? {
guard let first = elements.first else {
return nil
}
elements = Array(elements.dropFirst())
return first
}
}
private extension InterleavingCompositeCursor {
func drain() -> [CursorType.InterleavableElement] {
var elements: [CursorType.InterleavableElement] = []
while let nextInterleavableElement = try! next() {
elements.append(nextInterleavableElement)
}
return elements
}
}