Skip to content

Commit 3f7d9a7

Browse files
committed
Fix a non-deterministic test failure in testPrepareTargetAfterEditToDependency
We could get a `DiagnosticsRefreshRequest` before the modified target had been re-prepared.
1 parent 9e49f67 commit 3f7d9a7

File tree

3 files changed

+28
-16
lines changed

3 files changed

+28
-16
lines changed

Sources/SKTestSupport/TestSourceKitLSPClient.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ public final class TestSourceKitLSPClient: MessageHandler {
6969
///
7070
/// Conceptually, this is an array of `RequestHandler<any RequestType>` but
7171
/// since we can't express this in the Swift type system, we use `[Any]`.
72-
private nonisolated(unsafe) var requestHandlers = ThreadSafeBox<[Any]>(initialValue: [])
72+
///
73+
/// `isOneShort` if the request handler should only serve a single request and should be removed from
74+
/// `requestHandlers` after it has been called.
75+
private nonisolated(unsafe) var requestHandlers: ThreadSafeBox<[(requestHandler: Any, isOneShot: Bool)]> =
76+
ThreadSafeBox(initialValue: [])
7377

7478
/// A closure that is called when the `TestSourceKitLSPClient` is destructed.
7579
///
@@ -263,7 +267,12 @@ public final class TestSourceKitLSPClient: MessageHandler {
263267
/// The request handler will only handle a single request. If the request is called again, the request handler won't
264268
/// call again
265269
public func handleSingleRequest<R: RequestType>(_ requestHandler: @escaping RequestHandler<R>) {
266-
requestHandlers.value.append(requestHandler)
270+
requestHandlers.value.append((requestHandler: requestHandler, isOneShot: true))
271+
}
272+
273+
/// Handle all requests of the given type that are sent to the client.
274+
public func handleMultipleRequests<R: RequestType>(_ requestHandler: @escaping RequestHandler<R>) {
275+
requestHandlers.value.append((requestHandler: requestHandler, isOneShot: false))
267276
}
268277

269278
// MARK: - Conformance to MessageHandler
@@ -280,19 +289,21 @@ public final class TestSourceKitLSPClient: MessageHandler {
280289
reply: @escaping (LSPResult<Request.Response>) -> Void
281290
) {
282291
requestHandlers.withLock { requestHandlers in
283-
let requestHandlerAndIndex = requestHandlers.enumerated().compactMap {
284-
(index, handler) -> (RequestHandler<Request>, Int)? in
285-
guard let handler = handler as? RequestHandler<Request> else {
292+
let requestHandlerIndexAndIsOneShot = requestHandlers.enumerated().compactMap {
293+
(index, handlerAndIsOneShot) -> (RequestHandler<Request>, Int, Bool)? in
294+
guard let handler = handlerAndIsOneShot.requestHandler as? RequestHandler<Request> else {
286295
return nil
287296
}
288-
return (handler, index)
297+
return (handler, index, handlerAndIsOneShot.isOneShot)
289298
}.first
290-
guard let (requestHandler, index) = requestHandlerAndIndex else {
299+
guard let (requestHandler, index, isOneShot) = requestHandlerIndexAndIsOneShot else {
291300
reply(.failure(.methodNotFound(Request.method)))
292301
return
293302
}
294303
reply(.success(requestHandler(params)))
295-
requestHandlers.remove(at: index)
304+
if isOneShot {
305+
requestHandlers.remove(at: index)
306+
}
296307
}
297308
}
298309

Sources/sourcekit-lsp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ target_link_libraries(sourcekit-lsp PRIVATE
55
Diagnose
66
LanguageServerProtocol
77
LanguageServerProtocolJSONRPC
8+
SemanticIndex
89
SKCore
910
SKSupport
1011
SourceKitLSP

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -569,16 +569,9 @@ final class BackgroundIndexingTests: XCTestCase {
569569
DidChangeWatchedFilesNotification(changes: [FileEvent(uri: try project.uri(for: "MyFile.swift"), type: .changed)])
570570
)
571571

572-
// Send a document request for `uri` to trigger re-preparation of its target. We don't actually care about the
573-
// response for this request. Instead, we wait until SourceKit-LSP sends us a `DiagnosticsRefreshRequest`,
574-
// indicating that the target of `uri` has been prepared.
575-
_ = try await project.testClient.send(
576-
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
577-
)
578-
579572
let receivedEmptyDiagnostics = self.expectation(description: "Received diagnostic refresh request")
580573

581-
project.testClient.handleSingleRequest { (_: DiagnosticsRefreshRequest) in
574+
project.testClient.handleMultipleRequests { (_: DiagnosticsRefreshRequest) in
582575
Task {
583576
let updatedDiagnostics = try await project.testClient.send(
584577
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
@@ -594,6 +587,13 @@ final class BackgroundIndexingTests: XCTestCase {
594587
return VoidResponse()
595588
}
596589

590+
// Send a document request for `uri` to trigger re-preparation of its target. We don't actually care about the
591+
// response for this request. Instead, we wait until SourceKit-LSP sends us a `DiagnosticsRefreshRequest`,
592+
// indicating that the target of `uri` has been prepared.
593+
_ = try await project.testClient.send(
594+
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
595+
)
596+
597597
try await fulfillmentOfOrThrow([receivedEmptyDiagnostics])
598598
}
599599

0 commit comments

Comments
 (0)