Skip to content

Commit 70f48e3

Browse files
committed
Don’t run a swift build command to prepare a package manifest
Package manifests don’t have an associated target to prepare and are represented by a `ConfiguredTarget` with an empty target ID. We were mistakingly running `swift build` with an empty target name to prepare them, which failed. There is nothing to prepare.
1 parent d439d12 commit 70f48e3

File tree

6 files changed

+65
-14
lines changed

6 files changed

+65
-14
lines changed

Sources/SKSwiftPMWorkspace/SwiftPMBuildSystem.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ extension SwiftPMBuildSystem: SKCore.BuildSystem {
508508
singleTarget target: ConfiguredTarget,
509509
indexProcessDidProduceResult: @Sendable (IndexProcessResult) -> Void
510510
) async throws {
511+
if target == .forPackageManifest {
512+
// Nothing to prepare for package manifests.
513+
return
514+
}
515+
511516
// TODO (indexing): Add a proper 'prepare' job in SwiftPM instead of building the target.
512517
// https://github.com/apple/sourcekit-lsp/issues/1254
513518
guard let toolchain = await toolchainRegistry.default else {

Sources/SKTestSupport/TestSourceKitLSPClient.swift

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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
///
@@ -223,21 +227,17 @@ public final class TestSourceKitLSPClient: MessageHandler {
223227
///
224228
/// - Note: This also returns any notifications sent before the call to
225229
/// `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 {
231231
return try await withThrowingTaskGroup(of: (any NotificationType).self) { taskGroup in
232232
taskGroup.addTask {
233233
for await notification in self.notifications {
234234
return notification
235235
}
236-
throw TimeoutError()
236+
throw NotificationTimeoutError()
237237
}
238238
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()
241241
}
242242
let result = try await taskGroup.next()!
243243
taskGroup.cancelAll()
@@ -250,7 +250,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
250250
/// If the next notification is not a `PublishDiagnosticsNotification`, this
251251
/// methods throws.
252252
public func nextDiagnosticsNotification(
253-
timeout: TimeInterval = defaultTimeout
253+
timeout: Duration = .seconds(defaultTimeout)
254254
) async throws -> PublishDiagnosticsNotification {
255255
guard !usePullDiagnostics else {
256256
struct PushDiagnosticsError: Error, CustomStringConvertible {
@@ -266,7 +266,7 @@ public final class TestSourceKitLSPClient: MessageHandler {
266266
public func nextNotification<ExpectedNotificationType: NotificationType>(
267267
ofType: ExpectedNotificationType.Type,
268268
satisfying predicate: (ExpectedNotificationType) -> Bool = { _ in true },
269-
timeout: TimeInterval = defaultTimeout
269+
timeout: Duration = .seconds(defaultTimeout)
270270
) async throws -> ExpectedNotificationType {
271271
while true {
272272
let nextNotification = try await nextNotification(timeout: timeout)
@@ -276,6 +276,29 @@ public final class TestSourceKitLSPClient: MessageHandler {
276276
}
277277
}
278278

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+
279302
/// Handle the next request of the given type that is sent to the client.
280303
///
281304
/// The request handler will only handle a single request. If the request is called again, the request handler won't

Tests/SourceKitDTests/CrashRecoveryTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class CrashRecoveryTests: XCTestCase {
9090

9191
await swiftLanguageService._crash()
9292

93-
let crashedNotification = try await testClient.nextNotification(ofType: WorkDoneProgress.self, timeout: 5)
93+
let crashedNotification = try await testClient.nextNotification(ofType: WorkDoneProgress.self, timeout: .seconds(5))
9494
XCTAssertEqual(
9595
crashedNotification.value,
9696
.begin(
@@ -107,7 +107,7 @@ final class CrashRecoveryTests: XCTestCase {
107107
_ = try? await testClient.send(hoverRequest)
108108
let semanticFunctionalityRestoredNotification = try await testClient.nextNotification(
109109
ofType: WorkDoneProgress.self,
110-
timeout: 30
110+
timeout: .seconds(30)
111111
)
112112
XCTAssertEqual(semanticFunctionalityRestoredNotification.value, .end(WorkDoneProgressEnd()))
113113

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,4 +868,27 @@ final class BackgroundIndexingTests: XCTestCase {
868868
let message = try await project.testClient.nextNotification(ofType: ShowMessageNotification.self)
869869
XCTAssert(message.message.contains("Background indexing"), "Received unexpected message: \(message.message)")
870870
}
871+
872+
func testDontPreparePackageManifest() async throws {
873+
let project = try await SwiftPMTestProject(
874+
files: [
875+
"Lib.swift": ""
876+
],
877+
enableBackgroundIndexing: true
878+
)
879+
880+
_ = try await project.testClient.nextNotification(
881+
ofType: LogMessageNotification.self,
882+
satisfying: { $0.message.contains("Preparing MyLibrary") }
883+
)
884+
885+
// Opening the package manifest shouldn't cause any `swift build` calls to prepare them because they are not part of
886+
// a target that can be prepared.
887+
let (uri, _) = try project.openDocument("Package.swift")
888+
_ = try await project.testClient.send(DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri)))
889+
try await project.testClient.assertDoesNotReceiveNotification(
890+
ofType: LogMessageNotification.self,
891+
satisfying: { $0.message.contains("Preparing") }
892+
)
893+
}
871894
}

Tests/SourceKitLSPTests/BuildSystemTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ final class BuildSystemTests: XCTestCase {
193193

194194
var receivedCorrectDiagnostic = false
195195
for _ in 0..<Int(defaultTimeout) {
196-
let refreshedDiags = try await testClient.nextDiagnosticsNotification(timeout: 1)
196+
let refreshedDiags = try await testClient.nextDiagnosticsNotification(timeout: .seconds(1))
197197
if refreshedDiags.diagnostics.count == 0, try text == documentManager.latestSnapshot(doc).text {
198198
receivedCorrectDiagnostic = true
199199
break

Tests/SourceKitLSPTests/LocalSwiftTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,6 @@ final class LocalSwiftTests: XCTestCase {
14271427
XCTAssertEqual(diag.message, "Cannot find 'bar' in scope")
14281428

14291429
// Ensure that we don't get a second `PublishDiagnosticsNotification`
1430-
await assertThrowsError(try await testClient.nextDiagnosticsNotification(timeout: 2))
1430+
await assertThrowsError(try await testClient.nextDiagnosticsNotification(timeout: .seconds(2)))
14311431
}
14321432
}

0 commit comments

Comments
 (0)