@@ -52,6 +52,29 @@ fileprivate struct NotificationTimeoutError: Error, CustomStringConvertible {
5252 var description : String = " Failed to receive next notification within timeout "
5353}
5454
55+ /// A list of notifications that has been received by the SourceKit-LSP server but not handled from the test case yet.
56+ ///
57+ /// We can't use an `AsyncStream` for this because an `AsyncStream` is cancelled if a task that calls
58+ /// `AsyncStream.Iterator.next` is cancelled and we want to be able to wait for new notifications even if waiting for a
59+ /// a previous notification timed out.
60+ actor PendingNotifications {
61+ private var values : [ any NotificationType ] = [ ]
62+
63+ func add( _ value: any NotificationType ) {
64+ values. insert ( value, at: 0 )
65+ }
66+
67+ func next( timeout: Duration , pollingInterval: Duration = . milliseconds( 10 ) ) async throws -> any NotificationType {
68+ for _ in 0 ..< Int ( timeout. seconds / pollingInterval. seconds) {
69+ if let value = values. popLast ( ) {
70+ return value
71+ }
72+ try await Task . sleep ( for: pollingInterval)
73+ }
74+ throw NotificationTimeoutError ( )
75+ }
76+ }
77+
5578/// A mock SourceKit-LSP client (aka. a mock editor) that behaves like an editor
5679/// for testing purposes.
5780///
@@ -78,10 +101,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
78101 private let serverToClientConnection : LocalConnection
79102
80103 /// Stream of the notifications that the server has sent to the client.
81- private let notifications : AsyncStream < any NotificationType >
82-
83- /// Continuation to add a new notification from the ``server`` to the `notifications` stream.
84- private let notificationYielder : AsyncStream < any NotificationType > . Continuation
104+ private let notifications : PendingNotifications
85105
86106 /// The request handlers that have been set by `handleNextRequest`.
87107 ///
@@ -136,11 +156,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
136156 options. sourcekitdRequestTimeout = defaultTimeout
137157 }
138158
139- var notificationYielder: AsyncStream< any NotificationType> . Continuation!
140- self . notifications = AsyncStream { continuation in
141- notificationYielder = continuation
142- }
143- self. notificationYielder = notificationYielder
159+ self . notifications = PendingNotifications ( )
144160
145161 let serverToClientConnection = LocalConnection ( receiverName: " client " )
146162 self. serverToClientConnection = serverToClientConnection
@@ -250,26 +266,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
250266 /// - Note: This also returns any notifications sent before the call to
251267 /// `nextNotification`.
252268 package func nextNotification( timeout: Duration = . seconds( defaultTimeout) ) async throws -> any NotificationType {
253- // The task that gets the next notification from `self.notifications`.
254- let notificationYielder = Task {
255- for await notification in self . notifications {
256- return notification
257- }
258- throw NotificationTimeoutError ( )
259- }
260- // After `timeout`, we tell `notificationYielder` that we are no longer interested in its result by cancelling it.
261- // We wait for `notificationYielder` to accept this cancellation instead of returning immediately to avoid a
262- // situation where `notificationYielder` continues running, eats the first notification but it then never gets
263- // delivered to the test because we already delivered a timeout.
264- let cancellationTask = Task {
265- try await Task . sleep ( for: timeout)
266- notificationYielder. cancel ( )
267- }
268- defer {
269- // We have received a value and don't need the cancellationTask anymore
270- cancellationTask. cancel ( )
271- }
272- return try await notificationYielder. value
269+ return try await notifications. next ( timeout: timeout)
273270 }
274271
275272 /// Await the next diagnostic notification sent to the client.
@@ -342,8 +339,10 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
342339 // MARK: - Conformance to MessageHandler
343340
344341 /// - Important: Implementation detail of `TestSourceKitLSPServer`. Do not call from tests.
345- package func handle( _ params: some NotificationType ) {
346- notificationYielder. yield ( params)
342+ package func handle( _ notification: some NotificationType ) {
343+ Task {
344+ await notifications. add ( notification)
345+ }
347346 }
348347
349348 /// - Important: Implementation detail of `TestSourceKitLSPClient`. Do not call from tests.
0 commit comments