mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-20 00:04:14 +08:00
testing: add method to publish extension-provided results
This commit is contained in:
parent
52ebf0c7f8
commit
5a0bf3751a
@ -94,19 +94,19 @@ export namespace Iterable {
|
||||
/**
|
||||
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
|
||||
*/
|
||||
export function* slice<T>(iterable: ReadonlyArray<T>, from: number, to = iterable.length): Iterable<T> {
|
||||
export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
|
||||
if (from < 0) {
|
||||
from += iterable.length;
|
||||
from += arr.length;
|
||||
}
|
||||
|
||||
if (to < 0) {
|
||||
to += iterable.length;
|
||||
} else if (to > iterable.length) {
|
||||
to = iterable.length;
|
||||
to += arr.length;
|
||||
} else if (to > arr.length) {
|
||||
to = arr.length;
|
||||
}
|
||||
|
||||
for (; from < to; from++) {
|
||||
yield iterable[from];
|
||||
yield arr[from];
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,4 +135,25 @@ export namespace Iterable {
|
||||
|
||||
return [consumed, { [Symbol.iterator]() { return iterator; } }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the iterables are the same length and all items are
|
||||
* equal using the comparator function.
|
||||
*/
|
||||
export function equals<T>(a: Iterable<T>, b: Iterable<T>, comparator = (at: T, bt: T) => at === bt) {
|
||||
const ai = a[Symbol.iterator]();
|
||||
const bi = b[Symbol.iterator]();
|
||||
while (true) {
|
||||
const an = ai.next();
|
||||
const bn = bi.next();
|
||||
|
||||
if (an.done !== bn.done) {
|
||||
return false;
|
||||
} else if (an.done) {
|
||||
return true;
|
||||
} else if (!comparator(an.value, bn.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,4 +25,11 @@ suite('Iterable', function () {
|
||||
assert.equal(Iterable.first(customIterable), 'one'); // fresh
|
||||
});
|
||||
|
||||
test('equals', () => {
|
||||
assert.strictEqual(Iterable.equals([1, 2], [1, 2]), true);
|
||||
assert.strictEqual(Iterable.equals([1, 2], [1]), false);
|
||||
assert.strictEqual(Iterable.equals([1], [1, 2]), false);
|
||||
assert.strictEqual(Iterable.equals([2, 1], [1, 2]), false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
24
src/vs/vscode.proposed.d.ts
vendored
24
src/vs/vscode.proposed.d.ts
vendored
@ -2227,6 +2227,22 @@ declare module 'vscode' {
|
||||
*/
|
||||
export function createDocumentTestObserver(document: TextDocument): TestObserver;
|
||||
|
||||
/**
|
||||
* Inserts custom test results into the VS Code UI. The results are
|
||||
* inserted and sorted based off the `completedAt` timestamp. If the
|
||||
* results are being read from a file, for example, the `completedAt`
|
||||
* time should generally be the modified time of the file if not more
|
||||
* specific time is available.
|
||||
*
|
||||
* This will no-op if the inserted results are deeply equal to an
|
||||
* existing result.
|
||||
*
|
||||
* @param results test results
|
||||
* @param persist whether the test results should be saved by VS Code
|
||||
* and persisted across reloads. Defaults to true.
|
||||
*/
|
||||
export function publishTestResult(results: TestResults, persist?: boolean): void;
|
||||
|
||||
/**
|
||||
* List of test results stored by VS Code, sorted in descnding
|
||||
* order by their `completedAt` time.
|
||||
@ -2553,7 +2569,7 @@ declare module 'vscode' {
|
||||
* may be out of date. If the test still exists in the workspace, consumers
|
||||
* can use its `id` to correlate the result instance with the living test.
|
||||
*
|
||||
* @todo coverage and other info may eventually live here
|
||||
* @todo coverage and other info may eventually be provided here
|
||||
*/
|
||||
export interface TestResults {
|
||||
/**
|
||||
@ -2573,6 +2589,12 @@ declare module 'vscode' {
|
||||
* provided in {@link TestResult} interfaces.
|
||||
*/
|
||||
export interface TestItemWithResults extends TestItem {
|
||||
/**
|
||||
* ID of the test result, this is required in order to correlate the result
|
||||
* with the live test item.
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Current result of the test.
|
||||
*/
|
||||
|
||||
@ -9,8 +9,8 @@ import { isDefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { getTestSubscriptionKey, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { getTestSubscriptionKey, ISerializedTestResults, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { HydratedTestResult, ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
|
||||
@ -48,11 +48,10 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
}
|
||||
|
||||
this._register(resultService.onResultsChanged(evt => {
|
||||
if ('completed' in evt) {
|
||||
const serialized = evt.completed.toJSON();
|
||||
if (serialized) {
|
||||
this.proxy.$publishTestResults([serialized]);
|
||||
}
|
||||
const results = 'completed' in evt ? evt.completed : ('inserted' in evt ? evt.inserted : undefined);
|
||||
const serialized = results?.toJSON();
|
||||
if (serialized) {
|
||||
this.proxy.$publishTestResults([serialized]);
|
||||
}
|
||||
}));
|
||||
|
||||
@ -63,6 +62,13 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void {
|
||||
this.resultService.push(new HydratedTestResult(results, persist));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
||||
@ -339,6 +339,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.runTests(provider);
|
||||
},
|
||||
publishTestResult(results, persist = true) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.publishExtensionProvidedResults(results, persist);
|
||||
},
|
||||
get onDidChangeTestResults() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTesting.onResultsChanged;
|
||||
|
||||
@ -1864,6 +1864,7 @@ export interface MainThreadTestingShape {
|
||||
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
|
||||
$retireTest(extId: string): void;
|
||||
}
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTes
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TestItem, TestState } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { TestItem, TestResults, TestState } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, SerializedTestResultItem, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
|
||||
@ -101,6 +101,12 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
}, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements vscode.test.publishTestResults
|
||||
*/
|
||||
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
|
||||
this.proxy.$publishExtensionProvidedResults(TestResults.from(generateUuid(), results), persist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates test results shown to extensions.
|
||||
@ -109,7 +115,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
public $publishTestResults(results: ISerializedTestResults[]): void {
|
||||
this.results = Object.freeze(
|
||||
results
|
||||
.map(r => deepFreeze(convertTestResults(r)))
|
||||
.map(r => deepFreeze(TestResults.to(r)))
|
||||
.concat(this.results)
|
||||
.sort((a, b) => b.completedAt - a.completedAt)
|
||||
.slice(0, 32),
|
||||
@ -355,29 +361,6 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
}
|
||||
}
|
||||
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestItemWithResults => ({
|
||||
...TestItem.toShallow(item.item),
|
||||
result: TestState.to(item.state),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
.filter(isDefined)
|
||||
.map(c => convertTestResultItem(c, byInternalId)),
|
||||
});
|
||||
|
||||
const convertTestResults = (r: ISerializedTestResults): vscode.TestResults => {
|
||||
const roots: SerializedTestResultItem[] = [];
|
||||
const byInternalId = new Map<string, SerializedTestResultItem>();
|
||||
for (const item of r.items) {
|
||||
byInternalId.set(item.id, item);
|
||||
if (item.direct) {
|
||||
roots.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return { completedAt: r.completedAt, results: roots.map(r => convertTestResultItem(r, byInternalId)) };
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children
|
||||
* to only the children that are located in a certain vscode.Uri.
|
||||
|
||||
@ -22,7 +22,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { isString, isNumber } from 'vs/base/common/types';
|
||||
import { isString, isNumber, isDefined } from 'vs/base/common/types';
|
||||
import * as marked from 'vs/base/common/marked/marked';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
@ -32,7 +32,7 @@ import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITestItem, ITestState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ISerializedTestResults, ITestItem, ITestState, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@ -1622,7 +1622,6 @@ export namespace TestState {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace TestItem {
|
||||
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
|
||||
return {
|
||||
@ -1649,3 +1648,67 @@ export namespace TestItem {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestResults {
|
||||
export function from(id: string, results: vscode.TestResults): ISerializedTestResults {
|
||||
const serialized: ISerializedTestResults = {
|
||||
completedAt: results.completedAt,
|
||||
id,
|
||||
items: [],
|
||||
};
|
||||
|
||||
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestItemWithResults>][] = [
|
||||
[null, results.results],
|
||||
];
|
||||
|
||||
let counter = 0;
|
||||
while (queue.length) {
|
||||
const [parent, children] = queue.pop()!;
|
||||
for (const item of children) {
|
||||
const serializedItem: SerializedTestResultItem = {
|
||||
children: item.children?.map(c => c.id) ?? [],
|
||||
computedState: item.result.state,
|
||||
id: `${id}-${counter++}`,
|
||||
item: TestItem.from(item, parent?.item.extId),
|
||||
state: TestState.from(item.result),
|
||||
retired: undefined,
|
||||
parent: parent?.id ?? null,
|
||||
providerId: '',
|
||||
direct: !parent,
|
||||
};
|
||||
|
||||
serialized.items.push(serializedItem);
|
||||
if (item.children) {
|
||||
queue.push([serializedItem, item.children]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestItemWithResults => ({
|
||||
...TestItem.toShallow(item.item),
|
||||
result: TestState.to(item.state),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
.filter(isDefined)
|
||||
.map(c => convertTestResultItem(c, byInternalId)),
|
||||
});
|
||||
|
||||
export function to(serialized: ISerializedTestResults): vscode.TestResults {
|
||||
const roots: SerializedTestResultItem[] = [];
|
||||
const byInternalId = new Map<string, SerializedTestResultItem>();
|
||||
for (const item of serialized.items) {
|
||||
byInternalId.set(item.id, item);
|
||||
if (item.direct) {
|
||||
roots.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
completedAt: serialized.completedAt,
|
||||
results: roots.map(r => convertTestResultItem(r, byInternalId)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ export interface TestResultItem extends IncrementalTestCollectionItem {
|
||||
/** True if the test is outdated */
|
||||
retired: boolean;
|
||||
/** True if the test was directly requested by the run (is not a child or parent) */
|
||||
direct?: true;
|
||||
direct?: boolean;
|
||||
}
|
||||
|
||||
export type SerializedTestResultItem = Omit<TestResultItem, 'children' | 'retired'> & { children: string[], retired: undefined };
|
||||
|
||||
@ -3,8 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { findFirstInSorted } from 'vs/base/common/arrays';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
@ -409,7 +412,7 @@ export class LiveTestResult implements ITestResult {
|
||||
/**
|
||||
* Test results hydrated from a previously-serialized test run.
|
||||
*/
|
||||
class HydratedTestResult implements ITestResult {
|
||||
export class HydratedTestResult implements ITestResult {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@ -434,7 +437,7 @@ class HydratedTestResult implements ITestResult {
|
||||
|
||||
private readonly byExtId = new Map<string, TestResultItem>();
|
||||
|
||||
constructor(private readonly serialized: ISerializedTestResults) {
|
||||
constructor(private readonly serialized: ISerializedTestResults, private readonly persist = true) {
|
||||
this.id = serialized.id;
|
||||
this.completedAt = serialized.completedAt;
|
||||
|
||||
@ -467,14 +470,15 @@ class HydratedTestResult implements ITestResult {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public toJSON(): ISerializedTestResults {
|
||||
return this.serialized;
|
||||
public toJSON(): ISerializedTestResults | undefined {
|
||||
return this.persist ? this.serialized : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export type ResultChangeEvent =
|
||||
| { completed: LiveTestResult }
|
||||
| { started: LiveTestResult }
|
||||
| { inserted: ITestResult }
|
||||
| { removed: ITestResult[] };
|
||||
|
||||
export interface ITestResultService {
|
||||
@ -502,7 +506,7 @@ export interface ITestResultService {
|
||||
/**
|
||||
* Adds a new test result to the collection.
|
||||
*/
|
||||
push(result: LiveTestResult): LiveTestResult;
|
||||
push<T extends ITestResult>(result: T): T;
|
||||
|
||||
/**
|
||||
* Looks up a set of test results by ID.
|
||||
@ -519,6 +523,14 @@ export const ITestResultService = createDecorator<ITestResultService>('testResul
|
||||
|
||||
const RETAIN_LAST_RESULTS = 64;
|
||||
|
||||
/**
|
||||
* Returns if the tests in the results are exactly equal. Check the counts
|
||||
* first as a cheap check before starting to iterate.
|
||||
*/
|
||||
const resultsEqual = (a: ITestResult, b: ITestResult) =>
|
||||
a.completedAt === b.completedAt && equals(a.counts, b.counts) && Iterable.equals(a.tests, b.tests,
|
||||
(at, bt) => equals(at.state, bt.state) && equals(at.item, bt.item));
|
||||
|
||||
export class TestResultService implements ITestResultService {
|
||||
declare _serviceBrand: undefined;
|
||||
private changeResultEmitter = new Emitter<ResultChangeEvent>();
|
||||
@ -579,17 +591,47 @@ export class TestResultService implements ITestResultService {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public push(result: LiveTestResult): LiveTestResult {
|
||||
this.results.unshift(result);
|
||||
public push<T extends ITestResult>(result: T): T {
|
||||
if (result.completedAt === undefined) {
|
||||
this.results.unshift(result);
|
||||
} else {
|
||||
const index = findFirstInSorted(this.results, r => r.completedAt !== undefined && r.completedAt <= result.completedAt!);
|
||||
const prev = this.results[index];
|
||||
if (prev && resultsEqual(result, prev)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this.results.splice(index, 0, result);
|
||||
this.persist();
|
||||
}
|
||||
|
||||
if (this.results.length > RETAIN_LAST_RESULTS) {
|
||||
this.results.pop();
|
||||
}
|
||||
|
||||
result.onComplete(() => this.onComplete(result));
|
||||
result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter);
|
||||
this.isRunning.set(true);
|
||||
this.changeResultEmitter.fire({ started: result });
|
||||
result.setAllToState(queuedState, () => true);
|
||||
if (result instanceof LiveTestResult) {
|
||||
result.onComplete(() => this.onComplete(result));
|
||||
result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter);
|
||||
this.isRunning.set(true);
|
||||
this.changeResultEmitter.fire({ started: result });
|
||||
result.setAllToState(queuedState, () => true);
|
||||
} else {
|
||||
this.changeResultEmitter.fire({ inserted: result });
|
||||
// If this is not a new result, go through each of its tests. For each
|
||||
// test for which the new result is the most recently inserted, fir
|
||||
// a change event so that UI updates.
|
||||
for (const item of result.tests) {
|
||||
for (const otherResult of this.results) {
|
||||
if (otherResult === result) {
|
||||
this.testChangeEmitter.fire({ item, result, reason: TestResultItemChangeReason.ComputedStateChange });
|
||||
break;
|
||||
} else if (otherResult.getStateByExtId(item.item.extId) !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -615,19 +657,26 @@ export class TestResultService implements ITestResultService {
|
||||
}
|
||||
|
||||
this.results = keep;
|
||||
this.serializedResults.store(this.results.map(r => r.toJSON()).filter(isDefined));
|
||||
this.persist();
|
||||
this.changeResultEmitter.fire({ removed });
|
||||
}
|
||||
|
||||
private onComplete(result: LiveTestResult) {
|
||||
// move the complete test run down behind any still-running ones
|
||||
this.resort();
|
||||
this.isRunning.set(this.results.length > 0 && this.results[0].completedAt === undefined);
|
||||
this.serializedResults.store(this.results.map(r => r.toJSON()).filter(isDefined));
|
||||
this.updateIsRunning();
|
||||
this.persist();
|
||||
this.changeResultEmitter.fire({ completed: result });
|
||||
}
|
||||
|
||||
private resort() {
|
||||
this.results.sort((a, b) => (b.completedAt ?? Number.MAX_SAFE_INTEGER) - (a.completedAt ?? Number.MAX_SAFE_INTEGER));
|
||||
}
|
||||
|
||||
private updateIsRunning() {
|
||||
this.isRunning.set(this.results.length > 0 && this.results[0].completedAt === undefined);
|
||||
}
|
||||
|
||||
private persist() {
|
||||
this.serializedResults.store(this.results.map(r => r.toJSON()).filter(isDefined));
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { LiveTestResult, makeEmptyCounts, TestResultItemChange, TestResultItemChangeReason, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { HydratedTestResult, LiveTestResult, makeEmptyCounts, TestResultItemChange, TestResultItemChangeReason, TestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ReExportedTestRunState as TestRunState } from 'vs/workbench/contrib/testing/common/testStubs';
|
||||
import { getInitializedMainTestCollection } from 'vs/workbench/contrib/testing/test/common/ownedTestCollection';
|
||||
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
@ -190,5 +190,51 @@ suite('Workbench - Test Results Service', () => {
|
||||
r.markComplete();
|
||||
assert.deepStrictEqual(results.results, [r, r2]);
|
||||
});
|
||||
|
||||
const makeHydrated = (completedAt = 42, state = TestRunState.Passed) => new HydratedTestResult({
|
||||
completedAt,
|
||||
id: 'some-id',
|
||||
items: [{
|
||||
...getInitializedMainTestCollection().getNodeById('2')!,
|
||||
state: { state: TestRunState.Passed, duration: 0, messages: [] },
|
||||
computedState: TestRunState.Passed,
|
||||
retired: undefined,
|
||||
children: [],
|
||||
}]
|
||||
});
|
||||
|
||||
test('pushes hydrated results', () => {
|
||||
results.push(r);
|
||||
const hydrated = makeHydrated();
|
||||
results.push(hydrated);
|
||||
assert.deepStrictEqual(results.results, [r, hydrated]);
|
||||
});
|
||||
|
||||
test('deduplicates identical results', () => {
|
||||
results.push(r);
|
||||
const hydrated1 = makeHydrated();
|
||||
results.push(hydrated1);
|
||||
const hydrated2 = makeHydrated();
|
||||
results.push(hydrated2);
|
||||
assert.deepStrictEqual(results.results, [r, hydrated1]);
|
||||
});
|
||||
|
||||
test('does not deduplicate if different completedAt', () => {
|
||||
results.push(r);
|
||||
const hydrated1 = makeHydrated();
|
||||
results.push(hydrated1);
|
||||
const hydrated2 = makeHydrated(30);
|
||||
results.push(hydrated2);
|
||||
assert.deepStrictEqual(results.results, [r, hydrated1, hydrated2]);
|
||||
});
|
||||
|
||||
test('does not deduplicate if different tests', () => {
|
||||
results.push(r);
|
||||
const hydrated1 = makeHydrated();
|
||||
results.push(hydrated1);
|
||||
const hydrated2 = makeHydrated(undefined, TestRunState.Failed);
|
||||
results.push(hydrated2);
|
||||
assert.deepStrictEqual(results.results, [r, hydrated1, hydrated2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user