Add tests

This commit is contained in:
Adam Ward
2025-11-28 14:36:14 -05:00
parent 45fec7fbff
commit 60289cdfd6
5 changed files with 239 additions and 48 deletions

View File

@@ -139,6 +139,7 @@ package class MultiFileTestProject {
enableBackgroundIndexing: Bool = false,
usePullDiagnostics: Bool = true,
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
postInitialization: (@Sendable (InitializeResult) -> Void)? = nil,
testScratchDir overrideTestScratchDir: URL? = nil,
cleanUp: (@Sendable () -> Void)? = nil,
testName: String = #function
@@ -156,6 +157,7 @@ package class MultiFileTestProject {
enableBackgroundIndexing: enableBackgroundIndexing,
workspaceFolders: workspaces(scratchDirectory),
preInitialization: preInitialization,
postInitialization: postInitialization,
cleanUp: { [scratchDirectory] in
if cleanScratchDirectories {
try? FileManager.default.removeItem(at: scratchDirectory)

View File

@@ -190,6 +190,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
usePullDiagnostics: Bool = true,
pollIndex: Bool = true,
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
postInitialization: (@Sendable (InitializeResult) -> Void)? = nil,
cleanUp: (@Sendable () -> Void)? = nil,
testName: String = #function
) async throws {
@@ -231,6 +232,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
enableBackgroundIndexing: enableBackgroundIndexing,
usePullDiagnostics: usePullDiagnostics,
preInitialization: preInitialization,
postInitialization: postInitialization,
cleanUp: cleanUp,
testName: testName
)

View File

@@ -142,6 +142,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
enableBackgroundIndexing: Bool = false,
workspaceFolders: [WorkspaceFolder]? = nil,
preInitialization: ((TestSourceKitLSPClient) -> Void)? = nil,
postInitialization: (@Sendable (InitializeResult) -> Void)? = nil,
cleanUp: @Sendable @escaping () -> Void = {}
) async throws {
var options =
@@ -201,7 +202,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
if initialize {
let capabilities = capabilities
try await withTimeout(defaultTimeoutDuration) {
_ = try await self.send(
let initializeResult = try await self.send(
InitializeRequest(
processId: nil,
rootPath: nil,
@@ -212,6 +213,7 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
workspaceFolders: workspaceFolders
)
)
postInitialization?(initializeResult)
}
}
}

View File

@@ -16,53 +16,6 @@ import SKTestSupport
import ToolchainRegistry
import XCTest
fileprivate extension Toolchain {
#if compiler(>=6.4)
#warning(
"Once we require swift-play in the toolchain that's used to test SourceKit-LSP, we can just use `forTesting`"
)
#endif
static var forTestingWithSwiftPlay: Toolchain {
get async throws {
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
return Toolchain(
identifier: "\(toolchain.identifier)-swift-swift",
displayName: "\(toolchain.identifier) with swift-play",
path: toolchain.path,
clang: toolchain.clang,
swift: toolchain.swift,
swiftc: toolchain.swiftc,
swiftPlay: URL(fileURLWithPath: "/dummy/usr/bin/swift-play"),
clangd: toolchain.clangd,
sourcekitd: toolchain.sourcekitd,
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
libIndexStore: toolchain.libIndexStore
)
}
}
static var forTestingWithoutSwiftPlay: Toolchain {
get async throws {
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
return Toolchain(
identifier: "\(toolchain.identifier)-no-swift-swift",
displayName: "\(toolchain.identifier) without swift-play",
path: toolchain.path,
clang: toolchain.clang,
swift: toolchain.swift,
swiftc: toolchain.swiftc,
swiftPlay: nil,
clangd: toolchain.clangd,
sourcekitd: toolchain.sourcekitd,
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
libIndexStore: toolchain.libIndexStore
)
}
}
}
final class CodeLensTests: SourceKitLSPTestCase {
func testNoLenses() async throws {

View File

@@ -0,0 +1,232 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
@_spi(Testing) import BuildServerIntegration
import Foundation
@_spi(SourceKitLSP) import LanguageServerProtocol
import SKLogging
import SKTestSupport
import SemanticIndex
@_spi(Testing) import SourceKitLSP
import SwiftExtensions
import ToolchainRegistry
@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions
import XCTest
import struct TSCBasic.AbsolutePath
extension Toolchain {
#if compiler(>=6.4)
#warning(
"Once we require swift-play in the toolchain that's used to test SourceKit-LSP, we can just use `forTesting`"
)
#endif
static var forTestingWithSwiftPlay: Toolchain {
get async throws {
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
return Toolchain(
identifier: "\(toolchain.identifier)-swift-swift",
displayName: "\(toolchain.identifier) with swift-play",
path: toolchain.path,
clang: toolchain.clang,
swift: toolchain.swift,
swiftc: toolchain.swiftc,
swiftPlay: URL(fileURLWithPath: "/dummy/usr/bin/swift-play"),
clangd: toolchain.clangd,
sourcekitd: toolchain.sourcekitd,
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
libIndexStore: toolchain.libIndexStore
)
}
}
static var forTestingWithoutSwiftPlay: Toolchain {
get async throws {
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
return Toolchain(
identifier: "\(toolchain.identifier)-no-swift-swift",
displayName: "\(toolchain.identifier) without swift-play",
path: toolchain.path,
clang: toolchain.clang,
swift: toolchain.swift,
swiftc: toolchain.swiftc,
swiftPlay: nil,
clangd: toolchain.clangd,
sourcekitd: toolchain.sourcekitd,
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
libIndexStore: toolchain.libIndexStore
)
}
}
}
final class WorkspacePlaygroundDiscoveryTests: SourceKitLSPTestCase {
private var workspaceFiles: [RelativeFileLocation: String] = [
"Sources/MyLibrary/Test.swift": """
import Playgrounds
public func foo() -> String {
"bar"
}
1⃣#Playground("foo") {
print(foo())
}2
3⃣#Playground {
print(foo())
}4
public func bar(_ i: Int, _ j: Int) -> Int {
i + j
}
5⃣#Playground("bar") {
var i = bar(1, 2)
i = i + 1
print(i)
}6
""",
"Sources/MyLibrary/TestNoImport.swift": """
#Playground("fooNoImport") {
print(foo())
}
#Playground {
print(foo())
}
#Playground("barNoImport") {
var i = bar(1, 2)
i = i + 1
print(i)
}
""",
"Sources/MyLibrary/bar.swift": """
import Playgrounds
1⃣#Playground("bar2") {
print(foo())
}2
""",
"Sources/MyApp/baz.swift": """
import Playgrounds
1⃣#Playground("baz") {
print("baz")
}2
""",
]
private let packageManifestWithTestTarget = """
let package = Package(
name: "MyLibrary",
targets: [.target(name: "MyLibrary"), .target(name: "MyApp")]
)
"""
func testWorkspacePlaygroundsScanned() async throws {
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
let project = try await SwiftPMTestProject(
files: workspaceFiles,
manifest: packageManifestWithTestTarget,
toolchainRegistry: toolchainRegistry
)
let response = try await project.testClient.send(
WorkspacePlaygroundsRequest()
)
let (testUri, testPositions) = try project.openDocument("Test.swift")
let (barUri, barPositions) = try project.openDocument("bar.swift")
let (bazUri, bazPositions) = try project.openDocument("baz.swift")
// Notice sorted order
XCTAssertEqual(
response,
[
Playground(
id: "MyApp/baz.swift:3:2",
label: "baz",
location: .init(uri: bazUri, range: bazPositions["1"]..<bazPositions["2"]),
),
Playground(
id: "MyLibrary/Test.swift:7:1",
label: "foo",
location: .init(uri: testUri, range: testPositions["1"]..<testPositions["2"]),
),
Playground(
id: "MyLibrary/Test.swift:11:1",
label: nil,
location: .init(uri: testUri, range: testPositions["3"]..<testPositions["4"]),
),
Playground(
id: "MyLibrary/Test.swift:19:1",
label: "bar",
location: .init(uri: testUri, range: testPositions["5"]..<testPositions["6"]),
),
Playground(
id: "MyLibrary/bar.swift:3:1",
label: "bar2",
location: .init(uri: barUri, range: barPositions["1"]..<barPositions["2"]),
),
]
)
}
func testWorkspacePlaygroundsCapability() async throws {
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
let initializeResult = ThreadSafeBox<InitializeResult?>(initialValue: nil)
let _ = try await SwiftPMTestProject(
files: workspaceFiles,
manifest: packageManifestWithTestTarget,
toolchainRegistry: toolchainRegistry,
postInitialization: { result in
initializeResult.withLock {
$0 = result
}
}
)
switch initializeResult.value?.capabilities.experimental {
case .dictionary(let dict):
XCTAssertNotEqual(dict[WorkspacePlaygroundsRequest.method], nil)
default:
XCTFail("Experminental capabilities is not a dictionary")
}
}
func testWorkspacePlaygroundsCapabilityNoSwiftPlay() async throws {
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithoutSwiftPlay])
let initializeResult = ThreadSafeBox<InitializeResult?>(initialValue: nil)
let _ = try await SwiftPMTestProject(
files: workspaceFiles,
manifest: packageManifestWithTestTarget,
toolchainRegistry: toolchainRegistry,
postInitialization: { result in
initializeResult.withLock {
$0 = result
}
}
)
switch initializeResult.value?.capabilities.experimental {
case .dictionary(let dict):
XCTAssertEqual(dict[WorkspacePlaygroundsRequest.method], nil)
default:
XCTFail("Experminental capabilities is not a dictionary")
}
}
}