未验证 提交 c36a0992 编写于 作者: C Connor Peet

testing: implement retired test state

Fixes https://github.com/microsoft/vscode/issues/115086
上级 e9b69cd2
......@@ -51,6 +51,17 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
}
}
/**
* @inheritdoc
*/
$retireTest(extId: string): void {
for (const result of this.resultService.results) {
if (result instanceof LiveTestResult) {
result.retire(extId);
}
}
}
/**
* @inheritdoc
*/
......
......@@ -1843,6 +1843,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>;
$retireTest(extId: string): void;
}
// --- proxy identifiers
......
......@@ -170,6 +170,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
collection.addRoot(hierarchy.root, id);
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
hierarchy.onDidInvalidateTest?.(e => {
const internal = collection.getTestByReference(e);
if (!internal) {
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
} else {
this.proxy.$retireTest(internal.item.extId);
}
});
} catch (e) {
console.error(e);
}
......
......@@ -80,11 +80,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
}));
// when test states change, reflect in the tree
this._register(results.onTestChanged(([, { item, state, computedState }]) => {
// todo: optimize this to avoid needing to iterate
this._register(results.onTestChanged(([, { item, state, retired, computedState }]) => {
for (const i of this.items.values()) {
if (i.test.item.extId === item.extId) {
i.ownState = state.state;
i.retired = retired;
refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState);
this.addUpdated(i);
this.updateEmitter.fire();
return;
}
......@@ -229,6 +232,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1];
if (prevState) {
item.ownState = prevState.state.state;
item.retired = prevState.retired;
refreshComputedState(computedStateAccessor, item, this.addUpdated, prevState.computedState);
}
}
......
......@@ -41,6 +41,7 @@ export class HierarchicalElement implements ITestTreeElement {
}
public state = TestRunState.Unset;
public retired = false;
public ownState = TestRunState.Unset;
constructor(public readonly test: InternalTestItem, public readonly parentItem: HierarchicalFolder | HierarchicalElement) {
......@@ -73,6 +74,7 @@ export class HierarchicalFolder implements ITestTreeElement {
return Iterable.concatNested(Iterable.map(this.children, c => c.debuggable));
}
public retired = false;
public state = TestRunState.Unset;
public ownState = TestRunState.Unset;
......
......@@ -87,6 +87,11 @@ export interface ITestTreeElement {
*/
state: TestRunState;
/**
* Whether the node's test result is 'retired' -- from an outdated test run.
*/
readonly retired: boolean;
readonly ownState: TestRunState;
readonly label: string;
readonly parentItem: ITestTreeElement | null;
......
......@@ -41,6 +41,10 @@
margin-right: 0.25em;
}
.test-explorer .computed-state.retired {
opacity: 0.7;
}
.test-explorer .test-explorer-messages {
padding: 0 12px 8px;
}
......
......@@ -628,10 +628,19 @@ class ListAccessibilityProvider implements IListAccessibilityProvider<ITestTreeE
}
getAriaLabel(element: ITestTreeElement): string {
return localize({
let label = localize({
key: 'testing.treeElementLabel',
comment: ['label then the unit tests state, for example "Addition Tests (Running)"'],
}, '{0} ({1})', element.label, testStateNames[element.state]);
if (element.retired) {
label = localize({
key: 'testing.treeElementLabelOutdated',
comment: ['{0} is the original label in testing.treeElementLabel'],
}, '{0}, outdated result', label, testStateNames[element.state]);
}
return label;
}
}
......@@ -708,6 +717,10 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
const icon = testingStatesToIcons.get(element.state);
data.icon.className = 'computed-state ' + (icon ? ThemeIcon.asClassName(icon) : '');
if (element.retired) {
data.icon.className += ' retired';
}
const test = element.test;
if (test) {
if (test.item.location) {
......
......@@ -93,6 +93,7 @@ const itemToNode = (
item: { ...item.item },
state: unsetState,
computedState: TestRunState.Unset,
retired: false,
};
byExtId.set(n.item.extId, n);
......@@ -154,6 +155,7 @@ interface ISerializedResults {
interface TestResultItem extends IncrementalTestCollectionItem {
state: ITestState;
computedState: TestRunState;
retired: boolean;
}
/**
......@@ -300,6 +302,28 @@ export class LiveTestResult implements ITestResult {
}
}
/**
* Marks a test as retired. This can trigger it to be re-run in live mode.
*/
public retire(extId: string) {
const root = this.testByExtId.get(extId);
if (!root || root.retired) {
return;
}
const queue: Iterable<string>[] = [[root.id]];
while (queue.length) {
for (const id of queue.pop()!) {
const entry = this.testByInternalId.get(id);
if (entry && !entry.retired) {
entry.retired = true;
queue.push(entry.children);
this.changeEmitter.fire(entry);
}
}
}
}
/**
* Adds a test, by its ID, to the test run. This can end up being called
* if tests were started while discovery was still happening, so initially
......@@ -363,6 +387,7 @@ class HydratedTestResult implements ITestResult {
for (const [key, value] of serialized.items) {
this.map.set(key, value);
value.retired = true;
for (const message of value.state.messages) {
if (message.location) {
message.location.uri = URI.revive(message.location.uri);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册