[feature] Run individual test cases
We now show VS Code the individual test cases within each test binary,
which lets the user run or debug them individually from the UI.
Fixed: b/340669616
Change-Id: Ie4579251dfa4a54ef7f61379e549eb0222629917
Reviewed-on: https://fuchsia-review.googlesource.com/c/vscode-plugins/+/1048623
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Jacob Rutherford <jruthe@google.com>
diff --git a/src/ffx.ts b/src/ffx.ts
index ed4adb7..74d8557 100644
--- a/src/ffx.ts
+++ b/src/ffx.ts
@@ -415,7 +415,7 @@
device: string | undefined,
args: string[],
onData: (data: Object) => void
- ): FfxLog {
+ ): JsonStreamProcess {
let ffxArgs = (device ? ['--target', device] : []);
ffxArgs.push('--machine', 'json');
ffxArgs.push(...args);
@@ -541,5 +541,3 @@
});
}
}
-
-export type FfxLog = JsonStreamProcess;
diff --git a/src/fx.ts b/src/fx.ts
index 4f726fa..c0f1ac3 100644
--- a/src/fx.ts
+++ b/src/fx.ts
@@ -3,7 +3,8 @@
// found in the LICENSE file.
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
-import { DataStreamProcess } from './process';
+import { DataStreamProcess, JsonStreamProcess } from './process';
+import * as logger from './logger';
export class Fx {
constructor(public fuchsiaDir: string | undefined) {
@@ -29,4 +30,17 @@
}
return new DataStreamProcess(process, onData, onError);
}
+
+ public runJsonStreaming(
+ args: string[],
+ onData: (data: Object) => void
+ ): JsonStreamProcess {
+ return new JsonStreamProcess(
+ this.runStreaming(args),
+ onData,
+ (data) => {
+ logger.warn(`Error [fx ${args}]: ${data}`);
+ });
+ }
+
}
diff --git a/src/process.ts b/src/process.ts
index e7d19f1..5751bf3 100644
--- a/src/process.ts
+++ b/src/process.ts
@@ -78,4 +78,17 @@
public stop() {
this.process?.stop();
}
+
+ /**
+ * A future that resolves when the process exits.
+ */
+ public get exitCode(): Promise<number | null> {
+ return (async () => {
+ const exitCode = await this.process?.exitCode;
+ if (typeof exitCode === 'undefined') {
+ return null;
+ }
+ return exitCode;
+ })();
+ }
}
diff --git a/src/test_controller.ts b/src/test_controller.ts
index add4bbc..c3a1e2e 100644
--- a/src/test_controller.ts
+++ b/src/test_controller.ts
@@ -21,6 +21,8 @@
controller.resolveHandler = async (testItem?: TestItem) => {
if (!testItem) {
await discoverTests(controller);
+ } else {
+ await discoverTestCases(controller, setup.fx, testItem);
}
};
@@ -131,10 +133,12 @@
continue;
}
const relativePath = testData.relativePath;
- const uri = relativePath ? vscode.Uri.joinPath(workspaceFolder.uri, relativePath) : undefined;
+ const uri = relativePath ?
+ vscode.Uri.joinPath(workspaceFolder.uri, relativePath) : undefined;
const testItem = controller.createTestItem(testData.name, testData.prettyName, uri);
if (testData.os === 'fuchsia') {
testItem.tags = [new vscode.TestTag('FuchsiaTest')];
+ testItem.canResolveChildren = true;
}
controller.items.add(testItem);
}
@@ -180,6 +184,42 @@
}
/**
+ * discover the test cases within a given test item
+ * We use `fx test --list` to enumerate the test cases.
+ */
+async function discoverTestCases(controller: TestController, fx: Fx, testItem: TestItem) {
+ const args = ['test', '--logpath=-', '--list', testItem.id];
+ const process = fx.runJsonStreaming(args, (object: any) => {
+ const payload = object['payload'];
+ if (typeof payload !== 'object') {
+ return;
+ }
+ const testCases = payload['enumerate_test_cases'];
+ if (typeof testCases !== 'object') {
+ return;
+ }
+ const testCaseNames = testCases['test_case_names'];
+ if (typeof testCaseNames !== 'object') {
+ logger.error(`Invalid test_case_names field: ${testCaseNames}`);
+ return;
+ }
+ for (const testCaseName of testCaseNames) {
+ if (typeof testCaseName !== 'string') {
+ logger.error(`Invalid test_case_names arrray entry: ${testCaseName}`);
+ return;
+ }
+ if (testItem.children.get(testCaseName)) {
+ return;
+ }
+ const testCaseItem = controller.createTestItem(testCaseName, testCaseName, testItem.uri);
+ testItem.children.add(testCaseItem);
+ }
+ });
+
+ await process.exitCode;
+}
+
+/**
* convert a TestItemCollection into an Iterable<vscode.TestItem>
*/
function gatherTestItems(collection: vscode.TestItemCollection): Iterable<vscode.TestItem> {
@@ -227,7 +267,14 @@
for (const testItem of queue) {
run.started(testItem);
- const args = ['test', '--style', '--no-status', '--output', testItem.id];
+ var args = ['test', '--style', '--no-status', '--output'];
+ const parentTestItem = testItem.parent;
+ if (parentTestItem) {
+ args.push(parentTestItem.id, '--test-filter', testItem.id);
+ } else {
+ args.push(testItem.id);
+ }
+
const prettyArgs = `fx ${args.join(' ')}`;
let action = 'Running';