blob: b5de4b2be91b5fd48a1b7460c09672b0bb8f9568 [file] [log] [blame] [edit]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as vscode from 'vscode';
import {
TestController,
TestItem,
} from 'vscode';
import { Setup } from '../extension';
import { Fx } from '../fx';
import { setUpTestControllerDiscovery } from './discovery';
/**
* register the test controller and related commands
*/
export function setUpTestController(ctx: vscode.ExtensionContext, setup: Setup) {
const controller = vscode.tests.createTestController('fuchsiaTests', 'Fuchsia Tests');
ctx.subscriptions.push(controller);
setUpTestControllerDiscovery(controller, setup, ctx.workspaceState);
controller.createRunProfile(
'Run',
vscode.TestRunProfileKind.Run,
(request, token) => {
void runTest(controller, setup.fx, false, request, token);
}
);
controller.createRunProfile(
'Debug',
vscode.TestRunProfileKind.Debug,
(request, token) => {
void runTest(controller, setup.fx, true, request, token);
},
false,
new vscode.TestTag('FuchsiaTest')
);
}
/**
* convert a TestItemCollection into an Iterable<vscode.TestItem>
*/
function gatherTestItems(collection: vscode.TestItemCollection): Iterable<vscode.TestItem> {
const items: vscode.TestItem[] = [];
collection.forEach(item => items.push(item));
return items;
}
/**
* prepare the given buffer for display in the test results terminal
*/
function formatForTerminal(buffer: Buffer) {
let string = new TextDecoder().decode(buffer);
return string.replace(/\n/g, '\r\n').replace(/\r\r\n/g, '\r\n');
}
/**
* run the given tests
*/
async function runTest(
controller: TestController,
fx: Fx,
debug: boolean,
request: vscode.TestRunRequest,
token: vscode.CancellationToken
) {
const run = controller.createTestRun(request);
try {
const queue: TestItem[] = [];
for (const testItem of request.include ?? gatherTestItems(controller.items)) {
if (request.exclude?.includes(testItem)) {
continue;
}
queue.push(testItem);
run.enqueued(testItem);
}
/**
* append the given buffer to the output for the current test run
*/
function handleOutput(buffer: Buffer) {
run.appendOutput(formatForTerminal(buffer));
}
for (const testItem of queue) {
run.started(testItem);
const testURL: string = testItem.parent?.id || testItem.id;
const args = ['test', '--style', '--no-status', '--output', testURL];
if (testItem.id !== testURL) {
args.push('--test-filter', testItem.id);
}
if (debug) {
args.push('--break-on-failure', '--use-existing-debugger');
const hasAttached = await vscode.commands.executeCommand('fuchsia.zxdb.attach', testURL);
if (!hasAttached) {
const message = new vscode.TestMessage(
`Unable to attach zxdb to ${testURL}\n` +
'See "Fuchsia Extension" in the `Output` tab for more details.\n' +
'Alternatively, you can run the test without debugging.'
);
run.errored(testItem, message);
continue;
}
}
const prettyArgs = `fx ${args.join(' ')}`;
let exitCode;
try {
const action = debug ? 'Debugging' : 'Running';
run.appendOutput(`${action}: \x1b[1m${prettyArgs}\x1b[0m\r\n`);
const testProcess = fx.runAsync(args, handleOutput, handleOutput);
if (!testProcess) {
const message = new vscode.TestMessage(`Cannot start '${prettyArgs}'`);
run.errored(testItem, message);
continue;
}
exitCode = await testProcess.exitCode;
} finally {
if (debug) {
await vscode.debug.stopDebugging();
}
}
if (exitCode === 0) {
run.passed(testItem);
} else {
let message: vscode.TestMessage;
if (typeof exitCode === 'number') {
message = new vscode.TestMessage(`'${prettyArgs}' exited with code ${exitCode}.`);
} else {
message = new vscode.TestMessage(`'${prettyArgs}' terminated unexpectedly.`);
}
run.failed(testItem, message);
}
}
} finally {
run.end();
}
}