@@ -14,7 +14,9 @@ export let testRunProfile: vscode.TestRunProfile;
14
14
15
15
/**
16
16
* Types definition for the Gnattest XML file structure. The types match the XML
17
- * generated by src/test-mapping.adb in the libadalang-tools repository.
17
+ * generated by src/test-mapping.adb in the libadalang-tools repository. However
18
+ * these types do not describe the entire XML strutcture. Only the elements used
19
+ * are described.
18
20
*/
19
21
export type Root = {
20
22
tests_mapping : TestMapping ;
@@ -34,7 +36,6 @@ type Unit = {
34
36
type TestUnit = {
35
37
'@_target_file' : string ;
36
38
tested : Tested [ ] ;
37
- dangling : Dangling ;
38
39
} ;
39
40
40
41
type Tested = {
@@ -58,10 +59,6 @@ type Test = {
58
59
'@_name' : string ;
59
60
} ;
60
61
61
- type Dangling = {
62
- test : Test [ ] ;
63
- } ;
64
-
65
62
/**
66
63
* The XML paths in GNATtest XML files that should always be parsed as arrays.
67
64
* This is based the code in src/test-mapping.adb in the libadalang-tools
@@ -72,7 +69,6 @@ const alwaysArray = [
72
69
'tests_mapping.unit.test_unit' ,
73
70
'tests_mapping.unit.test_unit.tested' ,
74
71
'tests_mapping.unit.test_unit.tested.test_case' ,
75
- 'tests_mapping.unit.test_unit.dangling.test' ,
76
72
'tests_mapping.additional_tests' ,
77
73
] ;
78
74
@@ -133,24 +129,39 @@ async function refreshTestItemTree() {
133
129
await addTestsRootLevel ( ) ;
134
130
}
135
131
132
+ /**
133
+ * @returns the full path to the GNATtest XML file.
134
+ */
136
135
async function getGnatTestXmlPath ( ) : Promise < string > {
137
136
const objDir = await getObjectDir ( ) ;
138
137
const gnatTestXmlPath = path . join ( objDir , 'gnattest' , 'harness' , 'gnattest.xml' ) ;
139
138
return gnatTestXmlPath ;
140
139
}
141
140
141
+ /**
142
+ *
143
+ * @returns the full path to the GNATtest test driver GPR project.
144
+ */
142
145
async function getGnatTestDriverProjectPath ( ) : Promise < string > {
143
146
const objDir = await getObjectDir ( ) ;
144
147
const testDriverPath = path . join ( objDir , 'gnattest' , 'harness' , 'test_driver.gpr' ) ;
145
148
return testDriverPath ;
146
149
}
147
150
151
+ /**
152
+ *
153
+ * @returns the full path to the GNATtest test driver executable.
154
+ */
148
155
async function getGnatTestDriverExecPath ( ) : Promise < string > {
149
156
const objDir = await getObjectDir ( ) ;
150
157
const testDriverPath = path . join ( objDir , 'gnattest' , 'harness' , 'test_runner' + exe ) ;
151
158
return testDriverPath ;
152
159
}
153
160
161
+ /**
162
+ * Parse the GNATtest XML file and create the top-level TestItems in the test
163
+ * controller for later lazy resolution.
164
+ */
154
165
export async function addTestsRootLevel ( ) {
155
166
if ( fs . existsSync ( await getGnatTestXmlPath ( ) ) ) {
156
167
const xmlDoc : Root = await parseGnatTestXml ( ) ;
@@ -161,7 +172,10 @@ export async function addTestsRootLevel() {
161
172
}
162
173
}
163
174
164
- async function parseGnatTestXml ( ) {
175
+ /**
176
+ * @returns the object resulting from parsing the GNATtest XML file.
177
+ */
178
+ async function parseGnatTestXml ( ) : Promise < Root > {
165
179
const gnatTestXmlUri = vscode . Uri . file ( await getGnatTestXmlPath ( ) ) ;
166
180
const fileContent = await vscode . workspace . fs . readFile ( gnatTestXmlUri ) ;
167
181
const fileContentAsBuffer = Buffer . from ( fileContent ) ;
@@ -182,9 +196,11 @@ async function parseGnatTestXml() {
182
196
return xmlDoc ;
183
197
}
184
198
185
- /*
186
- Creating nested test items to visuliaze in the view
187
- */
199
+ /**
200
+ * Create a TestItem for a GNATtest Unit node.
201
+ *
202
+ * @param unit - a Unit node from the GNATtest XML
203
+ */
188
204
async function addUnitItem ( unit : Unit ) {
189
205
const srcFile = unit [ '@_source_file' ] ;
190
206
const srcUri = await findFileInWorkspace ( srcFile ) ;
@@ -205,15 +221,25 @@ async function addUnitItem(unit: Unit) {
205
221
}
206
222
}
207
223
224
+ /**
225
+ * Resolve a TestItem by computing its children node based on the GNATtest XML.
226
+ *
227
+ * @param testItem - the TestItem node
228
+ * @param unit - the corresponding Unit node from the GNATtest XML
229
+ */
208
230
function resolveUnitItem ( testItem : TestItem , unit : Unit ) {
209
231
for ( const t of unit . test_unit . flatMap ( ( u ) => u . tested ) ) {
210
232
addTestedItem ( testItem , t ) ;
211
233
}
212
234
}
213
235
214
- /*
215
- Adding Test Cases to a Test Unit Item
216
- */
236
+ /**
237
+ * Create a TestItem corresponding to a node from the GNATtest XML. The new
238
+ * TestItem is added as a child of parentTestItem.
239
+ *
240
+ * @param parentTestItem - parent TestItem
241
+ * @param tested - the "Tested" node from the GNATtest XML
242
+ */
217
243
function addTestedItem ( parentTestItem : vscode . TestItem , tested : Tested ) {
218
244
const testedSubprogramName = tested [ '@_name' ] ;
219
245
const pos = new vscode . Position (
@@ -248,12 +274,25 @@ function addTestedItem(parentTestItem: vscode.TestItem, tested: Tested) {
248
274
}
249
275
}
250
276
277
+ /**
278
+ * Resolve a TestItem by computing its children node based on the GNATtest XML.
279
+ *
280
+ * @param testItem - the TestItem to resolve
281
+ * @param tested - the corresponding "Tested" node in the GNATtest XML
282
+ */
251
283
async function resolveTestedItem ( testItem : TestItem , tested : Tested ) {
252
284
for ( const e of tested . test_case ) {
253
285
await addTestCaseItem ( testItem , e ) ;
254
286
}
255
287
}
256
288
289
+ /**
290
+ * Create a TestItem corresponding to a node from the GNATtest XML. The new
291
+ * TestItem is added as a child of parentTestItem.
292
+ *
293
+ * @param parentItem - the parent TestItem
294
+ * @param testCase - the "TestCase" node from the GNATtest XML
295
+ */
257
296
async function addTestCaseItem ( parentItem : vscode . TestItem , testCase : TestCase ) {
258
297
const test : Test = testCase . test ;
259
298
const testFileBasename = test [ '@_file' ] ;
@@ -321,12 +360,18 @@ function createTestCaseItemId(testCase: TestCase, sourceFileName: string): strin
321
360
return `${ sourceFileName } :${ testCase [ '@_line' ] } ` ;
322
361
}
323
362
324
- async function findFileInWorkspace ( name : string ) : Promise < vscode . Uri > {
325
- const matchingFiles = await vscode . workspace . findFiles ( `**/${ name } ` , undefined , 1 ) ;
363
+ /**
364
+ *
365
+ * @param basename - a basename
366
+ * @returns - the Uri of the first file found in the workspace with the given basename.
367
+ * @throws an Error in case no matching file is found in the workspace
368
+ */
369
+ async function findFileInWorkspace ( basename : string ) : Promise < vscode . Uri > {
370
+ const matchingFiles = await vscode . workspace . findFiles ( `**/${ basename } ` , undefined , 1 ) ;
326
371
if ( matchingFiles . length > 0 ) {
327
372
return matchingFiles [ 0 ] ;
328
373
} else {
329
- throw `Source file ${ name } not found in workspace` ;
374
+ throw Error ( `Source file ${ basename } not found in workspace` ) ;
330
375
}
331
376
}
332
377
@@ -405,21 +450,26 @@ async function resolveHandler(
405
450
}
406
451
}
407
452
453
+ /**
454
+ * Configure the handlers needed on the test controller to support test execution.
455
+ *
456
+ * @param controller - the TestController to configure
457
+ */
408
458
function configureTestExecution ( controller : vscode . TestController ) {
409
459
testRunProfile = controller . createRunProfile (
410
460
'GNATtest' ,
411
461
vscode . TestRunProfileKind . Run ,
412
462
runHandler
413
463
) ;
414
-
415
- // Tests Configuration Handler to Generates Tests for a Project.
416
- // testRunProfile.configureHandler = () => {
417
- // const terminal = vscode.window.createTerminal('Test Terminal');
418
- // terminal.sendText('gnattest -P ' + projectFileFullPath);
419
- // terminal.sendText('exit');
420
- // };
421
464
}
422
465
466
+ /**
467
+ * This handler is called when the User trigger an action in the UI intended to
468
+ * run tests. The request might be about a subset of tests, or all tests.
469
+ *
470
+ * @param request - the request based on the User selections
471
+ * @param token - a cancellation token
472
+ */
423
473
async function runHandler ( request : vscode . TestRunRequest , token : vscode . CancellationToken ) {
424
474
if ( ( request . include ?. length ?? 0 ) === 0 && ( request . exclude ?. length ?? 0 ) === 0 ) {
425
475
/**
@@ -595,6 +645,13 @@ async function handleRunAll(request: vscode.TestRunRequest, token: CancellationT
595
645
}
596
646
}
597
647
648
+ /**
649
+ * Invoke gprbuild on the test driver project and pipe the output into the given
650
+ * TestRun object.
651
+ *
652
+ * @param run - the TestRun object hosting this execution
653
+ * @throws an Error if the process ends with an error code
654
+ */
598
655
async function buildTestDriver ( run : vscode . TestRun ) {
599
656
/**
600
657
* TODO replace this with a task invocation to capture problems
@@ -620,6 +677,16 @@ async function buildTestDriver(run: vscode.TestRun) {
620
677
}
621
678
}
622
679
680
+ /**
681
+ * Decide the outcome of the given test based on the test driver output, and
682
+ * report it to the TestRun object.
683
+ *
684
+ * @param test - the test whose outcome must be decided
685
+ * @param driverOutput - the test driver standard output
686
+ * @param run - the TestRun object to report the outcome on
687
+ * @param duration - the duration of execution of the test to be reported along
688
+ * with the outcome, if the information is available.
689
+ */
623
690
function determineTestOutcome (
624
691
test : vscode . TestItem ,
625
692
driverOutput : string ,
@@ -690,6 +757,12 @@ function determineTestOutcome(
690
757
}
691
758
}
692
759
760
+ /**
761
+ *
762
+ * @param items - a {@link vscode.TestItemCollection}
763
+ * @param token - a cancellation token to stop the traversal
764
+ * @returns the array of leaf TestItems reachable from the given collection.
765
+ */
693
766
function collectLeafsFromCollection (
694
767
items : vscode . TestItemCollection ,
695
768
token ?: CancellationToken
@@ -704,6 +777,12 @@ function collectLeafsFromCollection(
704
777
return res ;
705
778
}
706
779
780
+ /**
781
+ *
782
+ * @param item - a {@link vscode.TestItem}
783
+ * @param token - a cancellation token to stop the traversal
784
+ * @returns the array of leaf TestItems reachable from the given TestItem
785
+ */
707
786
function collectLeafItems ( item : TestItem , token ?: CancellationToken ) : vscode . TestItem [ ] {
708
787
if ( item . children . size > 0 ) {
709
788
const res : vscode . TestItem [ ] = [ ] ;
@@ -719,11 +798,27 @@ function collectLeafItems(item: TestItem, token?: CancellationToken): vscode.Tes
719
798
}
720
799
}
721
800
801
+ /**
802
+ *
803
+ * @param text - a string possibly containing special RegExp characters.
804
+ * @returns a string where all RegExp special characters have been escaped. This
805
+ * can be useful when searching for an exact string which may contain special
806
+ * characters.
807
+ */
722
808
function escapeRegExp ( text : string ) {
723
809
return text . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, '\\$&' ) ;
724
810
}
725
811
726
- function logAndRun ( run : vscode . TestRun , cmd : string [ ] ) {
812
+ /**
813
+ *
814
+ * Run a command line as a child process and pipe the output into the TestRun
815
+ * object managing the test execution.
816
+ *
817
+ * @param run - the TestRun object hosting the execution
818
+ * @param cmd - a command line to run
819
+ * @returns the child process object returned by {@link cp.spawnSync}
820
+ */
821
+ function logAndRun ( run : vscode . TestRun , cmd : string [ ] ) : cp . SpawnSyncReturns < Buffer > {
727
822
run . appendOutput ( `$ ${ cmd . map ( ( arg ) => `"${ arg } "` ) . join ( ' ' ) } \r\n` ) ;
728
823
return cp . spawnSync ( cmd [ 0 ] , cmd . slice ( 1 ) ) ;
729
824
}
0 commit comments