diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index 8bb2b187745..c2c8ac5805b 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; -import { ExtensionRunTestsRequest, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestDiffOpType, TestResultState, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { ExtensionRunTestsRequest, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, SerializedTestMessage, TestDiffOpType, TestResultState, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage'; import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; @@ -176,7 +176,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh /** * @inheritdoc */ - public $appendTestMessagesInRun(runId: string, taskId: string, testId: string, messages: ITestMessage[]): void { + public $appendTestMessagesInRun(runId: string, taskId: string, testId: string, messages: SerializedTestMessage[]): void { const r = this.resultService.getResult(runId); if (r && r instanceof LiveTestResult) { for (const message of messages) { @@ -185,7 +185,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh message.location.range = Range.lift(message.location.range); } - r.appendMessage(testId, taskId, message); + r.appendMessage(testId, taskId, message as ITestMessage); } } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d8a28c2000d..e3e1eb4ced1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -24,7 +24,6 @@ import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model' import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; import * as modes from 'vs/editor/common/modes'; import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration'; -import { ILanguageStatus } from 'vs/workbench/services/languageStatus/common/languageStatusService'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationChange, IConfigurationData, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; @@ -60,12 +59,13 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { CoverageDetails, ExtensionRunTestsRequest, IFileCoverage, ISerializedTestResults, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, RunTestForControllerRequest, TestResultState, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { CoverageDetails, ExtensionRunTestsRequest, IFileCoverage, ISerializedTestResults, ITestItem, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, RunTestForControllerRequest, SerializedTestMessage, TestResultState, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { InternalTimelineOptions, Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { ActivationKind, ExtensionHostKind, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions'; import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; +import { ILanguageStatus } from 'vs/workbench/services/languageStatus/common/languageStatusService'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; import * as search from 'vs/workbench/services/search/common/search'; @@ -2161,7 +2161,7 @@ export interface MainThreadTestingShape { /** Updates the state of a test run in the given run. */ $updateTestStateInRun(runId: string, taskId: string, testId: string, state: TestResultState, duration?: number): void; /** Appends a message to a test in the run. */ - $appendTestMessagesInRun(runId: string, taskId: string, testId: string, messages: ITestMessage[]): void; + $appendTestMessagesInRun(runId: string, taskId: string, testId: string, messages: SerializedTestMessage[]): void; /** Appends raw output to the test run.. */ $appendOutputToRun(runId: string, taskId: string, output: VSBuffer, location?: ILocationDto, testId?: string): void; /** Triggered when coverage is added to test results. */ diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index a1c85f54bf3..650d3947320 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -14,7 +14,7 @@ import { MarshalledId } from 'vs/base/common/marshalling'; import { deepFreeze } from 'vs/base/common/objects'; import { isDefined } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; -import { ExtHostTestingShape, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostTestingShape, ILocationDto, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { InvalidTestItemError, TestItemImpl, TestItemRootImpl } from 'vs/workbench/api/common/extHostTestingPrivateApi'; @@ -340,6 +340,21 @@ class TestRunTracker extends Disposable { fn(test, ...args); }; + const appendMessages = (test: vscode.TestItem, messages: vscode.TestMessage | readonly vscode.TestMessage[]) => { + const converted = messages instanceof Array + ? messages.map(Convert.TestMessage.from) + : [Convert.TestMessage.from(messages)]; + + if (test.uri && test.range) { + const defaultLocation: ILocationDto = { range: Convert.Range.from(test.range), uri: test.uri }; + for (const message of converted) { + message.location = message.location || defaultLocation; + } + } + + this.proxy.$appendTestMessagesInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), converted); + }; + let ended = false; const run: vscode.TestRun = { isPersisted: this.dto.isPersisted, @@ -362,13 +377,11 @@ class TestRunTracker extends Disposable { this.proxy.$updateTestStateInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), TestResultState.Running); }), errored: guardTestMutation((test, messages, duration) => { - this.proxy.$appendTestMessagesInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), - messages instanceof Array ? messages.map(Convert.TestMessage.from) : [Convert.TestMessage.from(messages)]); + appendMessages(test, messages); this.proxy.$updateTestStateInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), TestResultState.Errored, duration); }), failed: guardTestMutation((test, messages, duration) => { - this.proxy.$appendTestMessagesInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), - messages instanceof Array ? messages.map(Convert.TestMessage.from) : [Convert.TestMessage.from(messages)]); + appendMessages(test, messages); this.proxy.$updateTestStateInRun(runId, taskId, TestId.fromExtHostTestItem(test, ctrlId).toString(), TestResultState.Failed, duration); }), passed: guardTestMutation((test, duration) => { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f10a80a7bc7..549ed7ceea1 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -32,7 +32,7 @@ import { SaveReason } from 'vs/workbench/common/editor'; import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import * as search from 'vs/workbench/contrib/search/common/search'; -import { CoverageDetails, DetailType, ICoveredCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestItemContext, ITestTag, SerializedTestResultItem, TestMessageType } from 'vs/workbench/contrib/testing/common/testCollection'; +import { CoverageDetails, DetailType, ICoveredCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestItemContext, ITestTag, SerializedTestErrorMessage, SerializedTestResultItem, TestMessageType } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -1640,7 +1640,7 @@ export namespace NotebookRendererScript { } export namespace TestMessage { - export function from(message: vscode.TestMessage): ITestErrorMessage { + export function from(message: vscode.TestMessage): SerializedTestErrorMessage { return { message: MarkdownString.fromStrict(message.message) || '', type: TestMessageType.Error, @@ -1650,10 +1650,11 @@ export namespace TestMessage { }; } - export function to(item: ITestErrorMessage): vscode.TestMessage { + export function to(item: SerializedTestErrorMessage): vscode.TestMessage { const message = new types.TestMessage(typeof item.message === 'string' ? item.message : MarkdownString.to(item.message)); message.actualOutput = item.actual; message.expectedOutput = item.expected; + message.location = item.location ? location.to(item.location) : undefined; return message; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d36356e7b79..d4c3cf082a6 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3324,6 +3324,7 @@ export class TestRunRequest implements vscode.TestRunRequest { export class TestMessage implements vscode.TestMessage { public expectedOutput?: string; public actualOutput?: string; + public location?: vscode.Location; public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) { const msg = new TestMessage(message); diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts index c7bc238a79b..d1666daeab0 100644 --- a/src/vs/workbench/contrib/testing/common/testCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -8,6 +8,7 @@ import { MarshalledId } from 'vs/base/common/marshalling'; import { URI } from 'vs/base/common/uri'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; +import { ILocationDto } from 'vs/workbench/api/common/extHost.protocol'; export const enum TestResultState { Unset = 0, @@ -109,6 +110,8 @@ export interface ITestErrorMessage { location: IRichLocation | undefined; } +export type SerializedTestErrorMessage = Omit & { location?: ILocationDto }; + export interface ITestOutputMessage { message: string; type: TestMessageType.Info; @@ -116,6 +119,10 @@ export interface ITestOutputMessage { location: IRichLocation | undefined; } +export type SerializedTestOutputMessage = Omit & { location?: ILocationDto }; + +export type SerializedTestMessage = SerializedTestErrorMessage | SerializedTestOutputMessage; + export type ITestMessage = ITestErrorMessage | ITestOutputMessage; export interface ITestTaskState { diff --git a/src/vs/workbench/test/browser/api/extHostTesting.test.ts b/src/vs/workbench/test/browser/api/extHostTesting.test.ts index de080ec74a8..f1d4fe47020 100644 --- a/src/vs/workbench/test/browser/api/extHostTesting.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTesting.test.ts @@ -11,11 +11,12 @@ import { mockObject, MockObject } from 'vs/base/test/common/mock'; import { MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; import { TestRunCoordinator, TestRunDto, TestRunProfileImpl } from 'vs/workbench/api/common/extHostTesting'; import * as convert from 'vs/workbench/api/common/extHostTypeConverters'; -import { TestMessage, TestResultState, TestRunProfileKind, TestTag } from 'vs/workbench/api/common/extHostTypes'; -import { TestDiffOpType, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection'; +import { TestMessage, TestResultState, TestRunProfileKind, TestTag, Location, Position, Range } from 'vs/workbench/api/common/extHostTypes'; +import { TestDiffOpType, TestItemExpandState, TestMessageType } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestItemImpl, testStubs } from 'vs/workbench/contrib/testing/common/testStubs'; import { TestSingleUseCollection } from 'vs/workbench/contrib/testing/test/common/ownedTestCollection'; +import { URI } from 'vs/workbench/workbench.web.api'; import type { TestItem, TestRunRequest } from 'vscode'; const simplify = (item: TestItem) => ({ @@ -572,6 +573,47 @@ suite('ExtHost Testing', () => { assert.deepStrictEqual(proxy.$addTestsToRun.args, expectedArgs); }); + test('adds test messages to run', () => { + const test1 = new TestItemImpl('ctrlId', 'id-c', 'test c', URI.file('/testc.txt')); + const test2 = new TestItemImpl('ctrlId', 'id-d', 'test d', URI.file('/testd.txt')); + test1.range = test2.range = new Range(new Position(0, 0), new Position(1, 0)); + single.root.children.replace([test1, test2]); + const task = c.createTestRun('ctrlId', single, req, 'hello world', false); + + const message1 = new TestMessage('some message'); + message1.location = new Location(URI.file('/a.txt'), new Position(0, 0)); + task.failed(test1, message1); + + const args = proxy.$appendTestMessagesInRun.args[0]; + assert.deepStrictEqual(proxy.$appendTestMessagesInRun.args[0], [ + args[0], + args[1], + new TestId(['ctrlId', 'id-c']).toString(), + [{ + message: 'some message', + type: TestMessageType.Error, + expected: undefined, + actual: undefined, + location: convert.location.from(message1.location) + }] + ]); + + // should use test location as default + task.failed(test2, new TestMessage('some message')); + assert.deepStrictEqual(proxy.$appendTestMessagesInRun.args[1], [ + args[0], + args[1], + new TestId(['ctrlId', 'id-d']).toString(), + [{ + message: 'some message', + type: TestMessageType.Error, + expected: undefined, + actual: undefined, + location: convert.location.from({ uri: test2.uri!, range: test2.range! }), + }] + ]); + }); + test('guards calls after runs are ended', () => { const task = c.createTestRun('ctrl', single, req, 'hello world', false); task.end();