Skip to content

Commit d78c243

Browse files
authored
Merge pull request swiftlang#134 from benlangmuir/swiftpm-test
[test] Add a test for SwiftPM integration
2 parents a8472ad + 2e98e6a commit d78c243

File tree

7 files changed

+271
-0
lines changed

7 files changed

+271
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
import SourceKit
14+
import SKSwiftPMWorkspace
15+
import LanguageServerProtocol
16+
import SKCore
17+
import IndexStoreDB
18+
import ISDBTibs
19+
import ISDBTestSupport
20+
import Basic
21+
import SPMUtility
22+
import XCTest
23+
import Foundation
24+
25+
public final class SKSwiftPMTestWorkspace {
26+
27+
/// The directory containing the original, unmodified project.
28+
public let projectDir: URL
29+
30+
/// A test-specific directory that we can put temporary files into.
31+
public let tmpDir: URL
32+
33+
/// The build directory.
34+
public let buildDir: URL
35+
36+
/// The source files used by the test.
37+
public let sources: TestSources
38+
39+
/// The source code index.
40+
public var index: IndexStoreDB
41+
42+
/// The toolchain.
43+
public let toolchain: Toolchain
44+
45+
/// Connection to the language server.
46+
public let testServer: TestSourceKitServer = TestSourceKitServer(connectionKind: .local)
47+
48+
public var sk: TestClient { testServer.client }
49+
50+
public init(projectDir: URL, tmpDir: URL, toolchain: Toolchain) throws {
51+
self.projectDir = projectDir
52+
self.tmpDir = tmpDir
53+
self.toolchain = toolchain
54+
55+
let fm = FileManager.default
56+
_ = try? fm.removeItem(at: tmpDir)
57+
58+
buildDir = tmpDir.appendingPathComponent("build", isDirectory: true)
59+
try fm.createDirectory(at: buildDir, withIntermediateDirectories: true, attributes: nil)
60+
let sourceDir = tmpDir.appendingPathComponent("src", isDirectory: true)
61+
try fm.copyItem(at: projectDir, to: sourceDir)
62+
let databaseDir = tmpDir.appendingPathComponent("db", isDirectory: true)
63+
try fm.createDirectory(at: databaseDir, withIntermediateDirectories: true, attributes: nil)
64+
65+
self.sources = try TestSources(rootDirectory: sourceDir)
66+
67+
let sourcePath = AbsolutePath(sources.rootDirectory.path)
68+
let buildPath = AbsolutePath(buildDir.path)
69+
let buildSetup = BuildSetup(configuration: .debug, path: buildPath, flags: BuildFlags())
70+
71+
let swiftpm = try SwiftPMWorkspace(
72+
workspacePath: sourcePath,
73+
toolchainRegistry: ToolchainRegistry.shared,
74+
buildSetup: buildSetup)
75+
76+
let libIndexStore = try IndexStoreLibrary(dylibPath: toolchain.libIndexStore!.pathString)
77+
78+
try fm.createDirectory(atPath: swiftpm.indexStorePath!.pathString, withIntermediateDirectories: true)
79+
80+
self.index = try IndexStoreDB(
81+
storePath: swiftpm.indexStorePath!.pathString,
82+
databasePath: tmpDir.path,
83+
library: libIndexStore,
84+
listenToUnitEvents: false)
85+
86+
sk.allowUnexpectedNotification = true
87+
88+
testServer.server!.workspace = Workspace(
89+
rootPath: sourcePath,
90+
clientCapabilities: ClientCapabilities(),
91+
buildSettings: swiftpm,
92+
index: index,
93+
buildSetup: buildSetup)
94+
}
95+
96+
deinit {
97+
_ = try? FileManager.default.removeItem(atPath: tmpDir.path)
98+
}
99+
}
100+
101+
extension SKSwiftPMTestWorkspace {
102+
103+
public func testLoc(_ name: String) -> TestLocation { sources.locations[name]! }
104+
105+
public func buildAndIndex() throws {
106+
try build()
107+
index.pollForUnitChangesAndWait()
108+
}
109+
110+
public enum Error: Swift.Error {
111+
case buildFailure(Foundation.Process.TerminationReason, exitCode: Int32)
112+
}
113+
114+
func build() throws {
115+
let p = Process()
116+
p.launchPath = String(toolchain.swiftc!.pathString.dropLast())
117+
p.arguments = [
118+
"build",
119+
"--package-path", sources.rootDirectory.path,
120+
"--build-path", buildDir.path,
121+
"-Xswiftc", "-index-ignore-system-modules",
122+
"-Xcc", "-index-ignore-system-symbols",
123+
]
124+
125+
p.launch()
126+
p.waitUntilExit()
127+
if p.terminationReason != .exit || p.terminationStatus != 0 {
128+
throw Error.buildFailure(p.terminationReason, exitCode: p.terminationStatus)
129+
}
130+
}
131+
}
132+
133+
extension SKSwiftPMTestWorkspace {
134+
public func openDocument(_ url: URL, language: Language) throws {
135+
sk.send(DidOpenTextDocument(textDocument: TextDocumentItem(
136+
url: url,
137+
language: language,
138+
version: 1,
139+
text: try sources.sourceCache.get(url))))
140+
}
141+
}
142+
143+
extension XCTestCase {
144+
145+
public func staticSourceKitSwiftPMWorkspace(name: String, testFile: String = #file) throws -> SKSwiftPMTestWorkspace? {
146+
let testDirName = testDirectoryName
147+
let toolchain = ToolchainRegistry.shared.default!
148+
let workspace = try SKSwiftPMTestWorkspace(
149+
projectDir: inputsDirectory(testFile: testFile)
150+
.appendingPathComponent(name, isDirectory: true),
151+
tmpDir: URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
152+
.appendingPathComponent("sk-test-data/\(testDirName)", isDirectory: true),
153+
toolchain: toolchain)
154+
155+
let hasClangFile: Bool = workspace.sources.locations.contains { _, loc in
156+
loc.url.pathExtension != "swift"
157+
}
158+
159+
if hasClangFile {
160+
if toolchain.libIndexStore == nil {
161+
fputs("warning: skipping test because libIndexStore is missing;" +
162+
"install Clang's IndexStore component\n", stderr)
163+
return nil
164+
}
165+
if !TibsToolchain(toolchain).clangHasIndexSupport {
166+
fputs("warning: skipping test because '\(toolchain.clang!)' does not " +
167+
"have indexstore support; use swift-clang\n", stderr)
168+
return nil
169+
}
170+
}
171+
172+
return workspace
173+
}
174+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// swift-tools-version:5.1
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "SwiftPMPackage",
7+
products: [],
8+
dependencies: [],
9+
targets: [
10+
.target(
11+
name: "exec",
12+
dependencies: ["lib"]),
13+
.target(
14+
name: "lib",
15+
dependencies: []),
16+
]
17+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import lib
2+
3+
Lib() . /*Lib.foo:call*/foo()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public struct Lib {
2+
public func /*Lib.foo:def*/foo() {}
3+
4+
public init() {}
5+
}

Tests/SKSwiftPMWorkspaceTests/XCTestManifests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ extension SwiftPMWorkspaceTests {
1515
("testMultiTargetSwift", testMultiTargetSwift),
1616
("testNoPackage", testNoPackage),
1717
("testNoToolchain", testNoToolchain),
18+
("testSymlinkInWorkspaceCXX", testSymlinkInWorkspaceCXX),
19+
("testSymlinkInWorkspaceSwift", testSymlinkInWorkspaceSwift),
1820
("testUnknownFile", testUnknownFile),
1921
("testUnparsablePackage", testUnparsablePackage),
2022
]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
@testable import SourceKit
14+
import LanguageServerProtocol
15+
import SKTestSupport
16+
import XCTest
17+
18+
final class SwiftPMIntegrationTests: XCTestCase {
19+
20+
func testSwiftPMIntegration() throws {
21+
guard let ws = try staticSourceKitSwiftPMWorkspace(name: "SwiftPMPackage") else { return }
22+
try ws.buildAndIndex()
23+
24+
let call = ws.testLoc("Lib.foo:call")
25+
let def = ws.testLoc("Lib.foo:def")
26+
try ws.openDocument(call.url, language: .swift)
27+
let refs = try ws.sk.sendSync(ReferencesRequest(textDocument: call.docIdentifier, position: call.position))
28+
29+
XCTAssertEqual(Set(refs), [
30+
Location(call),
31+
Location(def),
32+
])
33+
34+
let completions = try ws.sk.sendSync(CompletionRequest(textDocument: call.docIdentifier, position: call.position))
35+
36+
XCTAssertEqual(completions, CompletionList(isIncomplete: false, items: [
37+
CompletionItem(
38+
label: "foo()",
39+
detail: "Void",
40+
sortText: nil,
41+
filterText: "foo()",
42+
textEdit: nil,
43+
insertText: "foo()",
44+
insertTextFormat: .plain,
45+
kind: .method,
46+
deprecated: nil),
47+
CompletionItem(
48+
label: "self",
49+
detail: "Lib",
50+
sortText: nil,
51+
filterText: "self",
52+
textEdit: nil,
53+
insertText: "self",
54+
insertTextFormat: .plain,
55+
kind: .keyword,
56+
deprecated: nil),
57+
]))
58+
}
59+
}

Tests/SourceKitTests/XCTestManifests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ extension LocalClangTests {
5454
// `swift test --generate-linuxmain`
5555
// to regenerate.
5656
static let __allTests__LocalClangTests = [
57+
("testClangStdHeaderCanary", testClangStdHeaderCanary),
5758
("testFoldingRange", testFoldingRange),
5859
("testSymbolInfo", testSymbolInfo),
5960
]
@@ -98,6 +99,15 @@ extension SwiftCompletionTests {
9899
]
99100
}
100101

102+
extension SwiftPMIntegrationTests {
103+
// DO NOT MODIFY: This is autogenerated, use:
104+
// `swift test --generate-linuxmain`
105+
// to regenerate.
106+
static let __allTests__SwiftPMIntegrationTests = [
107+
("testSwiftPMIntegration", testSwiftPMIntegration),
108+
]
109+
}
110+
101111
public func __allTests() -> [XCTestCaseEntry] {
102112
return [
103113
testCase(CodeActionTests.__allTests__CodeActionTests),
@@ -108,6 +118,7 @@ public func __allTests() -> [XCTestCaseEntry] {
108118
testCase(LocalSwiftTests.__allTests__LocalSwiftTests),
109119
testCase(SKTests.__allTests__SKTests),
110120
testCase(SwiftCompletionTests.__allTests__SwiftCompletionTests),
121+
testCase(SwiftPMIntegrationTests.__allTests__SwiftPMIntegrationTests),
111122
]
112123
}
113124
#endif

0 commit comments

Comments
 (0)