Files
sourcekit-lsp/Tests/BuildSystemIntegrationTests/CompilationDatabaseTests.swift
2024-11-05 21:04:01 -08:00

462 lines
13 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
import BuildServerProtocol
import BuildSystemIntegration
import LanguageServerProtocol
import SKSupport
import SKTestSupport
import SwiftExtensions
import TSCBasic
import XCTest
final class CompilationDatabaseTests: XCTestCase {
func testEncodeCompDBCommand() throws {
// Requires JSONEncoder.OutputFormatting.sortedKeys
func check(
_ cmd: CompilationDatabase.Command,
_ expected: String,
file: StaticString = #filePath,
line: UInt = #line
) throws {
let encoder = JSONEncoder()
encoder.outputFormatting.insert(.sortedKeys)
let encodedString = try String(data: encoder.encode(cmd), encoding: .utf8)
XCTAssertEqual(encodedString, expected, file: file, line: line)
}
try check(
.init(directory: "a", filename: "b", commandLine: [], output: "c"),
"""
{"arguments":[],"directory":"a","file":"b","output":"c"}
"""
)
try check(
.init(directory: "a", filename: "b", commandLine: ["c", "d"], output: nil),
"""
{"arguments":["c","d"],"directory":"a","file":"b"}
"""
)
}
func testDecodeCompDBCommand() throws {
func check(
_ str: String,
_ expected: CompilationDatabase.Command,
file: StaticString = #filePath,
line: UInt = #line
) throws {
let cmd = try JSONDecoder().decode(CompilationDatabase.Command.self, from: str.data(using: .utf8)!)
XCTAssertEqual(cmd, expected, file: file, line: line)
}
try check(
"""
{
"arguments" : [
],
"directory" : "a",
"file" : "b",
"output" : "c"
}
""",
.init(directory: "a", filename: "b", commandLine: [], output: "c")
)
try check(
"""
{
"arguments" : [
"c",
"d"
],
"directory" : "a",
"file" : "b"
}
""",
.init(directory: "a", filename: "b", commandLine: ["c", "d"], output: nil)
)
try check(
"""
{
"directory":"a",
"file":"b.cpp",
"command": "/usr/bin/clang++ -std=c++11 -DFOO b.cpp"
}
""",
.init(
directory: "a",
filename: "b.cpp",
commandLine: [
"/usr/bin/clang++",
"-std=c++11",
"-DFOO",
"b.cpp",
],
output: nil
)
)
XCTAssertThrowsError(
try JSONDecoder().decode(
CompilationDatabase.Command.self,
from: """
{"directory":"a","file":"b"}
""".data(using: .utf8)!
)
)
}
func testJSONCompilationDatabaseCoding() {
checkCoding(
JSONCompilationDatabase([]),
json: """
[
]
"""
)
let db = JSONCompilationDatabase([
.init(directory: "a", filename: "b", commandLine: [], output: nil),
.init(directory: "c", filename: "b", commandLine: [], output: nil),
])
checkCoding(
db,
json: """
[
{
"arguments" : [
],
"directory" : "a",
"file" : "b"
},
{
"arguments" : [
],
"directory" : "c",
"file" : "b"
}
]
"""
)
}
func testJSONCompilationDatabaseLookup() throws {
#if os(Windows)
let fileSystemRoot = "c:/"
#else
let fileSystemRoot = "/"
#endif
let cmd1 = CompilationDatabase.Command(directory: "a", filename: "b", commandLine: [], output: nil)
let cmd2 = CompilationDatabase.Command(directory: "\(fileSystemRoot)c", filename: "b", commandLine: [], output: nil)
let cmd3 = CompilationDatabase.Command(
directory: "\(fileSystemRoot)c",
filename: "\(fileSystemRoot)b",
commandLine: [],
output: nil
)
let db = JSONCompilationDatabase([cmd1, cmd2, cmd3])
XCTAssertEqual(db[DocumentURI(filePath: "b", isDirectory: false)], [cmd1])
XCTAssertEqual(db[DocumentURI(filePath: "\(fileSystemRoot)c/b", isDirectory: false)], [cmd2])
XCTAssertEqual(db[DocumentURI(filePath: "\(fileSystemRoot)b", isDirectory: false)], [cmd3])
}
func testJSONCompilationDatabaseFromDirectory() throws {
let fs = InMemoryFileSystem()
try fs.createDirectory(AbsolutePath(validating: "/a"))
XCTAssertNil(
try tryLoadCompilationDatabase(
directory: AbsolutePath(validating: "/a"),
fs
)
)
try fs.writeFileContents(
AbsolutePath(validating: "/a/compile_commands.json"),
bytes: """
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "/a/a.swift"]
}
]
"""
)
XCTAssertNotNil(
try tryLoadCompilationDatabase(
directory: AbsolutePath(validating: "/a"),
fs
)
)
}
func testJSONCompilationDatabaseFromCustomDirectory() throws {
let fs = InMemoryFileSystem()
let root = try AbsolutePath(validating: "/a")
try fs.createDirectory(root)
XCTAssertNil(tryLoadCompilationDatabase(directory: root, fs))
let customDir = try RelativePath(validating: "custom/build/dir")
try fs.createDirectory(root.appending(customDir), recursive: true)
try fs.writeFileContents(
root
.appending(customDir)
.appending(component: "compile_commands.json"),
bytes: """
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "/a/a.swift"]
}
]
"""
)
XCTAssertNotNil(
try tryLoadCompilationDatabase(
directory: AbsolutePath(validating: "/a"),
additionalSearchPaths: [
RelativePath(validating: "."),
customDir,
],
fs
)
)
}
func testFixedCompilationDatabase() throws {
let fs = InMemoryFileSystem()
try fs.createDirectory(try AbsolutePath(validating: "/a"))
XCTAssertNil(
try tryLoadCompilationDatabase(
directory: AbsolutePath(validating: "/a"),
fs
)
)
try fs.writeFileContents(
try AbsolutePath(validating: "/a/compile_flags.txt"),
bytes: """
-xc++
-I
libwidget/include/
"""
)
let db = try XCTUnwrap(tryLoadCompilationDatabase(directory: AbsolutePath(validating: "/a"), fs))
// Note: Use `AbsolutePath(validating:).pathString` to normalize forward slashes to backslashes on Windows
XCTAssertEqual(
db[DocumentURI(filePath: "/a/b", isDirectory: false)],
[
CompilationDatabase.Command(
directory: try AbsolutePath(validating: "/a").pathString,
filename: try AbsolutePath(validating: "/a/b").pathString,
commandLine: ["clang", "-xc++", "-I", "libwidget/include/", try AbsolutePath(validating: "/a/b").pathString],
output: nil
)
]
)
}
func testInvalidCompilationDatabase() throws {
let fs = InMemoryFileSystem()
let dir = try AbsolutePath(validating: "/a")
try fs.createDirectory(dir)
try fs.writeFileContents(dir.appending(component: "compile_commands.json"), bytes: "")
XCTAssertNil(tryLoadCompilationDatabase(directory: dir, fs))
}
func testCompilationDatabaseBuildSystem() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift"]
}
]
"""
) { buildSystem in
let settings = try await buildSystem.sourceKitOptions(
request: TextDocumentSourceKitOptionsRequest(
textDocument: TextDocumentIdentifier(DocumentURI(URL(fileURLWithPath: "/a/a.swift"))),
target: BuildTargetIdentifier.dummy,
language: .swift
)
)
XCTAssertNotNil(settings)
XCTAssertEqual(settings?.workingDirectory, "/a")
XCTAssertEqual(settings?.compilerArguments, ["-swift-version", "4", "/a/a.swift"])
assertNil(await buildSystem.indexStorePath)
assertNil(await buildSystem.indexDatabasePath)
}
}
func testCompilationDatabaseBuildSystemIndexStoreSwift0() async throws {
try await checkCompilationDatabaseBuildSystem("[]") { buildSystem in
assertNil(await buildSystem.indexStorePath)
}
}
func testCompilationDatabaseBuildSystemIndexStoreSwift1() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift", "-index-store-path", "/b"]
}
]
"""
) { buildSystem in
assertEqual(
try URL(fileURLWithPath: await buildSystem.indexStorePath?.pathString ?? "").filePath,
"\(pathSeparator)b"
)
assertEqual(
try URL(fileURLWithPath: await buildSystem.indexDatabasePath?.pathString ?? "").filePath,
"\(pathSeparator)IndexDatabase"
)
}
}
func testCompilationDatabaseBuildSystemIndexStoreSwift2() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/a.swift"]
},
{
"file": "/a/b.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/b.swift"]
},
{
"file": "/a/c.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/c.swift", "-index-store-path", "/b"]
}
]
"""
) { buildSystem in
await assertEqual(buildSystem.indexStorePath, try AbsolutePath(validating: "/b"))
}
}
func testCompilationDatabaseBuildSystemIndexStoreSwift3() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "-index-store-path", "/b", "-swift-version", "4", "/a/a.swift"]
}
]
"""
) { buildSystem in
assertEqual(await buildSystem.indexStorePath, try AbsolutePath(validating: "/b"))
}
}
func testCompilationDatabaseBuildSystemIndexStoreSwift4() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.swift",
"directory": "/a",
"arguments": ["swiftc", "-swift-version", "4", "/a/c.swift", "-index-store-path"]
}
]
"""
) { buildSystem in
assertNil(await buildSystem.indexStorePath)
}
}
func testCompilationDatabaseBuildSystemIndexStoreClang() async throws {
try await checkCompilationDatabaseBuildSystem(
"""
[
{
"file": "/a/a.cpp",
"directory": "/a",
"arguments": ["clang", "/a/a.cpp"]
},
{
"file": "/a/b.cpp",
"directory": "/a",
"arguments": ["clang", "/a/b.cpp"]
},
{
"file": "/a/c.cpp",
"directory": "/a",
"arguments": ["clang", "/a/c.cpp", "-index-store-path", "/b"]
}
]
"""
) { buildSystem in
assertEqual(
try URL(fileURLWithPath: await buildSystem.indexStorePath?.pathString ?? "").filePath,
"\(pathSeparator)b"
)
assertEqual(
try URL(fileURLWithPath: await buildSystem.indexDatabasePath?.pathString ?? "").filePath,
"\(pathSeparator)IndexDatabase"
)
}
}
}
fileprivate var pathSeparator: String {
#if os(Windows)
return #"\"#
#else
return "/"
#endif
}
private func checkCompilationDatabaseBuildSystem(
_ compdb: ByteString,
block: (CompilationDatabaseBuildSystem) async throws -> ()
) async throws {
let fs = InMemoryFileSystem()
try fs.createDirectory(AbsolutePath(validating: "/a"))
try fs.writeFileContents(AbsolutePath(validating: "/a/compile_commands.json"), bytes: compdb)
let buildSystem = CompilationDatabaseBuildSystem(
projectRoot: try AbsolutePath(validating: "/a"),
searchPaths: try [RelativePath(validating: ".")],
connectionToSourceKitLSP: LocalConnection(receiverName: "Dummy SourceKit-LSP"),
fileSystem: fs
)
try await block(XCTUnwrap(buildSystem))
}