From 82cf433befe7c66a30b610b3d9a975daffc336b8 Mon Sep 17 00:00:00 2001 From: Daniel Mita Date: Fri, 31 May 2024 00:24:32 +0100 Subject: [PATCH 1/2] Initial changes for test API integration --- client/src/extension.ts | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index 7c01078..e5f7fc9 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -4,7 +4,8 @@ * ------------------------------------------------------------------------------------------ */ import * as path from 'path'; -import { workspace, ExtensionContext } from 'vscode'; +import * as child_process from 'child_process'; +import { workspace, ExtensionContext, tests, TestRunProfileKind, TestMessage } from 'vscode'; import { LanguageClient, @@ -48,6 +49,38 @@ export function activate(context: ExtensionContext) { // fileEvents: workspace.createFileSystemWatcher('**/.clientrc') } }; + + const controller = tests.createTestController('perlTestController', 'Perl Test Controller'); + context.subscriptions.push(controller); + + workspace.findFiles('**/*.t').then((files) => { + for (const file of files) { + const testItem = controller.createTestItem(file.fsPath, file.fsPath, file); + controller.items.add(testItem); + } + }); + + controller.createRunProfile('Run Tests', TestRunProfileKind.Run, (request, token) => { + const run = controller.createTestRun(request); + + for (const test of request.include) { + run.started(test); + + child_process.exec(`prove ${test.uri.fsPath}`, (error, stdout, stderr) => { + if (error) { + run.failed(test, new TestMessage(`Error: ${error.message}`)); + } else if (stderr) { + run.failed(test, new TestMessage(`Error: ${stderr}`)); + run.appendOutput(stderr, undefined, test); + } else { + run.passed(test); + } + run.appendOutput(stdout, undefined, test); + + run.end(); + }); + } + }, true) // Create the language client and start the client. client = new LanguageClient( From 888ed57c5d801096a5490e593612e8ecffe500c3 Mon Sep 17 00:00:00 2001 From: Daniel Mita Date: Sun, 2 Jun 2024 17:06:45 +0100 Subject: [PATCH 2/2] Capture via API --- client/src/extension.ts | 115 ++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 33 deletions(-) diff --git a/client/src/extension.ts b/client/src/extension.ts index e5f7fc9..717c670 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import * as child_process from 'child_process'; -import { workspace, ExtensionContext, tests, TestRunProfileKind, TestMessage } from 'vscode'; +import { workspace, ExtensionContext, tests, TestRunProfileKind, TestItem, TestMessage, Location, TestTag, Position } from 'vscode'; import { LanguageClient, @@ -49,38 +49,6 @@ export function activate(context: ExtensionContext) { // fileEvents: workspace.createFileSystemWatcher('**/.clientrc') } }; - - const controller = tests.createTestController('perlTestController', 'Perl Test Controller'); - context.subscriptions.push(controller); - - workspace.findFiles('**/*.t').then((files) => { - for (const file of files) { - const testItem = controller.createTestItem(file.fsPath, file.fsPath, file); - controller.items.add(testItem); - } - }); - - controller.createRunProfile('Run Tests', TestRunProfileKind.Run, (request, token) => { - const run = controller.createTestRun(request); - - for (const test of request.include) { - run.started(test); - - child_process.exec(`prove ${test.uri.fsPath}`, (error, stdout, stderr) => { - if (error) { - run.failed(test, new TestMessage(`Error: ${error.message}`)); - } else if (stderr) { - run.failed(test, new TestMessage(`Error: ${stderr}`)); - run.appendOutput(stderr, undefined, test); - } else { - run.passed(test); - } - run.appendOutput(stdout, undefined, test); - - run.end(); - }); - } - }, true) // Create the language client and start the client. client = new LanguageClient( @@ -92,6 +60,87 @@ export function activate(context: ExtensionContext) { // Start the client. This will also launch the server client.start(); + + const ctrl = tests.createTestController('perlNavigator', 'Perl Navigator'); + context.subscriptions.push(ctrl); + + const fileTag = new TestTag('file'); + // Run profile for .t files marked with above tag + ctrl.createRunProfile('Suite', TestRunProfileKind.Run, (request) => { + let fileItems: ReadonlyArray; + // Play button clicked on a specific test + if (request.include && request.include.length > 0) { + fileItems = request.include; + } + // Run tests button for all + else { + fileItems = [...ctrl.items].map(([_, testItem]) => testItem); + } + + for (const fileItem of fileItems) { + fileItem.canResolveChildren = true; + const run = ctrl.createTestRun(request, fileItem.uri.fsPath); + run.started(fileItem); + // Extract via Test2::API to JSON + const execPromise = new Promise((resolve, reject) => { + child_process.exec(`perl -e ' + use Test2::API qw; + use JSON::PP qw; + + my $events = intercept { + do $ARGV[0]; + }; + + print encode_json($events->squash_info->flatten); + ' ${fileItem.uri.fsPath}`, (error, stdout, stderr) => { + if (stderr) { + console.log(stderr); + } + if (error) { + console.log(error.message); + } + if (stdout) { + console.log(stdout); + try { + const data = JSON.parse(stdout); + resolve(data); + } catch (e) { + console.error('Error parsing JSON:', e); + } + } + }); + }); + execPromise.then((data) => { + let count = 1; + for (const obj of data as any[]) { + console.log(obj); + if (obj.hasOwnProperty('pass')) { + const item = ctrl.createTestItem(`${fileItem.uri.fsPath}:${count}`, obj.name, fileItem.uri); + fileItem.children.add(item); + if (obj.pass === 1) { + run.passed(item); + run.appendOutput(`ok ${count} - ${obj.name}\n`, new Location(fileItem.uri, new Position(obj.trace_line - 1, 0)), item); + } + else { + run.failed(item, obj.diag.map((msg) => new TestMessage(msg))); + run.appendOutput(`not ok ${count} - ${obj.name}\n`, new Location(fileItem.uri, new Position(obj.trace_line - 1, 0)), item); + } + count++; + } + } + run.end(); + }); + } + }, true, fileTag); + + // Detect all .t files and create TestItems for them + workspace.findFiles('**/*.t').then((files) => { + for (const file of files) { + const testItem = ctrl.createTestItem(file.fsPath, file.fsPath, file); + testItem.tags = [fileTag], + ctrl.items.add(testItem); + } + }); } export function deactivate(): Thenable | undefined {