10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ import ISDBTestSupport
13
14
import LanguageServerProtocol
14
15
import SourceKit
15
16
import SKTestSupport
@@ -93,6 +94,40 @@ final class CrashRecoveryTests: XCTestCase {
93
94
XCTAssertEqual ( postCrashHoverResponse, expectedHoverResponse)
94
95
} ( ) )
95
96
}
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
+ }
96
131
97
132
func testClangdCrashRecovery( ) {
98
133
guard longTestsEnabled else {
@@ -130,33 +165,7 @@ final class CrashRecoveryTests: XCTestCase {
130
165
131
166
// Crash clangd
132
167
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)
160
169
161
170
// Check that we have re-opened the document with the correct in-memory state
162
171
@@ -165,5 +174,37 @@ final class CrashRecoveryTests: XCTestCase {
165
174
XCTAssertEqual ( postCrashHoverResponse, expectedHoverResponse)
166
175
} ( ) )
167
176
}
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
+ }
168
209
}
169
210
0 commit comments