Skip to content

Commit 845e581

Browse files
committed
Add infrastructure to test which targets are being prepared
1 parent 9524753 commit 845e581

File tree

6 files changed

+71
-8
lines changed

6 files changed

+71
-8
lines changed

Sources/SKTestSupport/MultiFileTestProject.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class MultiFileTestProject {
8484
serverOptions: SourceKitLSPServer.Options = .testDefault,
8585
usePullDiagnostics: Bool = true,
8686
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
87+
cleanUp: (() -> Void)? = nil,
8788
testName: String = #function
8889
) async throws {
8990
scratchDirectory = try testScratchDir(testName: testName)
@@ -122,6 +123,7 @@ public class MultiFileTestProject {
122123
if cleanScratchDirectories {
123124
try? FileManager.default.removeItem(at: scratchDirectory)
124125
}
126+
cleanUp?()
125127
}
126128
)
127129
}

Sources/SKTestSupport/SwiftPMTestProject.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public class SwiftPMTestProject: MultiFileTestProject {
4747
pollIndex: Bool = true,
4848
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
4949
usePullDiagnostics: Bool = true,
50+
cleanUp: (() -> Void)? = nil,
5051
testName: String = #function
5152
) async throws {
5253
var filesByPath: [RelativeFileLocation: String] = [:]
@@ -72,6 +73,7 @@ public class SwiftPMTestProject: MultiFileTestProject {
7273
serverOptions: serverOptions,
7374
usePullDiagnostics: usePullDiagnostics,
7475
preInitialization: preInitialization,
76+
cleanUp: cleanUp,
7577
testName: testName
7678
)
7779

Sources/SemanticIndex/PreparationTaskDescription.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
3030
public let id = preparationIDForLogging.fetchAndIncrement()
3131

3232
/// The targets that should be prepared.
33-
private let targetsToPrepare: [ConfiguredTarget]
33+
public let targetsToPrepare: [ConfiguredTarget]
3434

3535
/// The build system manager that is used to get the toolchain and build settings for the files to index.
3636
private let buildSystemManager: BuildSystemManager
@@ -84,7 +84,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
8484
"Preparation failed: \(error.forLogging)"
8585
)
8686
}
87-
testHooks.preparationTaskDidFinish?(self)
87+
await testHooks.preparationTaskDidFinish?(self)
8888
logger.log(
8989
"Finished preparation in \(Date().timeIntervalSince(startDate) * 1000, privacy: .public)ms: \(targetsToPrepareDescription)"
9090
)

Sources/SemanticIndex/TestHooks.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313
/// Callbacks that allow inspection of internal state modifications during testing.
1414
public struct IndexTestHooks: Sendable {
15-
public var preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) -> Void)?
15+
public var preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) async -> Void)?
1616

1717
/// A callback that is called when an index task finishes.
18-
public var updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) -> Void)?
18+
public var updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) async -> Void)?
1919

