2
2
//
3
3
// This source file is part of the Swift.org open source project
4
4
//
5
- // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5
+ // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
9
9
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
- import Foundation
13
+ import BuildSystemIntegration
14
14
import LanguageServerProtocolExtensions
15
15
import SKLogging
16
- import SKOptions
16
+ import SourceKitLSP
17
+ import SwiftExtensions
17
18
import ToolchainRegistry
19
+ import XCTest
18
20
19
21
#if compiler(>=6)
20
22
package import BuildServerProtocol
23
+ package import Foundation
21
24
package import LanguageServerProtocol
25
+ package import SKOptions
22
26
#else
23
27
import BuildServerProtocol
28
+ import Foundation
24
29
import LanguageServerProtocol
30
+ import SKOptions
25
31
#endif
26
32
27
- /// Build system to be used for testing BuildSystem and BuildSystemDelegate functionality with SourceKitLSPServer
28
- /// and other components.
29
- package actor TestBuildSystem : MessageHandler {
30
- private let connectionToSourceKitLSP : any Connection
31
-
32
- /// Build settings by file.
33
- private var buildSettingsByFile : [ DocumentURI : TextDocumentSourceKitOptionsResponse ] = [ : ]
34
-
35
- package func setBuildSettings( for uri: DocumentURI , to buildSettings: TextDocumentSourceKitOptionsResponse ? ) {
36
- buildSettingsByFile [ uri] = buildSettings
37
- connectionToSourceKitLSP. send ( OnBuildTargetDidChangeNotification ( changes: nil ) )
38
- }
33
+ // MARK: - CustomBuildServer
39
34
40
- private let initializeData : SourceKitInitializeBuildResponseData
35
+ /// A build server that can be injected into `CustomBuildServerTestProject`.
36
+ package protocol CustomBuildServer : MessageHandler {
37
+ init ( projectRoot: URL , connectionToSourceKitLSP: any Connection )
41
38
42
- package init (
43
- initializeData: SourceKitInitializeBuildResponseData = SourceKitInitializeBuildResponseData (
44
- sourceKitOptionsProvider: true
45
- ) ,
46
- connectionToSourceKitLSP: any Connection
47
- ) {
48
- self . initializeData = initializeData
49
- self . connectionToSourceKitLSP = connectionToSourceKitLSP
50
- }
39
+ func initializeBuildRequest( _ request: InitializeBuildRequest ) async throws -> InitializeBuildResponse
40
+ func onBuildInitialized( _ notification: OnBuildInitializedNotification ) throws
41
+ func buildShutdown( _ request: BuildShutdownRequest ) async throws -> VoidResponse
42
+ func onBuildExit( _ notification: OnBuildExitNotification ) throws
43
+ func workspaceBuildTargetsRequest(
44
+ _ request: WorkspaceBuildTargetsRequest
45
+ ) async throws -> WorkspaceBuildTargetsResponse
46
+ func buildTargetSourcesRequest( _ request: BuildTargetSourcesRequest ) async throws -> BuildTargetSourcesResponse
47
+ func textDocumentSourceKitOptionsRequest(
48
+ _ request: TextDocumentSourceKitOptionsRequest
49
+ ) async throws -> TextDocumentSourceKitOptionsResponse ?
50
+ func prepareTarget( _ request: BuildTargetPrepareRequest ) async throws -> VoidResponse
51
+ func waitForBuildSystemUpdates( request: WorkspaceWaitForBuildSystemUpdatesRequest ) async -> VoidResponse
52
+ nonisolated func onWatchedFilesDidChange( _ notification: OnWatchedFilesDidChangeNotification ) throws
53
+ func workspaceWaitForBuildSystemUpdatesRequest(
54
+ _ request: WorkspaceWaitForBuildSystemUpdatesRequest
55
+ ) async throws -> VoidResponse
56
+ nonisolated func cancelRequest( _ notification: CancelRequestNotification ) throws
57
+ }
51
58
59
+ extension CustomBuildServer {
52
60
package nonisolated func handle( _ notification: some NotificationType ) {
53
61
do {
54
62
switch notification {
@@ -64,7 +72,7 @@ package actor TestBuildSystem: MessageHandler {
64
72
throw ResponseError . methodNotFound ( type ( of: notification) . method)
65
73
}
66
74
} catch {
67
- logger. error ( " Error while handling BSP notification " )
75
+ logger. error ( " Error while handling BSP notification: \( error . forLogging ) " )
68
76
}
69
77
}
70
78
@@ -102,10 +110,18 @@ package actor TestBuildSystem: MessageHandler {
102
110
reply ( . failure( ResponseError . methodNotFound ( type ( of: request) . method) ) )
103
111
}
104
112
}
113
+ }
105
114
106
- func initializeBuildRequest( _ request: InitializeBuildRequest ) async throws -> InitializeBuildResponse {
107
- return InitializeBuildResponse (
108
- displayName: " TestBuildSystem " ,
115
+ package extension CustomBuildServer {
116
+ // MARK: Helper functions for the implementation of BSP methods
117
+
118
+ func initializationResponse(
119
+ initializeData: SourceKitInitializeBuildResponseData = SourceKitInitializeBuildResponseData (
120
+ sourceKitOptionsProvider: true
121
+ )
122
+ ) -> InitializeBuildResponse {
123
+ InitializeBuildResponse (
124
+ displayName: " \( type ( of: self ) ) " ,
109
125
version: " " ,
110
126
bspVersion: " 2.2.0 " ,
111
127
capabilities: BuildServerCapabilities ( ) ,
@@ -114,17 +130,25 @@ package actor TestBuildSystem: MessageHandler {
114
130
)
115
131
}
116
132
117
- nonisolated func onBuildInitialized( _ notification: OnBuildInitializedNotification ) throws {
118
- // Nothing to do
133
+ func dummyTargetSourcesResponse( _ files: some Sequence < DocumentURI > ) -> BuildTargetSourcesResponse {
134
+ return BuildTargetSourcesResponse ( items: [
135
+ SourcesItem ( target: . dummy, sources: files. map { SourceItem ( uri: $0, kind: . file, generated: false ) } )
136
+ ] )
119
137
}
120
138
139
+ // MARK: Default implementation for all build server methods that usually don't need customization.
140
+
141
+ func initializeBuildRequest( _ request: InitializeBuildRequest ) async throws -> InitializeBuildResponse {
142
+ return initializationResponse ( )
143
+ }
144
+
145
+ nonisolated func onBuildInitialized( _ notification: OnBuildInitializedNotification ) throws { }
146
+
121
147
func buildShutdown( _ request: BuildShutdownRequest ) async throws -> VoidResponse {
122
148
return VoidResponse ( )
123
149
}
124
150
125
- nonisolated func onBuildExit( _ notification: OnBuildExitNotification ) throws {
126
- // Nothing to do
127
- }
151
+ nonisolated func onBuildExit( _ notification: OnBuildExitNotification ) throws { }
128
152
129
153
func workspaceBuildTargetsRequest(
130
154
_ request: WorkspaceBuildTargetsRequest
@@ -142,32 +166,15 @@ package actor TestBuildSystem: MessageHandler {
142
166
] )
143
167
}
144
168
145
- func buildTargetSourcesRequest( _ request: BuildTargetSourcesRequest ) async throws -> BuildTargetSourcesResponse {
146
- return BuildTargetSourcesResponse ( items: [
147
- SourcesItem (
148
- target: . dummy,
149
- sources: buildSettingsByFile. keys. map { SourceItem ( uri: $0, kind: . file, generated: false ) }
150
- )
151
- ] )
152
- }
153
-
154
- func textDocumentSourceKitOptionsRequest(
155
- _ request: TextDocumentSourceKitOptionsRequest
156
- ) async throws -> TextDocumentSourceKitOptionsResponse ? {
157
- return buildSettingsByFile [ request. textDocument. uri]
158
- }
159
-
160
169
func prepareTarget( _ request: BuildTargetPrepareRequest ) async throws -> VoidResponse {
161
170
return VoidResponse ( )
162
171
}
163
172
164
- package func waitForBuildSystemUpdates( request: WorkspaceWaitForBuildSystemUpdatesRequest ) async -> VoidResponse {
173
+ func waitForBuildSystemUpdates( request: WorkspaceWaitForBuildSystemUpdatesRequest ) async -> VoidResponse {
165
174
return VoidResponse ( )
166
175
}
167
176
168
- nonisolated func onWatchedFilesDidChange( _ notification: OnWatchedFilesDidChangeNotification ) throws {
169
- // Not watching any files
170
- }
177
+ nonisolated func onWatchedFilesDidChange( _ notification: OnWatchedFilesDidChangeNotification ) throws { }
171
178
172
179
func workspaceWaitForBuildSystemUpdatesRequest(
173
180
_ request: WorkspaceWaitForBuildSystemUpdatesRequest
@@ -177,3 +184,40 @@ package actor TestBuildSystem: MessageHandler {
177
184
178
185
nonisolated func cancelRequest( _ notification: CancelRequestNotification ) throws { }
179
186
}
187
+
188
+ // MARK: - CustomBuildServerTestProject
189
+
190
+ /// A test project that launches a custom build server in-process.
191
+ ///
192
+ /// In contrast to `ExternalBuildServerTestProject`, the custom build system runs in-process and is implemented in
193
+ /// Swift.
194
+ package final class CustomBuildServerTestProject < BuildServer: CustomBuildServer > : MultiFileTestProject {
195
+ private let buildServerBox = ThreadSafeBox < BuildServer ? > ( initialValue: nil )
196
+
197
+ package init (
198
+ files: [ RelativeFileLocation : String ] ,
199
+ buildServer buildServerType: BuildServer . Type ,
200
+ options: SourceKitLSPOptions ? = nil ,
201
+ enableBackgroundIndexing: Bool = false ,
202
+ testName: String = #function
203
+ ) async throws {
204
+ let hooks : Hooks = Hooks (
205
+ buildSystemHooks: BuildSystemHooks ( injectBuildServer: { [ buildServerBox] projectRoot, connectionToSourceKitLSP in
206
+ let buildServer = BuildServer ( projectRoot: projectRoot, connectionToSourceKitLSP: connectionToSourceKitLSP)
207
+ buildServerBox. value = buildServer
208
+ return LocalConnection ( receiverName: " TestBuildSystem " , handler: buildServer)
209
+ } )
210
+ )
211
+ try await super. init (
212
+ files: files,
213
+ options: options,
214
+ hooks: hooks,
215
+ enableBackgroundIndexing: enableBackgroundIndexing,
216
+ testName: testName
217
+ )
218
+ }
219
+
220
+ package func buildServer( file: StaticString = #filePath, line: UInt = #line) throws -> BuildServer {
221
+ try XCTUnwrap ( buildServerBox. value, " Accessing build server before it has been created " , file: file, line: line)
222
+ }
223
+ }
0 commit comments