Fix ordering of simultaneous work in test scheduler. (#94)

* Fix ordering of simultaneous work in test scheduler.

* clean up

* clean up

* UInt
This commit is contained in:
Brandon Williams
2020-05-14 11:59:16 -07:00
committed by GitHub
parent 90126dc066
commit 0798889245
3 changed files with 37 additions and 15 deletions

View File

@@ -5,9 +5,10 @@ import Foundation
public final class TestScheduler<SchedulerTimeType, SchedulerOptions>: Scheduler
where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeIntervalConvertible {
private var lastId: UInt = 0
public let minimumTolerance: SchedulerTimeType.Stride = .zero
public private(set) var now: SchedulerTimeType
private var scheduled: [(id: UUID, date: SchedulerTimeType, action: () -> Void)] = []
private var scheduled: [(id: UInt, date: SchedulerTimeType, action: () -> Void)] = []
/// Creates a test scheduler with the given date.
///
@@ -20,11 +21,7 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
///
/// - Parameter stride: A stride.
public func advance(by stride: SchedulerTimeType.Stride = .zero) {
self.scheduled = self.scheduled
// NB: Stabilizes sort via offset.
.enumerated()
.sorted(by: { $0.element.date < $1.element.date || $0.offset < $1.offset })
.map { $0.element }
self.scheduled.sort { $0.date < $1.date || $0.id < $1.id }
guard
let nextDate = self.scheduled.first?.date,
@@ -59,17 +56,17 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
options _: SchedulerOptions?,
_ action: @escaping () -> Void
) -> Cancellable {
let id = self.nextId()
let id = UUID()
func scheduleAction(_ date: SchedulerTimeType) -> () -> Void {
func scheduleAction(for date: SchedulerTimeType) -> () -> Void {
return { [weak self] in
action()
self?.scheduled.append((id, date, scheduleAction(date.advanced(by: interval))))
let nextDate = date.advanced(by: interval)
self?.scheduled.append((id, nextDate, scheduleAction(for: nextDate)))
}
}
self.scheduled.append((id, date, scheduleAction(date.advanced(by: interval))))
self.scheduled.append((id, date, scheduleAction(for: date)))
return AnyCancellable { [weak self] in
self?.scheduled.removeAll(where: { $0.id == id })
@@ -82,11 +79,16 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
options _: SchedulerOptions?,
_ action: @escaping () -> Void
) {
self.scheduled.append((UUID(), date, action))
self.scheduled.append((self.nextId(), date, action))
}
public func schedule(options _: SchedulerOptions?, _ action: @escaping () -> Void) {
self.scheduled.append((UUID(), self.now, action))
self.scheduled.append((self.nextId(), self.now, action))
}
private func nextId() -> UInt {
self.lastId += 1
return self.lastId
}
}

View File

@@ -3,6 +3,8 @@ import ComposableArchitecture
import XCTest
final class ComposableArchitectureTests: XCTestCase {
var cancellables: Set<AnyCancellable> = []
func testScheduling() {
enum CounterAction: Equatable {
case incrAndSquareLater
@@ -60,6 +62,24 @@ final class ComposableArchitectureTests: XCTestCase {
)
}
func testSimultaneousWorkOrdering() {
let testScheduler = TestScheduler<DispatchQueue.SchedulerTimeType, DispatchQueue.SchedulerOptions>(
now: .init(.init(uptimeNanoseconds: 1))
)
var values: [Int] = []
testScheduler.schedule(after: testScheduler.now, interval: 1) { values.append(1) }
.store(in: &self.cancellables)
testScheduler.schedule(after: testScheduler.now, interval: 2) { values.append(42) }
.store(in: &self.cancellables)
XCTAssertEqual(values, [])
testScheduler.advance()
XCTAssertEqual(values, [1, 42])
testScheduler.advance(by: 2)
XCTAssertEqual(values, [1, 42, 1, 1, 42])
}
func testLongLivingEffects() {
typealias Environment = (
startEffect: Effect<Void, Never>,

View File

@@ -103,8 +103,8 @@ final class TimerTests: XCTestCase {
scheduler.run()
XCTAssertEqual(count2, 14)
XCTAssertEqual(count3, 9)
XCTAssertEqual(count2, 15)
XCTAssertEqual(count3, 10)
}
func testTimerCompletion() {