2020
public init(
21-
preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) -> Void)? = nil,
22-
updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) -> Void)? = nil
21+
preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) async -> Void)? = nil,
22+
updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) async -> Void)? = nil
2323
) {
2424
self.preparationTaskDidFinish = preparationTaskDidFinish
2525
self.updateIndexStoreTaskDidFinish = updateIndexStoreTaskDidFinish

Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
9999
for file in filesToIndex {
100100
await updateIndexStoreForSingleFile(file)
101101
}
102-
testHooks.updateIndexStoreTaskDidFinish?(self)
102+
await testHooks.updateIndexStoreTaskDidFinish?(self)
103103
logger.log(
104104
"Finished updating index store in \(Date().timeIntervalSince(startDate) * 1000, privacy: .public)ms: \(filesToIndexDescription)"
105105
)

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,59 @@
1212

1313
import LSPTestSupport
1414
import LanguageServerProtocol
15+
import SKCore
1516
import SKTestSupport
17+
import SemanticIndex
1618
import SourceKitLSP
1719
import XCTest
1820

1921
fileprivate let backgroundIndexingOptions = SourceKitLSPServer.Options(
2022
indexOptions: IndexOptions(enableBackgroundIndexing: true)
2123
)
2224

25+
fileprivate actor ExpectedPreparationTracker {
26+
/// The targets we expect to be prepared. For targets within the same set, we don't care about the exact order.
27+
private var expectedPreparations: [Set<ConfiguredTarget>]
28+
29+
/// Implicitly-unwrapped optional so we can reference `self` when creating `IndexTestHooks`.
30+
/// `nonisolated(unsafe)` is fine because this is not modified after `testHooks` is created.
31+
nonisolated(unsafe) var testHooks: IndexTestHooks!
32+
33+
init(expectedPreparations: [Set<ConfiguredTarget>]) {
34+
self.expectedPreparations = expectedPreparations
35+
self.testHooks = IndexTestHooks(preparationTaskDidFinish: { [weak self] in
36+
await self?.preparationTaskDidFinish(taskDescription: $0)
37+
})
38+
}
39+
40+
func preparationTaskDidFinish(taskDescription: PreparationTaskDescription) -> Void {
41+
guard let expectedTargetsToPrepare = expectedPreparations.first else {
42+
XCTFail("Didn't expect a preparation but received \(taskDescription)")
43+
return
44+
}
45+
guard Set(taskDescription.targetsToPrepare).isSubset(of: expectedTargetsToPrepare) else {
46+
XCTFail("Received unexpected preparation of \(taskDescription)")
47+
return
48+
}
49+
let remainingExpectedTargetsToPrepare = expectedTargetsToPrepare.filter {
50+
!taskDescription.targetsToPrepare.contains($0)
51+
}
52+
if remainingExpectedTargetsToPrepare.isEmpty {
53+
expectedPreparations.remove(at: 0)
54+
} else {
55+
expectedPreparations[0] = remainingExpectedTargetsToPrepare
56+
}
57+
}
58+
59+
deinit {
60+
let expectedPreparations = self.expectedPreparations
61+
XCTAssert(
62+
expectedPreparations.isEmpty,
63+
"ExpectedPreparationTracker destroyed with unfulfilled expected preparations: \(expectedPreparations)."
64+
)
65+
}
66+
}
67+
2368
final class BackgroundIndexingTests: XCTestCase {
2469
func testBackgroundIndexingOfSingleFile() async throws {
2570
let project = try await SwiftPMTestProject(
@@ -508,6 +553,19 @@ final class BackgroundIndexingTests: XCTestCase {
508553

509554
func testPrepareTarget() async throws {
510555
try await SkipUnless.swiftpmStoresModulesInSubdirectory()
556+
var serverOptions = backgroundIndexingOptions
557+
let expectedPreparationTracker = ExpectedPreparationTracker(expectedPreparations: [
558+
[
559+
ConfiguredTarget(targetID: "LibA", runDestinationID: "dummy"),
560+
ConfiguredTarget(targetID: "LibB", runDestinationID: "dummy"),
561+
],
562+
[
563+
ConfiguredTarget(targetID: "LibA", runDestinationID: "dummy"),
564+
ConfiguredTarget(targetID: "LibB", runDestinationID: "dummy"),
565+
],
566+
])
567+
serverOptions.indexTestHooks = expectedPreparationTracker.testHooks
568+
511569
let project = try await SwiftPMTestProject(
512570
files: [
513571
"LibA/MyFile.swift": "",
@@ -531,7 +589,8 @@ final class BackgroundIndexingTests: XCTestCase {
531589
]
532590
)
533591
""",
534-
serverOptions: backgroundIndexingOptions
592+
serverOptions: serverOptions,
593+
cleanUp: { /* Keep expectedPreparationTracker alive */ _ = expectedPreparationTracker }
535594
)
536595

537596
let (uri, _) = try project.openDocument("MyOtherFile.swift")

0 commit comments

Comments
 (0)