Skip to content

Commit 81cef83

Browse files
committed
Add test cases for the request reducer
1 parent 43881fc commit 81cef83

9 files changed

+284
-31
lines changed

Package.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ let package = Package(
5454
dependencies: []
5555
),
5656

57-
// MARK: Csourcekitd:
57+
// MARK: Csourcekitd
5858
// C modules wrapper for sourcekitd.
5959
.target(
6060
name: "Csourcekitd",
6161
dependencies: [],
6262
exclude: ["CMakeLists.txt"]
6363
),
6464

65+
// MARK: Diagnose
66+
6567
.target(
6668
name: "Diagnose",
6769
dependencies: [
@@ -75,6 +77,19 @@ let package = Package(
7577
exclude: ["CMakeLists.txt"]
7678
),
7779

80+
.testTarget(
81+
name: "DiagnoseTests",
82+
dependencies: [
83+
"Diagnose",
84+
"LSPLogging",
85+
"LSPTestSupport",
86+
"SourceKitD",
87+
"SKCore",
88+
"SKTestSupport",
89+
.product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"),
90+
]
91+
),
92+
7893
// MARK: LanguageServerProtocol
7994
// The core LSP types, suitable for any LSP implementation.
8095
.target(

Sources/Diagnose/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_library(Diagnose STATIC
66
ReductionError.swift
77
ReproducerBundle.swift
88
RequestInfo.swift
9+
SourceKitD+RunWithYaml.swift
910
SourceKitDRequestExecutor.swift
1011
SourcekitdRequestCommand.swift
1112
SourceReducer.swift)

Sources/Diagnose/DiagnoseCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public struct DiagnoseCommand: AsyncParsableCommand {
128128
nspredicate = NSPredicate(format: predicate)
129129
}
130130
#endif
131-
let executor = SourceKitRequestExecutor(
131+
let executor = OutOfProcessSourceKitRequestExecutor(
132132
sourcekitd: URL(fileURLWithPath: sourcekitd),
133133
reproducerPredicate: nspredicate
134134
)

Sources/Diagnose/RequestInfo.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import Foundation
1414
import RegexBuilder
1515

1616
/// All the information necessary to replay a sourcektid request.
17-
struct RequestInfo {
17+
@_spi(Testing)
18+
public struct RequestInfo {
1819
/// The JSON request object. Contains the following dynamic placeholders:
1920
/// - `$OFFSET`: To be replaced by `offset` before running the request
2021
/// - `$FILE`: Will be replaced with a path to the file that contains the reduced source code.
@@ -26,10 +27,12 @@ struct RequestInfo {
2627
var offset: Int
2728

2829
/// The compiler arguments of the request. Replaces the `$COMPILER_ARGS`placeholder in the request template.
29-
var compilerArgs: [String]
30+
@_spi(Testing)
31+
public var compilerArgs: [String]
3032

3133
/// The contents of the file that the sourcekitd request operates on.
32-
var fileContents: String
34+
@_spi(Testing)
35+
public var fileContents: String
3336

3437
func request(for file: URL) throws -> String {
3538
let encoder = JSONEncoder()
@@ -47,7 +50,8 @@ struct RequestInfo {
4750

4851
}
4952

50-
init(requestTemplate: String, offset: Int, compilerArgs: [String], fileContents: String) {
53+
@_spi(Testing)
54+
public init(requestTemplate: String, offset: Int, compilerArgs: [String], fileContents: String) {
5155
self.requestTemplate = requestTemplate
5256
self.offset = offset
5357
self.compilerArgs = compilerArgs
@@ -57,7 +61,8 @@ struct RequestInfo {
5761
/// Creates `RequestInfo` from the contents of the JSON sourcekitd request at `requestPath`.
5862
///
5963
/// The contents of the source file are read from disk.
60-
init(request: String) throws {
64+
@_spi(Testing)
65+
public init(request: String) throws {
6166
var requestTemplate = request
6267

6368
// Extract offset
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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 SourceKitD
14+
15+
extension SourceKitD {
16+
/// Parse the request from YAML and execute it.
17+
@_spi(Testing)
18+
public func run(requestYaml: String) async throws -> SKDResponse {
19+
let request = try requestYaml.cString(using: .utf8)?.withUnsafeBufferPointer { buffer in
20+
var error: UnsafeMutablePointer<CChar>?
21+
let req = api.request_create_from_yaml(buffer.baseAddress, &error)
22+
if let error {
23+
throw ReductionError("Failed to parse sourcekitd request from YAML: \(String(cString: error))")
24+
}
25+
precondition(req != nil)
26+
return req
27+
}
28+
return await withCheckedContinuation { continuation in
29+
var handle: sourcekitd_request_handle_t? = nil
30+
api.send_request(request, &handle) { resp in
31+
continuation.resume(returning: SKDResponse(resp, sourcekitd: self))
32+
}
33+
}
34+
}
35+
}

Sources/Diagnose/SourceKitDRequestExecutor.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import struct TSCBasic.AbsolutePath
1717
import class TSCBasic.Process
1818

1919
/// The different states in which a sourcekitd request can finish.
20-
enum SourceKitDRequestResult {
20+
@_spi(Testing)
21+
public enum SourceKitDRequestResult {
2122
/// The request succeeded.
2223
case success(response: String)
2324

@@ -41,8 +42,14 @@ fileprivate extension String {
4142
}
4243
}
4344

45+
/// An executor that can run a sourcekitd request and indicate whether the request reprodes a specified issue.
46+
@_spi(Testing)
47+
public protocol SourceKitRequestExecutor {
48+
func run(request requestString: String) async throws -> SourceKitDRequestResult
49+
}
50+
4451
/// Runs `sourcekit-lsp run-sourcekitd-request` to check if a sourcekit-request crashes.
45-
struct SourceKitRequestExecutor {
52+
struct OutOfProcessSourceKitRequestExecutor: SourceKitRequestExecutor {
4653
/// The path to `sourcekitd.framework/sourcekitd`.
4754
private let sourcekitd: URL
4855

Sources/Diagnose/SourceReducer.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import SwiftSyntax
1818
// MARK: - Entry point
1919

2020
extension RequestInfo {
21-
func reduceInputFile(using executor: SourceKitRequestExecutor) async throws -> RequestInfo {
21+
@_spi(Testing)
22+
public func reduceInputFile(using executor: SourceKitRequestExecutor) async throws -> RequestInfo {
2223
let reducer = SourceReducer(sourcekitdExecutor: executor)
2324
return try await reducer.run(initialRequestInfo: self)
2425
}
@@ -156,7 +157,7 @@ fileprivate class SourceReducer {
156157
fileContents: reducedSource
157158
)
158159

159-
try reducedSource.write(to: temporarySourceFile, atomically: false, encoding: .utf8)
160+
try reducedSource.write(to: temporarySourceFile, atomically: true, encoding: .utf8)
160161
let result = try await sourcekitdExecutor.run(request: reducedRequestInfo.request(for: temporarySourceFile))
161162
if case .reproducesIssue = result {
162163
logSuccessfulReduction(reducedRequestInfo)

Sources/Diagnose/SourcekitdRequestCommand.swift

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,8 @@ public struct SourceKitdRequestCommand: AsyncParsableCommand {
3939

4040
public func run() async throws {
4141
let requestString = try String(contentsOf: URL(fileURLWithPath: sourcekitdRequestPath))
42-
43-
let sourcekitd = try SourceKitDImpl.getOrCreate(
44-
dylibPath: try! AbsolutePath(validating: sourcekitdPath)
45-
)
46-
47-
let request = try requestString.cString(using: .utf8)?.withUnsafeBufferPointer { buffer in
48-
var error: UnsafeMutablePointer<CChar>?
49-
let req = sourcekitd.api.request_create_from_yaml(buffer.baseAddress, &error)
50-
if let error {
51-
throw ReductionError("Failed to parse sourcekitd request from JSON: \(String(cString: error))")
52-
}
53-
precondition(req != nil)
54-
return req
55-
}
56-
let response: SKDResponse = await withCheckedContinuation { continuation in
57-
var handle: sourcekitd_request_handle_t? = nil
58-
sourcekitd.api.send_request(request, &handle) { resp in
59-
continuation.resume(returning: SKDResponse(resp, sourcekitd: sourcekitd))
60-
}
61-
}
42+
let sourcekitd = try SourceKitDImpl.getOrCreate(dylibPath: try! AbsolutePath(validating: sourcekitdPath))
43+
let response = try await sourcekitd.run(requestYaml: requestString)
6244

6345
switch response.error {
6446
case .requestFailed, .requestInvalid, .requestCancelled, .missingRequiredSymbol:

0 commit comments

Comments
 (0)