Skip to content

Commit 0798889

Browse files
authored
Fix ordering of simultaneous work in test scheduler. (#94)
* Fix ordering of simultaneous work in test scheduler. * clean up * clean up * UInt
1 parent 90126dc commit 0798889

File tree

3 files changed

+37
-15
lines changed

3 files changed

+37
-15
lines changed

Sources/ComposableArchitecture/Scheduling/TestScheduler.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import Foundation
55
public final class TestScheduler<SchedulerTimeType, SchedulerOptions>: Scheduler
66
where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeIntervalConvertible {
77

8+
private var lastId: UInt = 0
89
public let minimumTolerance: SchedulerTimeType.Stride = .zero
910
public private(set) var now: SchedulerTimeType
10-
private var scheduled: [(id: UUID, date: SchedulerTimeType, action: () -> Void)] = []
11+
private var scheduled: [(id: UInt, date: SchedulerTimeType, action: () -> Void)] = []
1112

1213
/// Creates a test scheduler with the given date.
1314
///
@@ -20,11 +21,7 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
2021
///
2122
/// - Parameter stride: A stride.
2223
public func advance(by stride: SchedulerTimeType.Stride = .zero) {
23-
self.scheduled = self.scheduled
24-
// NB: Stabilizes sort via offset.
25-
.enumerated()
26-
.sorted(by: { $0.element.date < $1.element.date || $0.offset < $1.offset })
27-
.map { $0.element }
24+
self.scheduled.sort { $0.date < $1.date || $0.id < $1.id }
2825

2926
guard
3027
let nextDate = self.scheduled.first?.date,
@@ -59,17 +56,17 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
5956
options _: SchedulerOptions?,
6057
_ action: @escaping () -> Void
6158
) -> Cancellable {
59+
let id = self.nextId()
6260

63-
let id = UUID()
64-
65-
func scheduleAction(_ date: SchedulerTimeType) -> () -> Void {
61+
func scheduleAction(for date: SchedulerTimeType) -> () -> Void {
6662
return { [weak self] in
6763
action()
68-
self?.scheduled.append((id, date, scheduleAction(date.advanced(by: interval))))
64+
let nextDate = date.advanced(by: interval)
65+
self?.scheduled.append((id, nextDate, scheduleAction(for: nextDate)))
6966
}
7067
}
7168

72-
self.scheduled.append((id, date, scheduleAction(date.advanced(by: interval))))
69+
self.scheduled.append((id, date, scheduleAction(for: date)))
7370

7471
return AnyCancellable { [weak self] in
7572
self?.scheduled.removeAll(where: { $0.id == id })
@@ -82,11 +79,16 @@ where SchedulerTimeType: Strideable, SchedulerTimeType.Stride: SchedulerTimeInte
8279
options _: SchedulerOptions?,
8380
_ action: @escaping () -> Void
8481
) {
85-
self.scheduled.append((UUID(), date, action))
82+
self.scheduled.append((self.nextId(), date, action))
8683
}
8784

8885
public func schedule(options _: SchedulerOptions?, _ action: @escaping () -> Void) {
89-
self.scheduled.append((UUID(), self.now, action))
86+
self.scheduled.append((self.nextId(), self.now, action))
87+
}
88+
89+
private func nextId() -> UInt {
90+
self.lastId += 1
91+
return self.lastId
9092
}
9193
}
9294

Tests/ComposableArchitectureTests/ComposableArchitectureTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import ComposableArchitecture
33
import XCTest
44

55
final class ComposableArchitectureTests: XCTestCase {
6+
var cancellables: Set<AnyCancellable> = []
7+
68
func testScheduling() {
79
enum CounterAction: Equatable {
810
case incrAndSquareLater
@@ -60,6 +62,24 @@ final class ComposableArchitectureTests: XCTestCase {
6062
)
6163
}
6264

65+
func testSimultaneousWorkOrdering() {
66+
let testScheduler = TestScheduler<DispatchQueue.SchedulerTimeType, DispatchQueue.SchedulerOptions>(
67+
now: .init(.init(uptimeNanoseconds: 1))
68+
)
69+
70+
var values: [Int] = []
71+
testScheduler.schedule(after: testScheduler.now, interval: 1) { values.append(1) }
72+
.store(in: &self.cancellables)
73+
testScheduler.schedule(after: testScheduler.now, interval: 2) { values.append(42) }
74+
.store(in: &self.cancellables)
75+
76+
XCTAssertEqual(values, [])
77+
testScheduler.advance()
78+
XCTAssertEqual(values, [1, 42])
79+
testScheduler.advance(by: 2)
80+
XCTAssertEqual(values, [1, 42, 1, 1, 42])
81+
}
82+
6383
func testLongLivingEffects() {
6484
typealias Environment = (
6585
startEffect: Effect<Void, Never>,

Tests/ComposableArchitectureTests/TimerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ final class TimerTests: XCTestCase {
103103

104104
scheduler.run()
105105

106-
XCTAssertEqual(count2, 14)
107-
XCTAssertEqual(count3, 9)
106+
XCTAssertEqual(count2, 15)
107+
XCTAssertEqual(count3, 10)
108108
}
109109

110110
func testTimerCompletion() {

0 commit comments

Comments
 (0)