@@ -36,10 +36,12 @@ class MockRealtime: RCNConfigRealtime, @unchecked Sendable {
36
36
/// The listener closure captured from the `updates` async stream.
37
37
var listener : ( ( RemoteConfigUpdate ? , Error ? ) -> Void ) ?
38
38
let mockRegistration = MockListenerRegistration ( )
39
+ var listenerAttachedExpectation : XCTestExpectation ?
39
40
40
41
override func addConfigUpdateListener( _ listener: @escaping ( RemoteConfigUpdate ? , Error ? )
41
42
-> Void ) -> ConfigUpdateListenerRegistration {
42
43
self . listener = listener
44
+ listenerAttachedExpectation? . fulfill ( )
43
45
return mockRegistration
44
46
}
45
47
@@ -101,6 +103,9 @@ class AsyncStreamTests: XCTestCase {
101
103
let expectation = self . expectation ( description: " Stream should yield an update. " )
102
104
let keysToUpdate = [ " foo " , " bar " ]
103
105
106
+ let listenerAttachedExpectation = self . expectation ( description: " Listener should be attached. " )
107
+ mockRealtime. listenerAttachedExpectation = listenerAttachedExpectation
108
+
104
109
let listeningTask = Task {
105
110
for try await update in config. updates {
106
111
XCTAssertEqual ( update. updatedKeys, Set ( keysToUpdate) )
@@ -109,8 +114,8 @@ class AsyncStreamTests: XCTestCase {
109
114
}
110
115
}
111
116
112
- // Ensure the listener is attached before sending the update.
113
- try await Task . sleep ( nanoseconds : 100_000_000 ) // 0.1 seconds
117
+ // Wait for the listener to be attached before sending the update.
118
+ await fulfillment ( of : [ listenerAttachedExpectation ] , timeout : 1.0 )
114
119
115
120
mockRealtime. sendUpdate ( keys: keysToUpdate)
116
121
@@ -122,6 +127,9 @@ class AsyncStreamTests: XCTestCase {
122
127
let expectation = self . expectation ( description: " Stream should throw an error. " )
123
128
let testError = TestError ( )
124
129
130
+ let listenerAttachedExpectation = self . expectation ( description: " Listener should be attached. " )
131
+ mockRealtime. listenerAttachedExpectation = listenerAttachedExpectation
132
+
125
133
let listeningTask = Task {
126
134
do {
127
135
for try await _ in config. updates {
@@ -133,8 +141,8 @@ class AsyncStreamTests: XCTestCase {
133
141
}
134
142
}
135
143
136
- // Ensure the listener is attached before sending the error.
137
- try await Task . sleep ( nanoseconds : 100_000_000 ) // 0.1 seconds
144
+ // Wait for the listener to be attached before sending the error.
145
+ await fulfillment ( of : [ listenerAttachedExpectation ] , timeout : 1.0 )
138
146
139
147
mockRealtime. sendError ( testError)
140
148
@@ -143,14 +151,17 @@ class AsyncStreamTests: XCTestCase {
143
151
}
144
152
145
153
func testStreamCancellation_callsRemoveOnListener( ) async throws {
154
+ let listenerAttachedExpectation = self . expectation ( description: " Listener should be attached. " )
155
+ mockRealtime. listenerAttachedExpectation = listenerAttachedExpectation
156
+
146
157
let listeningTask = Task {
147
158
for try await _ in config. updates {
148
159
// We will cancel the task, so it should not reach here.
149
160
}
150
161
}
151
162
152
- // Ensure the listener has time to be established .
153
- try await Task . sleep ( nanoseconds : 100_000_000 ) // 0.1 seconds
163
+ // Wait for the listener to be attached .
164
+ await fulfillment ( of : [ listenerAttachedExpectation ] , timeout : 1.0 )
154
165
155
166
// Verify the listener has not been removed yet.
156
167
XCTAssertFalse ( mockRealtime. mockRegistration. wasRemoveCalled)
@@ -168,6 +179,9 @@ class AsyncStreamTests: XCTestCase {
168
179
func testStreamFinishesGracefully_whenListenerSendsNil( ) async throws {
169
180
let expectation = self . expectation ( description: " Stream should finish without error. " )
170
181
182
+ let listenerAttachedExpectation = self . expectation ( description: " Listener should be attached. " )
183
+ mockRealtime. listenerAttachedExpectation = listenerAttachedExpectation
184
+
171
185
let listeningTask = Task {
172
186
var updateCount = 0
173
187
do {
@@ -182,7 +196,7 @@ class AsyncStreamTests: XCTestCase {
182
196
}
183
197
}
184
198
185
- try await Task . sleep ( nanoseconds : 100_000_000 )
199
+ await fulfillment ( of : [ listenerAttachedExpectation ] , timeout : 1.0 )
186
200
mockRealtime. sendCompletion ( )
187
201
188
202
await fulfillment ( of: [ expectation] , timeout: 1.0 )
@@ -199,6 +213,9 @@ class AsyncStreamTests: XCTestCase {
199
213
]
200
214
var receivedUpdates : [ Set < String > ] = [ ]
201
215
216
+ let listenerAttachedExpectation = self . expectation ( description: " Listener should be attached. " )
217
+ mockRealtime. listenerAttachedExpectation = listenerAttachedExpectation
218
+
202
219
let listeningTask = Task {
203
220
for try await update in config. updates {
204
221
receivedUpdates. append ( update. updatedKeys)
@@ -210,10 +227,9 @@ class AsyncStreamTests: XCTestCase {
210
227
return receivedUpdates
211
228
}
212
229
213
- try await Task . sleep ( nanoseconds : 100_000_000 )
230
+ await fulfillment ( of : [ listenerAttachedExpectation ] , timeout : 1.0 )
214
231
215
232
mockRealtime. sendUpdate ( keys: Array ( updatesToSend [ 0 ] ) )
216
- try await Task . sleep ( nanoseconds: 100_000_000 ) // Brief pause between sends
217
233
mockRealtime. sendUpdate ( keys: Array ( updatesToSend [ 1 ] ) )
218
234
219
235
await fulfillment ( of: [ expectation] , timeout: 2.0 )
0 commit comments