@@ -27,6 +27,10 @@ extension SourceKitLSPServer.Options {
2727 public static let testDefault = Self ( swiftPublishDiagnosticsDebounceDuration: 0 )
2828}
2929
30+ fileprivate struct NotificationTimeoutError : Error , CustomStringConvertible {
31+ var description : String = " Failed to receive next notification within timeout "
32+ }
33+
3034/// A mock SourceKit-LSP client (aka. a mock editor) that behaves like an editor
3135/// for testing purposes.
3236///
@@ -229,21 +233,17 @@ public final class TestSourceKitLSPClient: MessageHandler {
229233 ///
230234 /// - Note: This also returns any notifications sent before the call to
231235 /// `nextNotification`.
232- public func nextNotification( timeout: TimeInterval = defaultTimeout) async throws -> any NotificationType {
233- struct TimeoutError : Error , CustomStringConvertible {
234- var description : String = " Failed to receive next notification within timeout "
235- }
236-
236+ public func nextNotification( timeout: Duration = . seconds( defaultTimeout) ) async throws -> any NotificationType {
237237 return try await withThrowingTaskGroup ( of: ( any NotificationType ) . self) { taskGroup in
238238 taskGroup. addTask {
239239 for await notification in self . notifications {
240240 return notification
241241 }
242- throw TimeoutError ( )
242+ throw NotificationTimeoutError ( )
243243 }
244244 taskGroup. addTask {
245- try await Task . sleep ( nanoseconds : UInt64 ( timeout * 1_000_000_000 ) )
246- throw TimeoutError ( )
245+ try await Task . sleep ( for : timeout)
246+ throw NotificationTimeoutError ( )
247247 }
248248 let result = try await taskGroup. next ( ) !
249249 taskGroup. cancelAll ( )
@@ -256,7 +256,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
256256 /// If the next notification is not a `PublishDiagnosticsNotification`, this
257257 /// methods throws.
258258 public func nextDiagnosticsNotification(
259- timeout: TimeInterval = defaultTimeout
259+ timeout: Duration = . seconds ( defaultTimeout)
260260 ) async throws -> PublishDiagnosticsNotification {
261261 guard !usePullDiagnostics else {
262262 struct PushDiagnosticsError : Error , CustomStringConvertible {
@@ -272,7 +272,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
272272 public func nextNotification< ExpectedNotificationType: NotificationType > (
273273 ofType: ExpectedNotificationType . Type ,
274274 satisfying predicate: ( ExpectedNotificationType ) -> Bool = { _ in true } ,
275- timeout: TimeInterval = defaultTimeout
275+ timeout: Duration = . seconds ( defaultTimeout)
276276 ) async throws -> ExpectedNotificationType {
277277 while true {
278278 let nextNotification = try await nextNotification ( timeout: timeout)
@@ -282,6 +282,29 @@ public final class TestSourceKitLSPClient: MessageHandler {
282282 }
283283 }
284284
285+ /// Asserts that the test client does not receive a notification of the given type and satisfying the given predicate
286+ /// within the given duration.
287+ ///
288+ /// For stable tests, the code that triggered the notification should be run before this assertion instead of relying
289+ /// on the duration.
290+ ///
291+ /// The duration should not be 0 because we need to allow `nextNotification` some time to get the notification out of
292+ /// the `notifications` `AsyncStream`.
293+ public func assertDoesNotReceiveNotification< ExpectedNotificationType: NotificationType > (
294+ ofType: ExpectedNotificationType . Type ,
295+ satisfying predicate: ( ExpectedNotificationType ) -> Bool = { _ in true } ,
296+ within duration: Duration = . seconds( 0.2 )
297+ ) async throws {
298+ do {
299+ let notification = try await nextNotification (
300+ ofType: ExpectedNotificationType . self,
301+ satisfying: predicate,
302+ timeout: duration
303+ )
304+ XCTFail ( " Did not expect to receive notification but received \( notification) " )
305+ } catch is NotificationTimeoutError { }
306+ }
307+
285308 /// Handle the next request of the given type that is sent to the client.
286309 ///
287310 /// The request handler will only handle a single request. If the request is called again, the request handler won't
0 commit comments