@@ -14,7 +14,9 @@ export let testRunProfile: vscode.TestRunProfile;
1414
1515/**
1616 * 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.
1820 */
1921export type Root = {
2022 tests_mapping : TestMapping ;
@@ -34,7 +36,6 @@ type Unit = {
3436type TestUnit = {
3537 '@_target_file' : string ;
3638 tested : Tested [ ] ;
37- dangling : Dangling ;
3839} ;
3940
4041type Tested = {
@@ -58,10 +59,6 @@ type Test = {
5859 '@_name' : string ;
5960} ;
6061
61- type Dangling = {
62- test : Test [ ] ;
63- } ;
64-
6562/**
6663 * The XML paths in GNATtest XML files that should always be parsed as arrays.
6764 * This is based the code in src/test-mapping.adb in the libadalang-tools
@@ -72,7 +69,6 @@ const alwaysArray = [
7269 'tests_mapping.unit.test_unit' ,
7370 'tests_mapping.unit.test_unit.tested' ,
7471 'tests_mapping.unit.test_unit.tested.test_case' ,
75- 'tests_mapping.unit.test_unit.dangling.test' ,
7672 'tests_mapping.additional_tests' ,
7773] ;
7874
@@ -133,24 +129,39 @@ async function refreshTestItemTree() {
133129 await addTestsRootLevel ( ) ;
134130}
135131
132+ /**
133+ * @returns the full path to the GNATtest XML file.
134+ */
136135async function getGnatTestXmlPath ( ) : Promise < string > {
137136 const objDir = await getObjectDir ( ) ;
138137 const gnatTestXmlPath = path . join ( objDir , 'gnattest' , 'harness' , 'gnattest.xml' ) ;
139138 return gnatTestXmlPath ;
140139}
141140
141+ /**
142+ *
143+ * @returns the full path to the GNATtest test driver GPR project.
144+ */
142145async function getGnatTestDriverProjectPath ( ) : Promise < string > {
143146 const objDir = await getObjectDir ( ) ;
144147 const testDriverPath = path . join ( objDir , 'gnattest' , 'harness' , 'test_driver.gpr' ) ;
145148 return testDriverPath ;
146149}
147150
151+ /**
152+ *
153+ * @returns the full path to the GNATtest test driver executable.
154+ */
148155async function getGnatTestDriverExecPath ( ) : Promise < string > {
149156 const objDir = await getObjectDir ( ) ;
150157 const testDriverPath = path . join ( objDir , 'gnattest' , 'harness' , 'test_runner' + exe ) ;
151158 return testDriverPath ;
152159}
153160
161+ /**
162+ * Parse the GNATtest XML file and create the top-level TestItems in the test
163+ * controller for later lazy resolution.
164+ */
154165export async function addTestsRootLevel ( ) {
155166 if ( fs . existsSync ( await getGnatTestXmlPath ( ) ) ) {
156167 const xmlDoc : Root = await parseGnatTestXml ( ) ;
@@ -161,7 +172,10 @@ export async function addTestsRootLevel() {
161172 }
162173}
163174
164- async function parseGnatTestXml ( ) {
175+ /**
176+ * @returns the object resulting from parsing the GNATtest XML file.
177+ */
178+ async function parseGnatTestXml ( ) : Promise < Root > {
165179 const gnatTestXmlUri = vscode . Uri . file ( await getGnatTestXmlPath ( ) ) ;
166180 const fileContent = await vscode . workspace . fs . readFile ( gnatTestXmlUri ) ;
167181 const fileContentAsBuffer = Buffer . from ( fileContent ) ;
@@ -182,9 +196,11 @@ async function parseGnatTestXml() {
182196 return xmlDoc ;
183197}
184198
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+ */
188204async function addUnitItem ( unit : Unit ) {
189205 const srcFile = unit [ '@_source_file' ] ;
190206 const srcUri = await findFileInWorkspace ( srcFile ) ;
@@ -205,15 +221,25 @@ async function addUnitItem(unit: Unit) {
205221 }
206222}
207223
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+ */
208230function resolveUnitItem ( testItem : TestItem , unit : Unit ) {
209231 for ( const t of unit . test_unit . flatMap ( ( u ) => u . tested ) ) {
210232 addTestedItem ( testItem , t ) ;
211233 }
212234}
213235
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+ */
217243function addTestedItem ( parentTestItem : vscode . TestItem , tested : Tested ) {
218244 const testedSubprogramName = tested [ '@_name' ] ;
219245 const pos = new vscode . Position (
@@ -248,12 +274,25 @@ function addTestedItem(parentTestItem: vscode.TestItem, tested: Tested) {
248274 }
249275}
250276
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+ */
251283async function resolveTestedItem ( testItem : TestItem , tested : Tested ) {
252284 for ( const e of tested . test_case ) {
253285 await addTestCaseItem ( testItem , e ) ;
254286 }
255287}
256288
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+ */
257296async function addTestCaseItem ( parentItem : vscode . TestItem , testCase : TestCase ) {
258297 const test : Test = testCase . test ;
259298 const testFileBasename = test [ '@_file' ] ;
@@ -321,12 +360,18 @@ function createTestCaseItemId(testCase: TestCase, sourceFileName: string): strin
321360 return `${ sourceFileName } :${ testCase [ '@_line' ] } ` ;
322361}
323362
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 ) ;
326371 if ( matchingFiles . length > 0 ) {
327372 return matchingFiles [ 0 ] ;
328373 } else {
329- throw `Source file ${ name } not found in workspace` ;
374+ throw Error ( `Source file ${ basename } not found in workspace` ) ;
330375 }
331376}
332377
@@ -405,21 +450,26 @@ async function resolveHandler(
405450 }
406451}
407452
453+ /**
454+ * Configure the handlers needed on the test controller to support test execution.
455+ *
456+ * @param controller - the TestController to configure
457+ */
408458function configureTestExecution ( controller : vscode . TestController ) {
409459 testRunProfile = controller . createRunProfile (
410460 'GNATtest' ,
411461 vscode . TestRunProfileKind . Run ,
412462 runHandler
413463 ) ;
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- // };
421464}
422465
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+ */
423473async function runHandler ( request : vscode . TestRunRequest , token : vscode . CancellationToken ) {
424474 if ( ( request . include ?. length ?? 0 ) === 0 && ( request . exclude ?. length ?? 0 ) === 0 ) {
425475 /**
@@ -595,6 +645,13 @@ async function handleRunAll(request: vscode.TestRunRequest, token: CancellationT
595645 }
596646}
597647
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+ */
598655async function buildTestDriver ( run : vscode . TestRun ) {
599656 /**
600657 * TODO replace this with a task invocation to capture problems
@@ -620,6 +677,16 @@ async function buildTestDriver(run: vscode.TestRun) {
620677 }
621678}
622679
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+ */
623690function determineTestOutcome (
624691 test : vscode . TestItem ,
625692 driverOutput : string ,
@@ -690,6 +757,12 @@ function determineTestOutcome(
690757 }
691758}
692759
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+ */
693766function collectLeafsFromCollection (
694767 items : vscode . TestItemCollection ,
695768 token ?: CancellationToken
@@ -704,6 +777,12 @@ function collectLeafsFromCollection(
704777 return res ;
705778}
706779
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+ */
707786function collectLeafItems ( item : TestItem , token ?: CancellationToken ) : vscode . TestItem [ ] {
708787 if ( item . children . size > 0 ) {
709788 const res : vscode . TestItem [ ] = [ ] ;
@@ -719,11 +798,27 @@ function collectLeafItems(item: TestItem, token?: CancellationToken): vscode.Tes
719798 }
720799}
721800
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+ */
722808function escapeRegExp ( text : string ) {
723809 return text . replace ( / [ - [ \] { } ( ) * + ? . , \\ ^ $ | # \s ] / g, '\\$&' ) ;
724810}
725811
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 > {
727822 run . appendOutput ( `$ ${ cmd . map ( ( arg ) => `"${ arg } "` ) . join ( ' ' ) } \r\n` ) ;
728823 return cp . spawnSync ( cmd [ 0 ] , cmd . slice ( 1 ) ) ;
729824}
0 commit comments