| // 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(); |
| } |
| } |