import XCTest @testable import RastrilloNative @MainActor final class CoalescedRunnerTests: XCTestCase { func testSequentialCallsEachRefresh() async { let runner = CoalescedRunner() var passes = 0 for _ in 0..<3 { await runner.run { passes += 1 } } XCTAssertEqual(passes, 3) } func testBurstWaitsForTrailingPassAndReusesOriginalOperation() async { let runner = CoalescedRunner() var passes = 0 var release: CheckedContinuation? var trailingRelease: CheckedContinuation? let first = Task { @MainActor in await runner.run { passes += 1 if passes == 1 { await withCheckedContinuation { release = $0 } } else { await withCheckedContinuation { trailingRelease = $0 } } } } while release == nil { await Task.yield() } var entered = 0 var resumed = 0 let waiters = (0..<5).map { _ in Task { @MainActor in entered += 1 await runner.run { XCTFail("A burst must reuse the active operation") } resumed += 1 } } // Each task runs on this actor until run suspends, so observing all // entries proves they have queued without timing-based sleeps. while entered != 5 { await Task.yield() } XCTAssertEqual(passes, 1) XCTAssertEqual(resumed, 0) release?.resume() while trailingRelease == nil { await Task.yield() } XCTAssertEqual(resumed, 0, "Callers must also wait for the trailing pass") trailingRelease?.resume() await first.value for waiter in waiters { await waiter.value } XCTAssertEqual(passes, 2) XCTAssertEqual(resumed, 5) await runner.run { passes += 1 } XCTAssertEqual(passes, 3, "Completed work must not swallow the next refresh") } func testCancelledWaiterDoesNotCancelSharedRefresh() async { let runner = CoalescedRunner() var release: CheckedContinuation? var passes = 0 let first = Task { @MainActor in await runner.run { XCTAssertFalse(Task.isCancelled) passes += 1 if passes == 1 { await withCheckedContinuation { release = $0 } } } } while release == nil { await Task.yield() } var entered = false let waiter = Task { @MainActor in entered = true await runner.run { XCTFail("Must not replace the active operation") } } while !entered { await Task.yield() } waiter.cancel() release?.resume() await first.value await waiter.value XCTAssertEqual(passes, 2) } }