Skip to content

Commit 92cbdf6

Browse files
committed
Added ReactiveSwift FailingScheduler.
1 parent ffcbe63 commit 92cbdf6

File tree

8 files changed

+81
-40
lines changed

8 files changed

+81
-40
lines changed

Examples/CaseStudies/SwiftUICaseStudies/03-Effects-SystemEnvironment.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ struct SystemEnvironment<Environment> {
176176
Self(
177177
date: Date.init,
178178
environment: environment,
179-
mainQueue: .main,
179+
mainQueue: QueueScheduler.main,
180180
uuid: UUID.init
181181
)
182182
}
@@ -204,7 +204,7 @@ struct SystemEnvironment<Environment> {
204204
return Date()
205205
},
206206
environment: Environment,
207-
mainQueue: AnySchedulerOf<DispatchQueue> = .failing,
207+
mainQueue: DateScheduler = FailingScheduler(),
208208
uuid: @escaping () -> UUID = {
209209
XCTFail("UUID dependency is unimplemented.")
210210
return UUID()

Examples/CaseStudies/SwiftUICaseStudiesTests/02-Effects-WebSocketTests.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class WebSocketTests: XCTestCase {
1010
let receiveSubject = Signal<WebSocketClient.Message, NSError>.pipe()
1111

1212
var webSocket = WebSocketClient.failing
13-
webSocket.open = { _, _, _ in socketSubject.eraseToEffect() }
14-
webSocket.receive = { _ in receiveSubject.eraseToEffect() }
13+
webSocket.open = { _, _, _ in socketSubject.output.producer }
14+
webSocket.receive = { _ in receiveSubject.output.producer }
1515
webSocket.send = { _, _ in Effect(value: nil) }
1616
webSocket.sendPing = { _ in .none }
1717

@@ -59,8 +59,8 @@ class WebSocketTests: XCTestCase {
5959
let receiveSubject = Signal<WebSocketClient.Message, NSError>.pipe()
6060

6161
var webSocket = WebSocketClient.failing
62-
webSocket.open = { _, _, _ in socketSubject.eraseToEffect() }
63-
webSocket.receive = { _ in receiveSubject.eraseToEffect() }
62+
webSocket.open = { _, _, _ in socketSubject.output.producer }
63+
webSocket.receive = { _ in receiveSubject.output.producer }
6464
webSocket.send = { _, _ in Effect(value: NSError(domain: "", code: 1)) }
6565
webSocket.sendPing = { _ in .none }
6666

@@ -104,11 +104,11 @@ class WebSocketTests: XCTestCase {
104104
let pingSubject = Signal<NSError?, Never>.pipe()
105105

106106
var webSocket = WebSocketClient.failing
107-
webSocket.open = { _, _, _ in socketSubject.eraseToEffect() }
107+
webSocket.open = { _, _, _ in socketSubject.output.producer }
108108
webSocket.receive = { _ in .none }
109-
webSocket.sendPing = { _ in pingSubject.eraseToEffect() }
109+
webSocket.sendPing = { _ in pingSubject.output.producer }
110110

111-
let scheduler = DispatchQueue.test
111+
let scheduler = TestScheduler()
112112
let store = TestStore(
113113
initialState: .init(),
114114
reducer: webSocketReducer,
@@ -142,8 +142,8 @@ class WebSocketTests: XCTestCase {
142142
let socketSubject = Signal<WebSocketClient.Action, Never>.pipe()
143143

144144
var webSocket = WebSocketClient.failing
145-
webSocket.cancel = { _, _, _ in .fireAndForget { socketSubject.send(completion: .finished) } }
146-
webSocket.open = { _, _, _ in socketSubject.eraseToEffect() }
145+
webSocket.cancel = { _, _, _ in .fireAndForget { socketSubject.input.sendCompleted() } }
146+
webSocket.open = { _, _, _ in socketSubject.output.producer }
147147
webSocket.receive = { _ in .none }
148148
webSocket.sendPing = { _ in .none }
149149

Examples/CaseStudies/SwiftUICaseStudiesTests/04-HigherOrderReducers-ReusableOfflineDownloadsTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ReusableComponentsDownloadComponentTests: XCTestCase {
1919

2020
func testDownloadFlow() {
2121
var downloadClient = DownloadClient.failing
22-
downloadClient.download = { _, _ in self.downloadSubject.eraseToEffect() }
22+
downloadClient.download = { _, _ in self.downloadSubject.output.producer }
2323

2424
let store = TestStore(
2525
initialState: DownloadComponentState(
@@ -55,7 +55,7 @@ class ReusableComponentsDownloadComponentTests: XCTestCase {
5555

5656
func testDownloadThrottling() {
5757
var downloadClient = DownloadClient.failing
58-
downloadClient.download = { _, _ in self.downloadSubject.eraseToEffect() }
58+
downloadClient.download = { _, _ in self.downloadSubject.output.producer }
5959

6060
let store = TestStore(
6161
initialState: DownloadComponentState(
@@ -96,9 +96,9 @@ class ReusableComponentsDownloadComponentTests: XCTestCase {
9696
func testCancelDownloadFlow() {
9797
var downloadClient = DownloadClient.failing
9898
downloadClient.cancel = { _ in
99-
.fireAndForget { self.downloadSubject.send(completion: .finished) }
99+
.fireAndForget { self.downloadSubject.input.sendCompleted() }
100100
}
101-
downloadClient.download = { _, _ in self.downloadSubject.eraseToEffect() }
101+
downloadClient.download = { _, _ in self.downloadSubject.output.producer }
102102

103103
let store = TestStore(
104104
initialState: DownloadComponentState(
@@ -136,9 +136,9 @@ class ReusableComponentsDownloadComponentTests: XCTestCase {
136136
func testDownloadFinishesWhileTryingToCancel() {
137137
var downloadClient = DownloadClient.failing
138138
downloadClient.cancel = { _ in
139-
.fireAndForget { self.downloadSubject.send(completion: .finished) }
139+
.fireAndForget { self.downloadSubject.input.sendCompleted() }
140140
}
141-
downloadClient.download = { _, _ in self.downloadSubject.eraseToEffect() }
141+
downloadClient.download = { _, _ in self.downloadSubject.output.producer }
142142

143143
let store = TestStore(
144144
initialState: DownloadComponentState(
@@ -178,9 +178,9 @@ class ReusableComponentsDownloadComponentTests: XCTestCase {
178178
func testDeleteDownloadFlow() {
179179
var downloadClient = DownloadClient.failing
180180
downloadClient.cancel = { _ in
181-
.fireAndForget { self.downloadSubject.send(completion: .finished) }
181+
.fireAndForget { self.downloadSubject.input.sendCompleted() }
182182
}
183-
downloadClient.download = { _, _ in self.downloadSubject.eraseToEffect() }
183+
downloadClient.download = { _, _ in self.downloadSubject.output.producer }
184184

185185
let store = TestStore(
186186
initialState: DownloadComponentState(

Examples/Search/SearchTests/SearchTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ class SearchTests: XCTestCase {
142142
reducer: searchReducer,
143143
environment: SearchEnvironment(
144144
weatherClient: weatherClient,
145-
mainQueue: self.scheduler.eraseToAnyScheduler()
145+
mainQueue: self.scheduler
146146
)
147147
)
148148

Examples/SpeechRecognition/SpeechRecognitionTests/SpeechRecognitionTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class SpeechRecognitionTests: XCTestCase {
6262
func testAllowAndRecord() {
6363
var speechClient = SpeechClient.failing
6464
speechClient.finishTask = { _ in
65-
.fireAndForget { self.recognitionTaskSubject.send(completion: .finished) }
65+
.fireAndForget { self.recognitionTaskSubject.input.sendCompleted() }
6666
}
67-
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.eraseToEffect() }
67+
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.output.producer }
6868
speechClient.requestAuthorization = { Effect(value: .authorized) }
6969

7070
let store = TestStore(
@@ -111,7 +111,7 @@ class SpeechRecognitionTests: XCTestCase {
111111

112112
func testAudioSessionFailure() {
113113
var speechClient = SpeechClient.failing
114-
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.eraseToEffect() }
114+
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.output.producer }
115115
speechClient.requestAuthorization = { Effect(value: .authorized) }
116116

117117
let store = TestStore(
@@ -141,7 +141,7 @@ class SpeechRecognitionTests: XCTestCase {
141141

142142
func testAudioEngineFailure() {
143143
var speechClient = SpeechClient.failing
144-
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.eraseToEffect() }
144+
speechClient.recognitionTask = { _, _ in self.recognitionTaskSubject.output.producer }
145145
speechClient.requestAuthorization = { Effect(value: .authorized) }
146146

147147
let store = TestStore(

Examples/TicTacToe/Tests/LoginCoreTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class LoginCoreTests: XCTestCase {
6565
authenticationClient.twoFactor = { _ in
6666
Effect(value: .init(token: "deadbeefdeadbeef", twoFactorRequired: false))
6767
}
68-
let scheduler = DispatchQueue.test
68+
let scheduler = TestScheduler()
6969

7070
let store = TestStore(
7171
initialState: LoginState(),

Examples/VoiceMemos/VoiceMemosTests/VoiceMemosTests.swift

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Foundation
12
import ComposableArchitecture
23
import ReactiveSwift
34
import XCTest
@@ -6,9 +7,6 @@ import XCTest
67

78
class VoiceMemosTests: XCTestCase {
89
func testRecordMemoHappyPath() {
9-
// NB: Combine's concatenation behavior is different in 13.3
10-
guard #available(iOS 13.4, *) else { return }
11-
1210
let audioRecorderSubject = Signal<
1311
AudioRecorderClient.Action, AudioRecorderClient.Failure
1412
>.pipe()
@@ -18,7 +16,7 @@ class VoiceMemosTests: XCTestCase {
1816
environment.audioRecorder.currentTime = { _ in Effect(value: 2.5) }
1917
environment.audioRecorder.requestRecordPermission = { Effect(value: true) }
2018
environment.audioRecorder.startRecording = { _, _ in
21-
audioRecorderSubject.eraseToEffect()
19+
audioRecorderSubject.output.producer
2220
}
2321
environment.audioRecorder.stopRecording = { _ in
2422
.fireAndForget {
@@ -27,7 +25,7 @@ class VoiceMemosTests: XCTestCase {
2725
}
2826
}
2927
environment.date = { Date(timeIntervalSinceReferenceDate: 0) }
30-
environment.mainQueue = scheduler.eraseToAnyScheduler()
28+
environment.mainQueue = scheduler
3129
environment.temporaryDirectory = { URL(fileURLWithPath: "/tmp") }
3230
environment.uuid = { UUID(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF")! }
3331

@@ -81,7 +79,7 @@ class VoiceMemosTests: XCTestCase {
8179

8280
var environment = VoiceMemosEnvironment.failing
8381
environment.audioRecorder.requestRecordPermission = { Effect(value: false) }
84-
environment.mainQueue = .immediate
82+
environment.mainQueue = ImmediateScheduler()
8583
environment.openSettings = .fireAndForget { didOpenSettings = true }
8684

8785
let store = TestStore(
@@ -111,10 +109,10 @@ class VoiceMemosTests: XCTestCase {
111109
environment.audioRecorder.currentTime = { _ in Effect(value: 2.5) }
112110
environment.audioRecorder.requestRecordPermission = { Effect(value: true) }
113111
environment.audioRecorder.startRecording = { _, _ in
114-
audioRecorderSubject.eraseToEffect()
112+
audioRecorderSubject.output.producer
115113
}
116114
environment.date = { Date(timeIntervalSinceReferenceDate: 0) }
117-
environment.mainQueue = .immediate
115+
environment.mainQueue = ImmediateScheduler()
118116
environment.temporaryDirectory = { .init(fileURLWithPath: "/tmp") }
119117
environment.uuid = { UUID(uuidString: "DEADBEEF-DEAD-BEEF-DEAD-BEEFDEADBEEF")! }
120118

@@ -144,14 +142,13 @@ class VoiceMemosTests: XCTestCase {
144142
}
145143

146144
func testPlayMemoHappyPath() {
147-
let scheduler = DispatchQueue.test
145+
let scheduler = TestScheduler()
148146
var environment = VoiceMemosEnvironment.failing
149147
environment.audioPlayer.play = { _, _ in
150148
Effect(value: .didFinishPlaying(successfully: true))
151-
.delay(for: 1, scheduler: scheduler)
152-
.eraseToEffect()
149+
.delay(1, on: scheduler)
153150
}
154-
environment.mainQueue = scheduler.eraseToAnyScheduler()
151+
environment.mainQueue = scheduler
155152

156153
let url = URL(string: "https://www.pointfree.co/functions")!
157154
let store = TestStore(
@@ -195,7 +192,7 @@ class VoiceMemosTests: XCTestCase {
195192
func testPlayMemoFailure() {
196193
var environment = VoiceMemosEnvironment.failing
197194
environment.audioPlayer.play = { _, _ in Effect(error: .decodeErrorDidOccur) }
198-
environment.mainQueue = .immediate
195+
environment.mainQueue = ImmediateScheduler()
199196

200197
let url = URL(string: "https://www.pointfree.co/functions")!
201198
let store = TestStore(
@@ -285,7 +282,7 @@ class VoiceMemosTests: XCTestCase {
285282
var environment = VoiceMemosEnvironment.failing
286283
environment.audioPlayer.play = { _, _ in .none }
287284
environment.audioPlayer.stop = { _ in .none }
288-
environment.mainQueue = .immediate
285+
environment.mainQueue = ImmediateScheduler()
289286

290287
let store = TestStore(
291288
initialState: VoiceMemosState(
@@ -320,7 +317,7 @@ extension VoiceMemosEnvironment {
320317
XCTFail("VoiceMemosEnvironment.date is unimplemented")
321318
return Date()
322319
},
323-
mainQueue: .failing,
320+
mainQueue: FailingScheduler(),
324321
openSettings: .failing("VoiceMemosEnvironment.openSettings"),
325322
temporaryDirectory: {
326323
XCTFail("VoiceMemosEnvironment.temporaryDirectory is unimplemented")
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Foundation
2+
import ReactiveSwift
3+
import XCTestDynamicOverlay
4+
5+
public final class FailingScheduler: DateScheduler {
6+
public init() {}
7+
8+
public var currentDate: Date {
9+
XCTFail(
10+
"""
11+
A failing scheduler was asked the current time.
12+
"""
13+
)
14+
return Date()
15+
}
16+
17+
public func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? {
18+
XCTFail(
19+
"""
20+
A failing scheduler scheduled an action to run later.
21+
"""
22+
)
23+
return nil
24+
}
25+
26+
public func schedule(after date: Date, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, action: @escaping () -> Void) -> Disposable? {
27+
XCTFail(
28+
"""
29+
A failing scheduler scheduled an action to run later.
30+
"""
31+
)
32+
return nil
33+
}
34+
35+
public func schedule(_ action: @escaping () -> Void) -> Disposable? {
36+
XCTFail(
37+
"""
38+
A failing scheduler scheduled an action to run immediately.
39+
"""
40+
)
41+
42+
return nil
43+
}
44+
}

0 commit comments

Comments
 (0)