Skip to content

Commit 31e9c15

Browse files
committed
Add test case to check that build settings are recovered after a crash
1 parent 969b891 commit 31e9c15

File tree

3 files changed

+83
-27
lines changed

3 files changed

+83
-27
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*loc*/
2+
3+
#if FOO
4+
void foo() {}
5+
#else
6+
void foo() {}
7+
#endif
8+
9+
int main() {
10+
foo();
11+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"clang_flags": ["-fno-modules", "-Wunused-variable", "-DFOO"],
3+
"sources": ["main.cpp"]
4+
}

Tests/SourceKitTests/CrashRecoveryTests.swift

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import ISDBTestSupport
1314
import LanguageServerProtocol
1415
import SourceKit
1516
import SKTestSupport
@@ -93,6 +94,40 @@ final class CrashRecoveryTests: XCTestCase {
9394
XCTAssertEqual(postCrashHoverResponse, expectedHoverResponse)
9495
}())
9596
}
97+
98+
/// Crashes clangd and waits for it to restart
99+
/// - Parameters:
100+
/// - ws: The workspace for which the clangd server shall be crashed
101+
/// - loc: A test location that points to the first line of a given file. This line needs to be otherwise empty.
102+
private func crashClangd(for ws: SKTibsTestWorkspace, firstLineLoc loc: TestLocation) {
103+
let clangdServer = ws.testServer.server!.languageService(for: loc.docUri, .cpp, in: ws.testServer.server!.workspace!)!
104+
105+
let clangdCrashed = self.expectation(description: "clangd crashed")
106+
let clangdRestarted = self.expectation(description: "clangd restarted")
107+
108+
clangdServer.addStateChangeHandler { (oldState, newState) in
109+
switch newState {
110+
case .connectionInterrupted:
111+
clangdCrashed.fulfill()
112+
case .connected:
113+
clangdRestarted.fulfill()
114+
default:
115+
break
116+
}
117+
}
118+
119+
// Add a pragma to crash clang
120+
let addCrashPragma = TextDocumentContentChangeEvent(range: loc.position..<loc.position, rangeLength: 0, text: "#pragma clang __debug crash\n")
121+
ws.sk.send(DidChangeTextDocumentNotification(textDocument: VersionedTextDocumentIdentifier(loc.docUri, version: 3), contentChanges: [addCrashPragma]))
122+
123+
self.wait(for: [clangdCrashed], timeout: 5)
124+
125+
// Once clangds has crashed, remove the pragma again to allow it to restart
126+
let removeCrashPragma = TextDocumentContentChangeEvent(range: loc.position..<Position(line: 1, utf16index: 0), rangeLength: 28, text: "")
127+
ws.sk.send(DidChangeTextDocumentNotification(textDocument: VersionedTextDocumentIdentifier(loc.docUri, version: 4), contentChanges: [removeCrashPragma]))
128+
129+
self.wait(for: [clangdRestarted], timeout: 30)
130+
}
96131

97132
func testClangdCrashRecovery() {
98133
guard longTestsEnabled else {
@@ -130,33 +165,7 @@ final class CrashRecoveryTests: XCTestCase {
130165

131166
// Crash clangd
132167

133-
let clangdServer = ws.testServer.server!.languageService(for: loc.docUri, .cpp, in: ws.testServer.server!.workspace!)!
134-
135-
let clangdCrashed = self.expectation(description: "clangd crashed")
136-
let clangdRestarted = self.expectation(description: "clangd restarted")
137-
138-
clangdServer.addStateChangeHandler { (oldState, newState) in
139-
switch newState {
140-
case .connectionInterrupted:
141-
clangdCrashed.fulfill()
142-
case .connected:
143-
clangdRestarted.fulfill()
144-
default:
145-
break
146-
}
147-
}
148-
149-
// Add a pragma to crash clang
150-
let addCrashPragma = TextDocumentContentChangeEvent(range: loc.position..<loc.position, rangeLength: 0, text: "#pragma clang __debug crash\n")
151-
ws.sk.send(DidChangeTextDocumentNotification(textDocument: VersionedTextDocumentIdentifier(loc.docUri, version: 3), contentChanges: [addCrashPragma]))
152-
153-
self.wait(for: [clangdCrashed], timeout: 5)
154-
155-
// Once clangds has crashed, remove the pragma again to allow it to restart
156-
let removeCrashPragma = TextDocumentContentChangeEvent(range: loc.position..<Position(line: 1, utf16index: 0), rangeLength: 30, text: "")
157-
ws.sk.send(DidChangeTextDocumentNotification(textDocument: VersionedTextDocumentIdentifier(loc.docUri, version: 4), contentChanges: [removeCrashPragma]))
158-
159-
self.wait(for: [clangdRestarted], timeout: 30)
168+
crashClangd(for: ws, firstLineLoc: loc)
160169

161170
// Check that we have re-opened the document with the correct in-memory state
162171

@@ -165,5 +174,37 @@ final class CrashRecoveryTests: XCTestCase {
165174
XCTAssertEqual(postCrashHoverResponse, expectedHoverResponse)
166175
}())
167176
}
177+
178+
func testClangdCrashRecoveryReopensWithCorrectBuildSettings() {
179+
let ws = try! staticSourceKitTibsWorkspace(name: "ClangCrashRecoveryBuildSettings")!
180+
let loc = ws.testLoc("loc")
181+
182+
try! ws.openDocument(loc.url, language: .cpp)
183+
184+
// Do a sanity check and verify that we get the expected result from a hover response before crashing clangd.
185+
186+
let expectedHighlightResponse = [
187+
DocumentHighlight(range: Position(line: 3, utf16index: 5)..<Position(line: 3, utf16index: 8), kind: .text),
188+
DocumentHighlight(range: Position(line: 9, utf16index: 2)..<Position(line: 9, utf16index: 5), kind: .text)
189+
]
190+
191+
192+
let highlightRequest = DocumentHighlightRequest(textDocument: loc.docIdentifier, position: Position(line: 9, utf16index: 3))
193+
let preCrashHighlightResponse = try! ws.sk.sendSync(highlightRequest)
194+
precondition(preCrashHighlightResponse == expectedHighlightResponse, "Sanity check failed. The Hover response was not what we expected, even before crashing sourcekitd")
195+
196+
// Crash clangd
197+
198+
crashClangd(for: ws, firstLineLoc: loc)
199+
200+
// Check that we have re-opened the document with the correct build settings
201+
// If we did not recover the correct build settings, document highlight would
202+
// pick the definition of foo() in the #else branch.
203+
204+
XCTAssertNoThrow(try {
205+
let postCrashHighlightResponse = try ws.sk.sendSync(highlightRequest)
206+
XCTAssertEqual(postCrashHighlightResponse, expectedHighlightResponse)
207+
}())
208+
}
168209
}
169210

0 commit comments

Comments
 (0)