@@ -50,6 +50,29 @@ fileprivate struct NotificationTimeoutError: Error, CustomStringConvertible {
5050 var description : String = " Failed to receive next notification within timeout "
5151}
5252
53+ /// A list of notifications that has been received by the SourceKit-LSP server but not handled from the test case yet.
54+ ///
55+ /// We can't use an `AsyncStream` for this because an `AsyncStream` is cancelled if a task that calls
56+ /// `AsyncStream.Iterator.next` is cancelled and we want to be able to wait for new notifications even if waiting for a
57+ /// a previous notification timed out.
58+ actor PendingNotifications {
59+ private var values : [ any NotificationType ] = [ ]
60+
61+ func add( _ value: any NotificationType ) {
62+ values. insert ( value, at: 0 )
63+ }
64+
65+ func next( timeout: Duration , pollingInterval: Duration = . milliseconds( 10 ) ) async throws -> any NotificationType {
66+ for _ in 0 ..< Int ( timeout. seconds / pollingInterval. seconds) {
67+ if let value = values. popLast ( ) {
68+ return value
69+ }
70+ try await Task . sleep ( for: pollingInterval)
71+ }
72+ throw NotificationTimeoutError ( )
73+ }
74+ }
75+
5376/// A mock SourceKit-LSP client (aka. a mock editor) that behaves like an editor
5477/// for testing purposes.
5578///
@@ -76,10 +99,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
7699 private let serverToClientConnection : LocalConnection
77100
78101 /// Stream of the notifications that the server has sent to the client.
79- private let notifications : AsyncStream < any NotificationType >
80-
81- /// Continuation to add a new notification from the ``server`` to the `notifications` stream.
82- private let notificationYielder : AsyncStream < any NotificationType > . Continuation
102+ private let notifications : PendingNotifications
83103
84104 /// The request handlers that have been set by `handleNextRequest`.
85105 ///
@@ -134,11 +154,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
134154 options. sourcekitdRequestTimeout = defaultTimeout
135155 }
136156
137- var notificationYielder: AsyncStream< any NotificationType> . Continuation!
138- self . notifications = AsyncStream { continuation in
139- notificationYielder = continuation
140- }
141- self. notificationYielder = notificationYielder
157+ self . notifications = PendingNotifications ( )
142158
143159 let serverToClientConnection = LocalConnection ( receiverName: " client " )
144160 self. serverToClientConnection = serverToClientConnection
@@ -248,26 +264,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
248264 /// - Note: This also returns any notifications sent before the call to
249265 /// `nextNotification`.
250266 package func nextNotification( timeout: Duration = . seconds( defaultTimeout) ) async throws -> any NotificationType {
251- // The task that gets the next notification from `self.notifications`.
252- let notificationYielder = Task {
253- for await notification in self . notifications {
254- return notification
255- }
256- throw NotificationTimeoutError ( )
257- }
258- // After `timeout`, we tell `notificationYielder` that we are no longer interested in its result by cancelling it.
259- // We wait for `notificationYielder` to accept this cancellation instead of returning immediately to avoid a
260- // situation where `notificationYielder` continues running, eats the first notification but it then never gets
261- // delivered to the test because we already delivered a timeout.
262- let cancellationTask = Task {
263- try await Task . sleep ( for: timeout)
264- notificationYielder. cancel ( )
265- }
266- defer {
267- // We have received a value and don't need the cancellationTask anymore
268- cancellationTask. cancel ( )
269- }
270- return try await notificationYielder. value
267+ return try await notifications. next ( timeout: timeout)
271268 }
272269
273270 /// Await the next diagnostic notification sent to the client.
@@ -340,8 +337,10 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
340337 // MARK: - Conformance to MessageHandler
341338
342339 /// - Important: Implementation detail of `TestSourceKitLSPServer`. Do not call from tests.
343- package func handle( _ params: some NotificationType ) {
344- notificationYielder. yield ( params)
340+ package func handle( _ notification: some NotificationType ) {
341+ Task {
342+ await notifications. add ( notification)
343+ }
345344 }
346345
347346 /// - Important: Implementation detail of `TestSourceKitLSPClient`. Do not call from tests.
0 commit comments