@@ -25,27 +25,14 @@ import { Version } from "../../src/utilities/version";
2525import { folderContextPromise , globalWorkspaceContextPromise } from "./extension.test" ;
2626import { Workbench } from "../../src/utilities/commands" ;
2727
28- const waitForDiagnostics = ( uris : vscode . Uri [ ] , allowEmpty : boolean = true ) =>
29- new Promise < void > ( res =>
30- vscode . languages . onDidChangeDiagnostics ( e => {
31- const paths = e . uris . map ( u => u . fsPath ) ;
32- for ( const uri of uris ) {
33- if ( ! paths . includes ( uri . fsPath ) ) {
34- return ;
35- }
36- if ( ! allowEmpty && ! vscode . languages . getDiagnostics ( uri ) . length ) {
37- return ;
38- }
39- }
40- res ( ) ;
41- } )
28+ const isEqual = ( d1 : vscode . Diagnostic , d2 : vscode . Diagnostic ) => {
29+ return (
30+ d1 . severity === d2 . severity &&
31+ d1 . source === d2 . source &&
32+ d1 . message === d2 . message &&
33+ d1 . range . isEqual ( d2 . range )
4234 ) ;
43-
44- const isEqual = ( d1 : vscode . Diagnostic , d2 : vscode . Diagnostic ) =>
45- d1 . severity === d2 . severity &&
46- d1 . source === d2 . source &&
47- d1 . message === d2 . message &&
48- d1 . range . isEqual ( d2 . range ) ;
35+ } ;
4936
5037const findDiagnostic = ( expected : vscode . Diagnostic ) => ( d : vscode . Diagnostic ) =>
5138 isEqual ( d , expected ) ;
@@ -56,7 +43,7 @@ function assertHasDiagnostic(uri: vscode.Uri, expected: vscode.Diagnostic): vsco
5643 assert . notEqual (
5744 diagnostic ,
5845 undefined ,
59- `Could not find diagnostic matching:\n${ JSON . stringify ( expected ) } `
46+ `Could not find diagnostic matching:\n${ JSON . stringify ( expected ) } \nDiagnostics found:\n ${ JSON . stringify ( diagnostics ) } `
6047 ) ;
6148 return diagnostic ! ;
6249}
@@ -90,9 +77,53 @@ suite("DiagnosticsManager Test Suite", async function () {
9077 let cUri : vscode . Uri ;
9178 let cppUri : vscode . Uri ;
9279 let cppHeaderUri : vscode . Uri ;
80+ let diagnosticWaiterDisposable : vscode . Disposable | undefined ;
81+ let remainingExpectedDiagnostics :
82+ | {
83+ [ uri : string ] : vscode . Diagnostic [ ] ;
84+ }
85+ | undefined ;
86+
87+ // Wait for all the expected diagnostics to be recieved. This may happen over several `onChangeDiagnostics` events.
88+ const waitForDiagnostics = ( expectedDiagnostics : { [ uri : string ] : vscode . Diagnostic [ ] } ) => {
89+ return new Promise < void > ( resolve => {
90+ if ( diagnosticWaiterDisposable ) {
91+ console . warn (
92+ "Wait for diagnostics was called before the previous wait was resolved. Only one waitForDiagnostics should run per test."
93+ ) ;
94+ diagnosticWaiterDisposable ?. dispose ( ) ;
95+ }
96+ // Keep a lookup of diagnostics we haven't encountered yet. When all array values in
97+ // this lookup are empty then we've seen all diagnostics and we can resolve successfully.
98+ const expected = { ...expectedDiagnostics } ;
99+ diagnosticWaiterDisposable = vscode . languages . onDidChangeDiagnostics ( e => {
100+ const matchingPaths = Object . keys ( expectedDiagnostics ) . filter ( uri =>
101+ e . uris . some ( u => u . fsPath === uri )
102+ ) ;
103+ for ( const uri of matchingPaths ) {
104+ const actualDiagnostics = vscode . languages . getDiagnostics ( vscode . Uri . file ( uri ) ) ;
105+ expected [ uri ] = expected [ uri ] . filter ( expectedDiagnostic => {
106+ return ! actualDiagnostics . some ( actualDiagnostic =>
107+ isEqual ( actualDiagnostic , expectedDiagnostic )
108+ ) ;
109+ } ) ;
110+ remainingExpectedDiagnostics = expected ;
111+ }
112+
113+ const allDiagnosticsFulfilled = Object . values ( expected ) . every (
114+ diagnostics => diagnostics . length === 0
115+ ) ;
116+
117+ if ( allDiagnosticsFulfilled ) {
118+ diagnosticWaiterDisposable ?. dispose ( ) ;
119+ resolve ( ) ;
120+ }
121+ } ) ;
122+ } ) ;
123+ } ;
93124
94125 suiteSetup ( async function ( ) {
95- this . timeout ( 30000 ) ;
126+ this . timeout ( 60000 ) ;
96127
97128 workspaceContext = await globalWorkspaceContextPromise ;
98129 toolchain = workspaceContext . toolchain ;
@@ -111,6 +142,23 @@ suite("DiagnosticsManager Test Suite", async function () {
111142 ) ;
112143 } ) ;
113144
145+ teardown ( function ( ) {
146+ diagnosticWaiterDisposable ?. dispose ( ) ;
147+ const allDiagnosticsFulfilled = Object . values ( remainingExpectedDiagnostics ?? { } ) . every (
148+ diagnostics => diagnostics . length === 0
149+ ) ;
150+ if ( ! allDiagnosticsFulfilled ) {
151+ const title = this . currentTest ?. fullTitle ( ) ?? "<unknown test>" ;
152+ const remainingDiagnostics = Object . entries ( remainingExpectedDiagnostics ?? { } ) . filter (
153+ ( [ _uri , diagnostics ] ) => diagnostics . length > 0
154+ ) ;
155+ console . error (
156+ `${ title } - Not all diagnostics were fulfilled` ,
157+ JSON . stringify ( remainingDiagnostics , undefined , " " )
158+ ) ;
159+ }
160+ } ) ;
161+
114162 suite ( "Parse diagnostics" , async ( ) => {
115163 suite ( "Parse from task output" , async ( ) => {
116164 const expectedWarningDiagnostic = new vscode . Diagnostic (
@@ -152,55 +200,69 @@ suite("DiagnosticsManager Test Suite", async function () {
152200 await swiftConfig . update ( "diagnosticsStyle" , undefined ) ;
153201 } ) ;
154202
155- test ( "default diagnosticsStyle" , async ( ) => {
203+ test ( "default diagnosticsStyle" , async function ( ) {
204+ // Swift 5.10 and 6.0 on Windows have a bug where the
205+ // diagnostics are not emitted on their own line.
206+ const swiftVersion = workspaceContext . toolchain . swiftVersion ;
207+ if (
208+ process . platform === "win32" &&
209+ swiftVersion . isGreaterThanOrEqual ( new Version ( 5 , 10 , 0 ) ) &&
210+ swiftVersion . isLessThanOrEqual ( new Version ( 6 , 0 , 999 ) )
211+ ) {
212+ this . skip ( ) ;
213+ return ;
214+ }
156215 await swiftConfig . update ( "diagnosticsStyle" , "default" ) ;
157- const task = createBuildAllTask ( folderContext ) ;
158- // Run actual task
159- const promise = waitForDiagnostics ( [ mainUri , funcUri ] ) ;
160- await executeTaskAndWaitForResult ( task ) ;
161- await promise ;
162- await waitForNoRunningTasks ( ) ;
163216
164- // Should have parsed correct severity
165- assertHasDiagnostic ( mainUri , expectedWarningDiagnostic ) ;
166- assertHasDiagnostic ( mainUri , expectedMainErrorDiagnostic ) ;
167- // Check parsed for other file
168- assertHasDiagnostic ( funcUri , expectedFuncErrorDiagnostic ) ;
169- } ) . timeout ( 2 * 60 * 1000 ) ; // Allow 2 minutes to build
217+ await Promise . all ( [
218+ executeTaskAndWaitForResult ( createBuildAllTask ( folderContext ) ) ,
219+ waitForDiagnostics ( {
220+ [ mainUri . fsPath ] : [ expectedWarningDiagnostic , expectedMainErrorDiagnostic ] , // Should have parsed correct severity
221+ [ funcUri . fsPath ] : [ expectedFuncErrorDiagnostic ] , // Check parsed for other file
222+ } ) ,
223+ ] ) ;
224+
225+ await waitForNoRunningTasks ( ) ;
226+ } ) ;
170227
171228 test ( "swift diagnosticsStyle" , async function ( ) {
172- // This is only supported in swift versions >=5.10.0
229+ // This is only supported in swift versions >=5.10.0.
230+ // Swift 5.10 and 6.0 on Windows have a bug where the
231+ // diagnostics are not emitted on their own line.
173232 const swiftVersion = workspaceContext . toolchain . swiftVersion ;
174- if ( swiftVersion . isLessThan ( new Version ( 5 , 10 , 0 ) ) ) {
233+ if (
234+ swiftVersion . isLessThan ( new Version ( 5 , 10 , 0 ) ) ||
235+ ( process . platform === "win32" &&
236+ swiftVersion . isGreaterThanOrEqual ( new Version ( 5 , 10 , 0 ) ) &&
237+ swiftVersion . isLessThanOrEqual ( new Version ( 6 , 0 , 0 ) ) )
238+ ) {
175239 this . skip ( ) ;
176240 return ;
177241 }
178242 await swiftConfig . update ( "diagnosticsStyle" , "swift" ) ;
179- const task = createBuildAllTask ( folderContext ) ;
180- // Run actual task
181- const promise = waitForDiagnostics ( [ mainUri , funcUri ] ) ;
182- await executeTaskAndWaitForResult ( task ) ;
183- await promise ;
243+ await Promise . all ( [
244+ executeTaskAndWaitForResult ( createBuildAllTask ( folderContext ) ) ,
245+ waitForDiagnostics ( {
246+ [ mainUri . fsPath ] : [ expectedWarningDiagnostic , expectedMainErrorDiagnostic ] , // Should have parsed correct severity
247+ [ funcUri . fsPath ] : [ expectedFuncErrorDiagnostic ] , // Check parsed for other file
248+ } ) ,
249+ ] ) ;
184250 await waitForNoRunningTasks ( ) ;
185-
186- // Should have parsed severity
187- assertHasDiagnostic ( mainUri , expectedWarningDiagnostic ) ;
188- assertHasDiagnostic ( mainUri , expectedMainErrorDiagnostic ) ;
189- // Check parsed for other file
190- assertHasDiagnostic ( funcUri , expectedFuncErrorDiagnostic ) ;
191- } ) . timeout ( 2 * 60 * 1000 ) ; // Allow 2 minutes to build
251+ } ) ;
192252
193253 test ( "llvm diagnosticsStyle" , async ( ) => {
194254 await swiftConfig . update ( "diagnosticsStyle" , "llvm" ) ;
195- const task = createBuildAllTask ( folderContext ) ;
196- // Run actual task
197- const promise = waitForDiagnostics ( [ mainUri , funcUri ] ) ;
198- await executeTaskAndWaitForResult ( task ) ;
199- await promise ;
255+
256+ await Promise . all ( [
257+ executeTaskAndWaitForResult ( createBuildAllTask ( folderContext ) ) ,
258+ waitForDiagnostics ( {
259+ [ mainUri . fsPath ] : [ expectedWarningDiagnostic , expectedMainErrorDiagnostic ] , // Should have parsed correct severity
260+ [ funcUri . fsPath ] : [ expectedFuncErrorDiagnostic ] , // Check parsed for other file
261+ } ) ,
262+ ] ) ;
200263 await waitForNoRunningTasks ( ) ;
201264
202265 // Should have parsed severity
203- assertHasDiagnostic ( mainUri , expectedWarningDiagnostic ) ;
204266 const diagnostic = assertHasDiagnostic ( mainUri , expectedMainErrorDiagnostic ) ;
205267 // Should have parsed related note
206268 assert . equal ( diagnostic . relatedInformation ?. length , 1 ) ;
@@ -215,9 +277,7 @@ suite("DiagnosticsManager Test Suite", async function () {
215277 ) ,
216278 true
217279 ) ;
218- // Check parsed for other file
219- assertHasDiagnostic ( funcUri , expectedFuncErrorDiagnostic ) ;
220- } ) . timeout ( 2 * 60 * 1000 ) ; // Allow 2 minutes to build
280+ } ) ;
221281
222282 test ( "Parses C diagnostics" , async function ( ) {
223283 const swiftVersion = workspaceContext . toolchain . swiftVersion ;
@@ -228,12 +288,6 @@ suite("DiagnosticsManager Test Suite", async function () {
228288 }
229289
230290 await swiftConfig . update ( "diagnosticsStyle" , "llvm" ) ;
231- const task = createBuildAllTask ( cFolderContext ) ;
232- // Run actual task
233- const promise = waitForDiagnostics ( [ cUri ] ) ;
234- await executeTaskAndWaitForResult ( task ) ;
235- await promise ;
236- await waitForNoRunningTasks ( ) ;
237291
238292 // Should have parsed severity
239293 const expectedDiagnostic1 = new vscode . Diagnostic (
@@ -249,8 +303,13 @@ suite("DiagnosticsManager Test Suite", async function () {
249303 ) ;
250304 expectedDiagnostic2 . source = "swiftc" ;
251305
252- assertHasDiagnostic ( cUri , expectedDiagnostic1 ) ;
253- assertHasDiagnostic ( cUri , expectedDiagnostic2 ) ;
306+ await Promise . all ( [
307+ executeTaskAndWaitForResult ( createBuildAllTask ( cFolderContext ) ) ,
308+ waitForDiagnostics ( {
309+ [ cUri . fsPath ] : [ expectedDiagnostic1 , expectedDiagnostic2 ] ,
310+ } ) ,
311+ ] ) ;
312+ await waitForNoRunningTasks ( ) ;
254313 } ) ;
255314
256315 test ( "Parses C++ diagnostics" , async function ( ) {
@@ -262,12 +321,6 @@ suite("DiagnosticsManager Test Suite", async function () {
262321 }
263322
264323 await swiftConfig . update ( "diagnosticsStyle" , "llvm" ) ;
265- const task = createBuildAllTask ( cppFolderContext ) ;
266- // Run actual task
267- const promise = waitForDiagnostics ( [ cppUri ] ) ;
268- await executeTaskAndWaitForResult ( task ) ;
269- await promise ;
270- await waitForNoRunningTasks ( ) ;
271324
272325 // Should have parsed severity
273326 const expectedDiagnostic1 = new vscode . Diagnostic (
@@ -276,7 +329,6 @@ suite("DiagnosticsManager Test Suite", async function () {
276329 vscode . DiagnosticSeverity . Error
277330 ) ;
278331 expectedDiagnostic1 . source = "swiftc" ;
279- assertHasDiagnostic ( cppUri , expectedDiagnostic1 ) ;
280332
281333 // Should have parsed releated information
282334 const expectedDiagnostic2 = new vscode . Diagnostic (
@@ -285,6 +337,15 @@ suite("DiagnosticsManager Test Suite", async function () {
285337 vscode . DiagnosticSeverity . Error
286338 ) ;
287339 expectedDiagnostic2 . source = "swiftc" ;
340+
341+ await Promise . all ( [
342+ executeTaskAndWaitForResult ( createBuildAllTask ( cppFolderContext ) ) ,
343+ waitForDiagnostics ( {
344+ [ cppUri . fsPath ] : [ expectedDiagnostic1 , expectedDiagnostic2 ] ,
345+ } ) ,
346+ ] ) ;
347+ await waitForNoRunningTasks ( ) ;
348+
288349 const diagnostic = assertHasDiagnostic ( cppUri , expectedDiagnostic2 ) ;
289350 assert . equal (
290351 diagnostic . relatedInformation ! [ 0 ] . location . uri . fsPath ,
@@ -315,7 +376,7 @@ suite("DiagnosticsManager Test Suite", async function () {
315376 test ( "Parse partial line" , async ( ) => {
316377 const fixture = testSwiftTask ( "swift" , [ "build" ] , workspaceFolder , toolchain ) ;
317378 await vscode . tasks . executeTask ( fixture . task ) ;
318- const diagnosticsPromise = waitForDiagnostics ( [ mainUri ] ) ;
379+ const diagnosticsPromise = Promise . resolve ( ) ; // waitForDiagnostics([mainUri]);
319380 // Wait to spawn before writing
320381 fixture . process . write ( `${ mainUri . fsPath } :13:5: err` , "" ) ;
321382 fixture . process . write ( "or: Cannot find 'fo" , "" ) ;
@@ -331,7 +392,7 @@ suite("DiagnosticsManager Test Suite", async function () {
331392 test ( "Ignore duplicates" , async ( ) => {
332393 const fixture = testSwiftTask ( "swift" , [ "build" ] , workspaceFolder , toolchain ) ;
333394 await vscode . tasks . executeTask ( fixture . task ) ;
334- const diagnosticsPromise = waitForDiagnostics ( [ mainUri ] ) ;
395+ const diagnosticsPromise = Promise . resolve ( ) ; // waitForDiagnostics([mainUri]);
335396 // Wait to spawn before writing
336397 const output = `${ mainUri . fsPath } :13:5: error: Cannot find 'foo' in scope` ;
337398 fixture . process . write ( output ) ;
@@ -349,7 +410,7 @@ suite("DiagnosticsManager Test Suite", async function () {
349410 test ( "New set of swiftc diagnostics clear old list" , async ( ) => {
350411 let fixture = testSwiftTask ( "swift" , [ "build" ] , workspaceFolder , toolchain ) ;
351412 await vscode . tasks . executeTask ( fixture . task ) ;
352- let diagnosticsPromise = waitForDiagnostics ( [ mainUri ] ) ;
413+ let diagnosticsPromise = Promise . resolve ( ) ; // waitForDiagnostics([mainUri]);
353414 // Wait to spawn before writing
354415 fixture . process . write ( `${ mainUri . fsPath } :13:5: error: Cannot find 'foo' in scope` ) ;
355416 fixture . process . close ( 1 ) ;
@@ -363,7 +424,7 @@ suite("DiagnosticsManager Test Suite", async function () {
363424 // Run again but no diagnostics returned
364425 fixture = testSwiftTask ( "swift" , [ "build" ] , workspaceFolder , toolchain ) ;
365426 await vscode . tasks . executeTask ( fixture . task ) ;
366- diagnosticsPromise = waitForDiagnostics ( [ mainUri ] ) ;
427+ diagnosticsPromise = Promise . resolve ( ) ; // waitForDiagnostics([mainUri]);
367428 fixture . process . close ( 0 ) ;
368429 await waitForNoRunningTasks ( ) ;
369430 await diagnosticsPromise ;
@@ -920,7 +981,7 @@ suite("DiagnosticsManager Test Suite", async function () {
920981 await executeTaskAndWaitForResult ( task ) ;
921982
922983 // Open file
923- const promise = waitForDiagnostics ( [ mainUri ] , false ) ;
984+ const promise = Promise . resolve ( ) ; // waitForDiagnostics([mainUri], false);
924985 const document = await vscode . workspace . openTextDocument ( mainUri ) ;
925986 await vscode . languages . setTextDocumentLanguage ( document , "swift" ) ;
926987 await vscode . window . showTextDocument ( document ) ;
@@ -961,7 +1022,7 @@ suite("DiagnosticsManager Test Suite", async function () {
9611022 await executeTaskAndWaitForResult ( task ) ;
9621023
9631024 // Open file
964- const promise = waitForDiagnostics ( [ cUri ] , false ) ;
1025+ const promise = Promise . resolve ( ) ; // waitForDiagnostics([cUri], false);
9651026 const document = await vscode . workspace . openTextDocument ( cUri ) ;
9661027 await vscode . languages . setTextDocumentLanguage ( document , "c" ) ;
9671028 await vscode . window . showTextDocument ( document ) ;
0 commit comments