Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TestMessageStackFrame API #14154

Merged
merged 6 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

- [plugin] move stubbed API TerminalShellIntegration into main API [#14168](https://github.com/eclipse-theia/theia/pull/14168) - Contributed on behalf of STMicroelectronics
- [plugin] support evolution on proposed API extensionAny [#14199](https://github.com/eclipse-theia/theia/pull/14199) - Contributed on behalf of STMicroelectronics
- [test] support TestMessage stack traces [#14154](https://github.com/eclipse-theia/theia/pull/14154) - Contributed on behalf of STMicroelectronics

<a name="breaking_changes_1.54.0">[Breaking Changes:](#breaking_changes_1.54.0)</a> -->
- [core] Updated AuthenticationService to handle multiple accounts per provider [#14149](https://github.com/eclipse-theia/theia/pull/14149) - Contributed on behalf of STMicroelectronics
Expand Down
11 changes: 11 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ export interface Range {
readonly endColumn: number;
}

export interface Position {
/**
* line number (starts at 1)
*/
readonly lineNumber: number,
/**
* column (starts at 1)
*/
readonly column: number
}

export { MarkdownStringDTO as MarkdownString };

export interface SerializedDocumentFilter {
Expand Down
16 changes: 15 additions & 1 deletion packages/plugin-ext/src/common/test-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
import { UriComponents } from './uri-components';
import { Location, Range } from './plugin-api-rpc-model';
import { isObject } from '@theia/core';
import * as languageProtocol from '@theia/core/shared/vscode-languageserver-protocol';

export enum TestRunProfileKind {
Run = 1,
Expand Down Expand Up @@ -74,17 +75,30 @@ export interface TestFailureDTO extends TestStateChangeDTO {
readonly duration?: number;
}

export namespace TestFailureDTO {
export function is(ref: unknown): ref is TestFailureDTO {
return isObject<TestFailureDTO>(ref)
&& (ref.state === TestExecutionState.Failed || ref.state === TestExecutionState.Errored);
}
}
export interface TestSuccessDTO extends TestStateChangeDTO {
readonly state: TestExecutionState.Passed;
readonly duration?: number;
}

export interface TestMessageStackFrameDTO {
uri?: languageProtocol.DocumentUri;
position?: languageProtocol.Position;
label: string;
}

export interface TestMessageDTO {
readonly expected?: string;
readonly actual?: string;
readonly location?: Location;
readonly location?: languageProtocol.Location;
readonly message: string | MarkdownString;
readonly contextValue?: string;
readonly stackTrace?: TestMessageStackFrameDTO[];
}

export interface TestItemDTO {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import { TreeViewWidget } from '../view/tree-view-widget';
import { CodeEditorWidgetUtil, codeToTheiaMappings, ContributionPoint } from './vscode-theia-menu-mappings';
import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { TestItem, TestMessage } from '@theia/test/lib/browser/test-service';
import { fromLocation } from '../hierarchy/hierarchy-types-converters';

export type ArgumentAdapter = (...args: unknown[]) => unknown[];

Expand Down Expand Up @@ -315,7 +314,8 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter {
actual: testMessage.actual,
expected: testMessage.expected,
contextValue: testMessage.contextValue,
location: testMessage.location ? fromLocation(testMessage.location) : undefined
location: testMessage.location,
stackTrace: testMessage.stackTrace
};
return [TestMessageArg.create(testItemReference, testMessageDTO)];
}
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-ext/src/main/browser/test-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import { CancellationToken, Disposable, Event, URI } from '@theia/core';
import { MAIN_RPC_CONTEXT, TestControllerUpdate, TestingExt, TestingMain } from '../../common';
import { RPCProtocol } from '../../common/rpc-protocol';
import { interfaces } from '@theia/core/shared/inversify';
import { TestExecutionState, TestItemDTO, TestItemReference, TestOutputDTO, TestRunDTO, TestRunProfileDTO, TestStateChangeDTO } from '../../common/test-types';
import {
TestExecutionState, TestItemDTO, TestItemReference, TestOutputDTO,
TestRunDTO, TestRunProfileDTO, TestStateChangeDTO
} from '../../common/test-types';
import { TestRunProfileKind } from '../../plugin/types-impl';
import { CommandRegistryMainImpl } from './command-registry-main';

Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ import {
TestTag,
TestRunRequest,
TestMessage,
TestMessageStackFrame,
ExtensionKind,
InlineCompletionItem,
InlineCompletionList,
Expand Down Expand Up @@ -1463,6 +1464,7 @@ export function createAPIFactory(
TestTag,
TestRunRequest,
TestMessage,
TestMessageStackFrame,
ExtensionKind,
InlineCompletionItem,
InlineCompletionList,
Expand Down
37 changes: 34 additions & 3 deletions packages/plugin-ext/src/plugin/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ import { TestItemImpl, TestItemCollection } from './test-item';
import { AccumulatingTreeDeltaEmitter, TreeDelta } from '@theia/test/lib/common/tree-delta';
import {
TestItemDTO, TestOutputDTO, TestExecutionState, TestRunProfileDTO,
TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference, TestMessageArg, TestMessageDTO
TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference, TestMessageArg, TestMessageDTO,
TestMessageStackFrameDTO
} from '../common/test-types';
import * as protocol from '@theia/core/shared/vscode-languageserver-protocol';
import { ChangeBatcher, observableProperty } from '@theia/test/lib/common/collections';
import { TestRunRequest } from './types-impl';
import { Location, Position, Range, TestRunRequest, URI } from './types-impl';
import { MarkdownString } from '../common/plugin-api-rpc-model';

type RefreshHandler = (token: theia.CancellationToken) => void | theia.Thenable<void>;
Expand Down Expand Up @@ -374,7 +376,36 @@ export class TestingExtImpl implements TestingExt {
actualOutput: testMessage.actual,
expectedOutput: testMessage.expected,
contextValue: testMessage.contextValue,
location: testMessage.location ? Convert.toLocation(testMessage.location) : undefined
location: this.toLocation(testMessage.location),
stackTrace: testMessage.stackTrace ? testMessage.stackTrace.map(frame => this.toStackFrame(frame)) : undefined
};
}

toLocation(location: protocol.Location | undefined): Location | undefined {
if (!location) {
return undefined;
}
return new Location(URI.parse(location.uri), this.toRange(location.range));
}

toRange(range: protocol.Range): Range {
return new Range(this.toPosition(range.start), this.toPosition(range.end));
}

toPosition(position: protocol.Position): Position;
toPosition(position: protocol.Position | undefined): Position | undefined;
toPosition(position: protocol.Position | undefined): Position | undefined {
if (!position) {
return undefined;
}
return new Position(position.line, position.character);
}

toStackFrame(stackFrame: TestMessageStackFrameDTO): theia.TestMessageStackFrame {
return {
label: stackFrame.label,
position: this.toPosition(stackFrame.position),
uri: stackFrame.uri ? URI.parse(stackFrame.uri) : undefined
};
}

Expand Down
44 changes: 38 additions & 6 deletions packages/plugin-ext/src/plugin/type-converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { CellRange, isTextStreamMime } from '@theia/notebook/lib/common';
import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering';

import { TestItemDTO, TestMessageDTO } from '../common/test-types';
import { TestItemDTO, TestMessageDTO, TestMessageStackFrameDTO } from '../common/test-types';
import { PluginIconPath } from './plugin-icon-path';

const SIDE_GROUP = -2;
Expand Down Expand Up @@ -134,12 +134,21 @@ export function fromRange(range: theia.Range | undefined): model.Range | undefin
endColumn: end.character + 1
};
}

export function fromPosition(position: types.Position | theia.Position): Position {
export function fromPosition(position: types.Position | theia.Position): Position;
export function fromPosition(position: types.Position | theia.Position | undefined): Position | undefined;
export function fromPosition(position: types.Position | theia.Position | undefined): Position | undefined {
if (!position) {
return undefined;
}
return { lineNumber: position.line + 1, column: position.character + 1 };
}

export function toPosition(position: Position): types.Position {
export function toPosition(position: Position): types.Position;
export function toPosition(position: Position | undefined): types.Position | undefined;
export function toPosition(position: Position | undefined): types.Position | undefined {
if (!position) {
return undefined;
}
return new types.Position(position.lineNumber - 1, position.column - 1);
}

Expand Down Expand Up @@ -474,6 +483,18 @@ export function fromLocation(location: theia.Location | undefined): model.Locati
};
}

export function fromLocationToLanguageServerLocation(location: theia.Location): lstypes.Location;
export function fromLocationToLanguageServerLocation(location: theia.Location | undefined): lstypes.Location | undefined;
export function fromLocationToLanguageServerLocation(location: theia.Location | undefined): lstypes.Location | undefined {
if (!location) {
return undefined;
}
return <lstypes.Location>{
uri: location.uri.toString(),
range: location.range
};
}

export function fromTextDocumentShowOptions(options: theia.TextDocumentShowOptions): model.TextDocumentShowOptions {
if (options.selection) {
return {
Expand Down Expand Up @@ -1697,15 +1718,26 @@ export namespace TestMessage {
return message.map(msg => TestMessage.from(msg)[0]);
}
return [{
location: fromLocation(message.location),
location: fromLocationToLanguageServerLocation(message.location),
message: fromMarkdown(message.message)!,
expected: message.expectedOutput,
actual: message.actualOutput,
contextValue: message.contextValue
contextValue: message.contextValue,
stackTrace: message.stackTrace && message.stackTrace.map(frame => TestMessageStackFrame.from(frame))
}];
}
}

export namespace TestMessageStackFrame {
export function from(stackTrace: theia.TestMessageStackFrame): TestMessageStackFrameDTO {
return {
label: stackTrace.label,
position: stackTrace.position,
uri: stackTrace?.uri?.toString()
};
}
}

export namespace TestItem {
export function from(test: theia.TestItem): TestItemDTO {
return <TestItemDTO>TestItem.fromPartial(test);
Expand Down
11 changes: 10 additions & 1 deletion packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3036,7 +3036,7 @@ export class DebugThread implements theia.DebugThread {
}

export class DebugStackFrame implements theia.DebugStackFrame {
constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { }
constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { }
}

@es5ClassCompat
Expand Down Expand Up @@ -3350,6 +3350,7 @@ export class TestMessage implements theia.TestMessage {
public actualOutput?: string;
public location?: theia.Location;
public contextValue?: string;
public stackTrace?: theia.TestMessageStackFrame[] | undefined;

public static diff(message: string | theia.MarkdownString, expected: string, actual: string): theia.TestMessage {
const msg = new TestMessage(message);
Expand All @@ -3366,6 +3367,14 @@ export class TestCoverageCount {
constructor(public covered: number, public total: number) { }
}

export class TestMessageStackFrame implements theia.TestMessageStackFrame {
constructor(
public label: string,
public uri?: theia.Uri,
public position?: Position
) { }
}

@es5ClassCompat
export class FileCoverage {

Expand Down
33 changes: 33 additions & 0 deletions packages/plugin/src/theia.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16966,6 +16966,34 @@ export module '@theia/plugin' {
error: string | MarkdownString | undefined;
}

/**
* A stack frame found in the {@link TestMessage.stackTrace}.
*/
export class TestMessageStackFrame {
/**
* The location of this stack frame. This should be provided as a URI if the
* location of the call frame can be accessed by the editor.
*/
uri?: Uri;

/**
* Position of the stack frame within the file.
*/
position?: Position;

/**
* The name of the stack frame, typically a method or function name.
*/
label: string;

/**
* @param label The name of the stack frame
* @param file The file URI of the stack frame
* @param position The position of the stack frame within the file
*/
constructor(label: string, uri?: Uri, position?: Position);
}

/**
* Message associated with the test state. Can be linked to a specific
* source range -- useful for assertion failures, for example.
Expand Down Expand Up @@ -17022,6 +17050,11 @@ export module '@theia/plugin' {
*/
contextValue?: string;

/**
* The stack trace associated with the message or failure.
*/
stackTrace?: TestMessageStackFrame[];

/**
* Creates a new TestMessage that will present as a diff in the editor.
* @param message Message to display to the user.
Expand Down
12 changes: 8 additions & 4 deletions packages/test/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,29 @@
}

.theia-test-view .passed,
.theia-test-result-view .passed {
.theia-test-run-view .passed {
color: var(--theia-successBackground);
}

.theia-test-view .failed,
.theia-test-result-view .failed {
.theia-test-run-view .failed {
color: var(--theia-editorError-foreground);
}

.theia-test-view .errored,
.theia-test-result-view .errored {
.theia-test-run-view .errored {
color: var(--theia-editorError-foreground);
}

.theia-test-view .queued,
.theia-test-result-view .queued {
.theia-test-run-view .queued {
color: var(--theia-editorWarning-foreground);
}

.theia-test-result-view .debug-frame {
white-space: pre;
}

.theia-test-view .theia-TreeNode:not(:hover):not(.theia-mod-selected) .theia-test-tree-inline-action {
display: none;
}
13 changes: 10 additions & 3 deletions packages/test/src/browser/test-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// *****************************************************************************

import { CancellationToken, ContributionProvider, Disposable, Emitter, Event, QuickPickService, isObject, nls } from '@theia/core/lib/common';
import { CancellationTokenSource, Location, Range } from '@theia/core/shared/vscode-languageserver-protocol';
import { CancellationTokenSource, Location, Range, Position, DocumentUri } from '@theia/core/shared/vscode-languageserver-protocol';
import { CollectionDelta, TreeDelta } from '../common/tree-delta';
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
import URI from '@theia/core/lib/common/uri';
Expand Down Expand Up @@ -56,9 +56,16 @@ export enum TestExecutionState {
export interface TestMessage {
readonly expected?: string;
readonly actual?: string;
readonly location: Location;
readonly location?: Location;
readonly message: string | MarkdownString;
readonly contextValue?: string;
readonly stackTrace?: TestMessageStackFrame[];
}

export interface TestMessageStackFrame {
readonly label: string,
readonly uri?: DocumentUri,
readonly position?: Position,
}

export namespace TestMessage {
Expand Down Expand Up @@ -367,7 +374,7 @@ export class DefaultTestService implements TestService {

selectDefaultProfile(): void {
this.pickProfileKind().then(kind => {
const profiles = this.getControllers().flatMap(c => c.testRunProfiles).filter(profile => profile.kind === kind);
const profiles = this.getControllers().flatMap(c => c.testRunProfiles).filter(profile => profile.kind === kind);
this.pickProfile(profiles, nls.localizeByDefault('Pick a test profile to use')).then(activeProfile => {
if (activeProfile) {
// only change the default for the controller containing selected profile for default and its profiles with same kind
Expand Down
Loading
Loading