22//
33// This source file is part of the Swift.org open source project
44//
5- // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5+ // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66// Licensed under Apache License v2.0 with Runtime Library Exception
77//
88// See https://swift.org/LICENSE.txt for license information
99// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010//
1111//===----------------------------------------------------------------------===//
1212
13- import Foundation
13+ import BuildSystemIntegration
1414import LanguageServerProtocolExtensions
1515import SKLogging
16- import SKOptions
16+ import SourceKitLSP
17+ import SwiftExtensions
1718import ToolchainRegistry
19+ import XCTest
1820
1921#if compiler(>=6)
2022package import BuildServerProtocol
23+ package import Foundation
2124package import LanguageServerProtocol
25+ package import SKOptions
2226#else
2327import BuildServerProtocol
28+ import Foundation
2429import LanguageServerProtocol
30+ import SKOptions
2531#endif
2632
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
3934
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 )
4138
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+ }
5158
59+ extension CustomBuildServer {
5260 package nonisolated func handle( _ notification: some NotificationType ) {
5361 do {
5462 switch notification {
@@ -64,7 +72,7 @@ package actor TestBuildSystem: MessageHandler {
6472 throw ResponseError . methodNotFound ( type ( of: notification) . method)
6573 }
6674 } catch {
67- logger. error ( " Error while handling BSP notification " )
75+ logger. error ( " Error while handling BSP notification: \( error . forLogging ) " )
6876 }
6977 }
7078
@@ -102,10 +110,18 @@ package actor TestBuildSystem: MessageHandler {
102110 reply ( . failure( ResponseError . methodNotFound ( type ( of: request) . method) ) )
103111 }
104112 }
113+ }
105114
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 ) ) " ,
109125 version: " " ,
110126 bspVersion: " 2.2.0 " ,
111127 capabilities: BuildServerCapabilities ( ) ,
@@ -114,17 +130,25 @@ package actor TestBuildSystem: MessageHandler {
114130 )
115131 }
116132
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+ ] )
119137 }
120138
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+
121147 func buildShutdown( _ request: BuildShutdownRequest ) async throws -> VoidResponse {
122148 return VoidResponse ( )
123149 }
124150
125- nonisolated func onBuildExit( _ notification: OnBuildExitNotification ) throws {
126- // Nothing to do
127- }
151+ nonisolated func onBuildExit( _ notification: OnBuildExitNotification ) throws { }
128152
129153 func workspaceBuildTargetsRequest(
130154 _ request: WorkspaceBuildTargetsRequest
@@ -142,32 +166,15 @@ package actor TestBuildSystem: MessageHandler {
142166 ] )
143167 }
144168
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-
160169 func prepareTarget( _ request: BuildTargetPrepareRequest ) async throws -> VoidResponse {
161170 return VoidResponse ( )
162171 }
163172
164- package func waitForBuildSystemUpdates( request: WorkspaceWaitForBuildSystemUpdatesRequest ) async -> VoidResponse {
173+ func waitForBuildSystemUpdates( request: WorkspaceWaitForBuildSystemUpdatesRequest ) async -> VoidResponse {
165174 return VoidResponse ( )
166175 }
167176
168- nonisolated func onWatchedFilesDidChange( _ notification: OnWatchedFilesDidChangeNotification ) throws {
169- // Not watching any files
170- }
177+ nonisolated func onWatchedFilesDidChange( _ notification: OnWatchedFilesDidChangeNotification ) throws { }
171178
172179 func workspaceWaitForBuildSystemUpdatesRequest(
173180 _ request: WorkspaceWaitForBuildSystemUpdatesRequest
@@ -177,3 +184,40 @@ package actor TestBuildSystem: MessageHandler {
177184
178185 nonisolated func cancelRequest( _ notification: CancelRequestNotification ) throws { }
179186}
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