@@ -27,6 +27,10 @@ extension SourceKitLSPServer.Options {
27
27
public static let testDefault = Self ( swiftPublishDiagnosticsDebounceDuration: 0 )
28
28
}
29
29
30
+ fileprivate struct NotificationTimeoutError : Error , CustomStringConvertible {
31
+ var description : String = " Failed to receive next notification within timeout "
32
+ }
33
+
30
34
/// A mock SourceKit-LSP client (aka. a mock editor) that behaves like an editor
31
35
/// for testing purposes.
32
36
///
@@ -223,21 +227,17 @@ public final class TestSourceKitLSPClient: MessageHandler {
223
227
///
224
228
/// - Note: This also returns any notifications sent before the call to
225
229
/// `nextNotification`.
226
- public func nextNotification( timeout: TimeInterval = defaultTimeout) async throws -> any NotificationType {
227
- struct TimeoutError : Error , CustomStringConvertible {
228
- var description : String = " Failed to receive next notification within timeout "
229
- }
230
-
230
+ public func nextNotification( timeout: Duration = . seconds( defaultTimeout) ) async throws -> any NotificationType {
231
231
return try await withThrowingTaskGroup ( of: ( any NotificationType ) . self) { taskGroup in
232
232
taskGroup. addTask {
233
233
for await notification in self . notifications {
234
234
return notification
235
235
}
236
- throw TimeoutError ( )
236
+ throw NotificationTimeoutError ( )
237
237
}
238
238
taskGroup. addTask {
239
- try await Task . sleep ( nanoseconds : UInt64 ( timeout * 1_000_000_000 ) )
240
- throw TimeoutError ( )
239
+ try await Task . sleep ( for : timeout)
240
+ throw NotificationTimeoutError ( )
241
241
}
242
242
let result = try await taskGroup. next ( ) !
243
243
taskGroup. cancelAll ( )
@@ -250,7 +250,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
250
250
/// If the next notification is not a `PublishDiagnosticsNotification`, this
251
251
/// methods throws.
252
252
public func nextDiagnosticsNotification(
253
- timeout: TimeInterval = defaultTimeout
253
+ timeout: Duration = . seconds ( defaultTimeout)
254
254
) async throws -> PublishDiagnosticsNotification {
255
255
guard !usePullDiagnostics else {
256
256
struct PushDiagnosticsError : Error , CustomStringConvertible {
@@ -266,7 +266,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
266
266
public func nextNotification< ExpectedNotificationType: NotificationType > (
267
267
ofType: ExpectedNotificationType . Type ,
268
268
satisfying predicate: ( ExpectedNotificationType ) -> Bool = { _ in true } ,
269
- timeout: TimeInterval = defaultTimeout
269
+ timeout: Duration = . seconds ( defaultTimeout)
270
270
) async throws -> ExpectedNotificationType {
271
271
while true {
272
272
let nextNotification = try await nextNotification ( timeout: timeout)
@@ -276,6 +276,29 @@ public final class TestSourceKitLSPClient: MessageHandler {
276
276
}
277
277
}
278
278
279
+ /// Asserts that the test client does not receive a notification of the given type and satisfying the given predicate
280
+ /// within the given duration.
281
+ ///
282
+ /// For stable tests, the code that triggered the notification should be run before this assertion instead of relying
283
+ /// on the duration.
284
+ ///
285
+ /// The duration should not be 0 because we need to allow `nextNotification` some time to get the notification out of
286
+ /// the `notifications` `AsyncStream`.
287
+ public func assertDoesNotReceiveNotification< ExpectedNotificationType: NotificationType > (
288
+ ofType: ExpectedNotificationType . Type ,
289
+ satisfying predicate: ( ExpectedNotificationType ) -> Bool = { _ in true } ,
290
+ within duration: Duration = . seconds( 0.2 )
291
+ ) async throws {
292
+ do {
293
+ let notification = try await nextNotification (
294
+ ofType: ExpectedNotificationType . self,
295
+ satisfying: predicate,
296
+ timeout: duration
297
+ )
298
+ XCTFail ( " Did not expect to receive notification but received \( notification) " )
299
+ } catch is NotificationTimeoutError { }
300
+ }
301
+
279
302
/// Handle the next request of the given type that is sent to the client.
280
303
///
281
304
/// The request handler will only handle a single request. If the request is called again, the request handler won't
0 commit comments