Skip to content

Commit 9524753

Browse files
committed
Introduce test hooks that can be used to monitor when preparation and index tasks finish
1 parent 2d27d57 commit 9524753

9 files changed

+60
-26
lines changed

Sources/SemanticIndex/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_library(SemanticIndex STATIC
44
IndexTaskDescription.swift
55
PreparationTaskDescription.swift
66
SemanticIndexManager.swift
7+
TestHooks.swift
78
UpdateIndexStoreTaskDescription.swift
89
)
910
set_target_properties(SemanticIndex PROPERTIES

Sources/SemanticIndex/PreparationTaskDescription.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public struct PreparationTaskDescription: IndexTaskDescription {
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
3737

38+
/// Test hooks that should be called when the preparation task finishes.
39+
private let testHooks: IndexTestHooks
40+
3841
/// The task is idempotent because preparing the same target twice produces the same result as preparing it once.
3942
public var isIdempotent: Bool { true }
4043

@@ -50,10 +53,12 @@ public struct PreparationTaskDescription: IndexTaskDescription {
5053

5154
init(
5255
targetsToPrepare: [ConfiguredTarget],
53-
buildSystemManager: BuildSystemManager
56+
buildSystemManager: BuildSystemManager,
57+
testHooks: IndexTestHooks
5458
) {
5559
self.targetsToPrepare = targetsToPrepare
5660
self.buildSystemManager = buildSystemManager
61+
self.testHooks = testHooks
5762
}
5863

5964
public func execute() async {
@@ -79,6 +84,7 @@ public struct PreparationTaskDescription: IndexTaskDescription {
7984
"Preparation failed: \(error.forLogging)"
8085
)
8186
}
87+
testHooks.preparationTaskDidFinish?(self)
8288
logger.log(
8389
"Finished preparation in \(Date().timeIntervalSince(startDate) * 1000, privacy: .public)ms: \(targetsToPrepareDescription)"
8490
)

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public final actor SemanticIndexManager {
6060
/// The build system manager that is used to get compiler arguments for a file.
6161
private let buildSystemManager: BuildSystemManager
6262

63+
private let testHooks: IndexTestHooks
64+
6365
/// The task to generate the build graph (resolving package dependencies, generating the build description,
6466
/// ...). `nil` if no build graph is currently being generated.
6567
private var generateBuildGraphTask: Task<Void, Never>?
@@ -121,12 +123,14 @@ public final actor SemanticIndexManager {
121123
public init(
122124
index: UncheckedIndex,
123125
buildSystemManager: BuildSystemManager,
126+
testHooks: IndexTestHooks,
124127
indexTaskScheduler: TaskScheduler<AnyIndexTaskDescription>,
125128
indexTasksWereScheduled: @escaping @Sendable (Int) -> Void,
126129
indexTaskDidFinish: @escaping @Sendable () -> Void
127130
) {
128131
self.index = index
129132
self.buildSystemManager = buildSystemManager
133+
self.testHooks = testHooks
130134
self.indexTaskScheduler = indexTaskScheduler
131135
self.indexTasksWereScheduled = indexTasksWereScheduled
132136
self.indexTaskDidFinish = indexTaskDidFinish
@@ -296,7 +300,8 @@ public final actor SemanticIndexManager {
296300
let taskDescription = AnyIndexTaskDescription(
297301
PreparationTaskDescription(
298302
targetsToPrepare: targetsToPrepare,
299-
buildSystemManager: self.buildSystemManager
303+
buildSystemManager: self.buildSystemManager,
304+
testHooks: testHooks
300305
)
301306
)
302307
if !targetsToPrepare.isEmpty {
@@ -355,7 +360,8 @@ public final actor SemanticIndexManager {
355360
UpdateIndexStoreTaskDescription(
356361
filesToIndex: Set(files),
357362
buildSystemManager: self.buildSystemManager,
358-
index: index
363+
index: index,
364+
testHooks: testHooks
359365
)
360366
)
361367
let updateIndexStoreTask = await self.indexTaskScheduler.schedule(priority: priority, taskDescription) { newState in

Sources/SemanticIndex/TestHooks.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Callbacks that allow inspection of internal state modifications during testing.
14+
public struct IndexTestHooks: Sendable {
15+
public var preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) -> Void)?
16+
17+
/// A callback that is called when an index task finishes.
18+
public var updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) -> Void)?
19+
20+
public init(
21+
preparationTaskDidFinish: (@Sendable (PreparationTaskDescription) -> Void)? = nil,
22+
updateIndexStoreTaskDidFinish: (@Sendable (UpdateIndexStoreTaskDescription) -> Void)? = nil
23+
) {
24+
self.preparationTaskDidFinish = preparationTaskDidFinish
25+
self.updateIndexStoreTaskDidFinish = updateIndexStoreTaskDidFinish
26+
}
27+
}

Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
4949
/// case we don't need to index it again.
5050
private let index: UncheckedIndex
5151

52+
/// Test hooks that should be called when the index task finishes.
53+
private let testHooks: IndexTestHooks
54+
5255
/// The task is idempotent because indexing the same file twice produces the same result as indexing it once.
5356
public var isIdempotent: Bool { true }
5457

@@ -65,11 +68,13 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
6568
init(
6669
filesToIndex: Set<FileToIndex>,
6770
buildSystemManager: BuildSystemManager,
68-
index: UncheckedIndex
71+
index: UncheckedIndex,
72+
testHooks: IndexTestHooks
6973
) {
7074
self.filesToIndex = filesToIndex
7175
self.buildSystemManager = buildSystemManager
7276
self.index = index
77+
self.testHooks = testHooks
7378
}
7479

7580
public func execute() async {
@@ -94,6 +99,7 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
9499
for file in filesToIndex {
95100
await updateIndexStoreForSingleFile(file)
96101
}
102+
testHooks.updateIndexStoreTaskDidFinish?(self)
97103
logger.log(
98104
"Finished updating index store in \(Date().timeIntervalSince(startDate) * 1000, privacy: .public)ms: \(filesToIndexDescription)"
99105
)

Sources/SourceKitLSP/SourceKitLSPServer+Options.swift

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Foundation
1414
import LanguageServerProtocol
1515
import SKCore
1616
import SKSupport
17+
import SemanticIndex
1718

1819
import struct TSCBasic.AbsolutePath
1920
import struct TSCBasic.RelativePath
@@ -22,16 +23,6 @@ extension SourceKitLSPServer {
2223

2324
/// Configuration options for the SourceKitServer.
2425
public struct Options: Sendable {
25-
/// Callbacks that allow inspection of internal state modifications during testing.
26-
public struct TestHooks: Sendable {
27-
/// A callback that is called when an index task finishes.
28-
public var indexTaskDidFinish: (@Sendable () -> Void)?
29-
30-
public init(indexTaskDidFinish: (@Sendable () -> Void)? = nil) {
31-
self.indexTaskDidFinish = indexTaskDidFinish
32-
}
33-
}
34-
3526
/// Additional compiler flags (e.g. `-Xswiftc` for SwiftPM projects) and other build-related
3627
/// configuration.
3728
public var buildSetup: BuildSetup
@@ -58,7 +49,7 @@ extension SourceKitLSPServer {
5849
/// notification when running unit tests.
5950
public var swiftPublishDiagnosticsDebounceDuration: TimeInterval
6051

61-
public var testHooks: TestHooks
52+
public var indexTestHooks: IndexTestHooks
6253

6354
public init(
6455
buildSetup: BuildSetup = .default,
@@ -68,7 +59,7 @@ extension SourceKitLSPServer {
6859
completionOptions: SKCompletionOptions = .init(),
6960
generatedInterfacesPath: AbsolutePath = defaultDirectoryForGeneratedInterfaces,
7061
swiftPublishDiagnosticsDebounceDuration: TimeInterval = 2, /* 2s */
71-
testHooks: TestHooks = TestHooks()
62+
indexTestHooks: IndexTestHooks = IndexTestHooks()
7263
) {
7364
self.buildSetup = buildSetup
7465
self.clangdOptions = clangdOptions
@@ -77,7 +68,7 @@ extension SourceKitLSPServer {
7768
self.completionOptions = completionOptions
7869
self.generatedInterfacesPath = generatedInterfacesPath
7970
self.swiftPublishDiagnosticsDebounceDuration = swiftPublishDiagnosticsDebounceDuration
80-
self.testHooks = testHooks
71+
self.indexTestHooks = indexTestHooks
8172
}
8273
}
8374
}

Sources/SourceKitLSP/SourceKitLSPServer.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,6 @@ extension SourceKitLSPServer {
12111211
logger.log("Cannot open workspace before server is initialized")
12121212
return nil
12131213
}
1214-
let indexTaskDidFinishCallback = options.testHooks.indexTaskDidFinish
12151214
var options = self.options
12161215
options.buildSetup = self.options.buildSetup.merging(buildSetup(for: workspaceFolder))
12171216
return try? await Workspace(
@@ -1237,7 +1236,6 @@ extension SourceKitLSPServer {
12371236
},
12381237
indexTaskDidFinish: { [weak self] in
12391238
self?.indexProgressManager.indexStatusDidChange()
1240-
indexTaskDidFinishCallback?()
12411239
}
12421240
)
12431241
}
@@ -1286,7 +1284,6 @@ extension SourceKitLSPServer {
12861284
if self.workspaces.isEmpty {
12871285
logger.error("no workspace found")
12881286

1289-
let indexTaskDidFinishCallback = self.options.testHooks.indexTaskDidFinish
12901287
let workspace = await Workspace(
12911288
documentManager: self.documentManager,
12921289
rootUri: req.rootURI,
@@ -1302,7 +1299,6 @@ extension SourceKitLSPServer {
13021299
},
13031300
indexTaskDidFinish: { [weak self] in
13041301
self?.indexProgressManager.indexStatusDidChange()
1305-
indexTaskDidFinishCallback?()
13061302
}
13071303
)
13081304

Sources/SourceKitLSP/Workspace.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public final class Workspace: Sendable {
112112
self.semanticIndexManager = SemanticIndexManager(
113113
index: uncheckedIndex,
114114
buildSystemManager: buildSystemManager,
115+
testHooks: options.indexTestHooks,
115116
indexTaskScheduler: indexTaskScheduler,
116117
indexTasksWereScheduled: indexTasksWereScheduled,
117118
indexTaskDidFinish: indexTaskDidFinish

Tests/SourceKitLSPTests/BackgroundIndexingTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,11 @@ final class BackgroundIndexingTests: XCTestCase {
165165

166166
func testBackgroundIndexingHappensWithLowPriority() async throws {
167167
var serverOptions = backgroundIndexingOptions
168-
serverOptions.testHooks.indexTaskDidFinish = {
169-
XCTAssert(
170-
Task.currentPriority == .low,
171-
"An index task ran with priority \(Task.currentPriority)"
172-
)
168+
serverOptions.indexTestHooks.preparationTaskDidFinish = { taskDescription in
169+
XCTAssert(Task.currentPriority == .low, "\(taskDescription) ran with priority \(Task.currentPriority)")
170+
}
171+
serverOptions.indexTestHooks.updateIndexStoreTaskDidFinish = { taskDescription in
172+
XCTAssert(Task.currentPriority == .low, "\(taskDescription) ran with priority \(Task.currentPriority)")
173173
}
174174
let project = try await SwiftPMTestProject(
175175
files: [

0 commit comments

Comments
 (0)