From bf2e8f9a70b04e1d0e0819f0d891e7bad40503fb Mon Sep 17 00:00:00 2001 From: "Craig S. Keogh" Date: Sat, 29 Jun 2024 11:46:31 +0930 Subject: [PATCH] Categorise test cases into test suite(s) Meson test cases may belong to test suite(s). If so, in VSCode's "Testing" feature from the Activity Bar, add the test suite(s) as a parent item and add the test cases as children. The user has the choice to run all tests belonging to a test suite. Will pass the test suite to the meson test command --suite. The option --suite is needed to run test cases that the meson config purposely excludes (for example, test suite "flaky"). Fixes #245 Quote the "test case" in the meson command, so can run test cases with a space in the name. The quotes are needed because `cp.ExecFileOptions` sets `shell=true`. Fixes #256 --- src/tests.ts | 86 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/src/tests.ts b/src/tests.ts index a6b2d78..f88f571 100644 --- a/src/tests.ts +++ b/src/tests.ts @@ -4,18 +4,73 @@ import { Tests, DebugEnvironmentConfiguration } from "./types"; import { getMesonTests, getMesonTargets } from "./introspection"; import { workspaceState } from "./extension"; -export async function rebuildTests(controller: vscode.TestController) { - let tests = await getMesonTests(workspaceState.get("mesonbuild.buildDir")!); +function addMesonTestsToController( + controller: vscode.TestController, + tests: Tests, +): WeakMap | null { + /* returns a WeakMap (value True) for TestItems with corresponding Meson + * tests. If a TestItem is not in the WeakMap, then the TestItem is outdated. + * If 0 existing TestItems, then there is nothing outdated, returns null. + */ + const testsVisited = new WeakMap(); + const emptyController = controller.items.size == 0; + for (const test of tests) { + if (test.suite == undefined || test.suite.length == 0) { + const testItem = controller.createTestItem(test.name, test.name); + controller.items.add(testItem); + if (!emptyController) { + testsVisited.set(testItem, true); + } + } else { + /* if test suite(s) are defined, create TestItems as children */ + for (const suiteLabel of test.suite) { + let suite = controller.items.get(suiteLabel); + if (suite == undefined) { + suite = controller.createTestItem(suiteLabel, suiteLabel); + controller.items.add(suite); + } + if (!emptyController) { + testsVisited.set(suite, true); + } + let testItem = suite.children.get(test.name); + if (testItem == undefined) { + testItem = controller.createTestItem(test.name, test.name); + suite.children.add(testItem); + } + if (!emptyController) { + testsVisited.set(testItem, true); + } + } + } + } + return emptyController ? null : testsVisited; +} - controller.items.forEach((item) => { - if (!tests.some((test) => item.id == test.name)) { - controller.items.delete(item.id); +function deleteTestsFromControllerNotVisited( + controller: vscode.TestController, + testsVisited: WeakMap, +) { + for (const [test_id, test] of controller.items) { + if (testsVisited.get(test) == undefined) { + for (const [child_id] of test.children) { + test.children.delete(child_id); + } + controller.items.delete(test_id); + } else { + for (const [child_id, child] of test.children) { + if (testsVisited.get(child) == undefined) { + test.children.delete(child_id); + } + } } - }); + } +} - for (let testDescr of tests) { - let testItem = controller.createTestItem(testDescr.name, testDescr.name); - controller.items.add(testItem); +export async function rebuildTests(controller: vscode.TestController) { + let tests = await getMesonTests(workspaceState.get("mesonbuild.buildDir")!); + const testsVisited = addMesonTestsToController(controller, tests); + if (testsVisited != null) { + deleteTestsFromControllerNotVisited(controller, testsVisited); } } @@ -38,10 +93,21 @@ export async function testRunHandler( for (let test of queue) { run.started(test); let starttime = Date.now(); + let suite = ""; + let testcase = ""; + if (test.children.size > 0) { + suite = `--suite="${test.id}"`; + } else if (test.parent != undefined) { + suite = `--suite="${test.parent.id}"`; + testcase = `"${test.id}"`; + } else { + testcase = `"${test.id}"`; + } + try { await exec( extensionConfiguration("mesonPath"), - ["test", "-C", buildDir, "--print-errorlog", test.id], + ["test", "-C", buildDir, "--print-errorlog", suite, testcase], extensionConfiguration("testEnvironment"), ); let duration = Date.now() - starttime;