未验证 提交 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 ...@@ -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 * @inheritdoc
*/ */
......
...@@ -1843,6 +1843,7 @@ export interface MainThreadTestingShape { ...@@ -1843,6 +1843,7 @@ export interface MainThreadTestingShape {
$publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void; $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void; $updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>; $runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
$retireTest(extId: string): void;
} }
// --- proxy identifiers // --- proxy identifiers
......
...@@ -170,6 +170,14 @@ export class ExtHostTesting implements ExtHostTestingShape { ...@@ -170,6 +170,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
collection.addRoot(hierarchy.root, id); collection.addRoot(hierarchy.root, id);
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1])); Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id)); 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) { } catch (e) {
console.error(e); console.error(e);
} }
......
...@@ -80,11 +80,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes ...@@ -80,11 +80,14 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
})); }));
// when test states change, reflect in the tree // 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()) { for (const i of this.items.values()) {
if (i.test.item.extId === item.extId) { if (i.test.item.extId === item.extId) {
i.ownState = state.state; i.ownState = state.state;
i.retired = retired;
refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState); refreshComputedState(computedStateAccessor, i, this.addUpdated, computedState);
this.addUpdated(i);
this.updateEmitter.fire(); this.updateEmitter.fire();
return; return;
} }
...@@ -229,6 +232,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes ...@@ -229,6 +232,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1]; const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1];
if (prevState) { if (prevState) {
item.ownState = prevState.state.state; item.ownState = prevState.state.state;
item.retired = prevState.retired;
refreshComputedState(computedStateAccessor, item, this.addUpdated, prevState.computedState); refreshComputedState(computedStateAccessor, item, this.addUpdated, prevState.computedState);
} }
} }
......
...@@ -41,6 +41,7 @@ export class HierarchicalElement implements ITestTreeElement { ...@@ -41,6 +41,7 @@ export class HierarchicalElement implements ITestTreeElement {
} }
public state = TestRunState.Unset; public state = TestRunState.Unset;
public retired = false;
public ownState = TestRunState.Unset; public ownState = TestRunState.Unset;
constructor(public readonly test: InternalTestItem, public readonly parentItem: HierarchicalFolder | HierarchicalElement) { constructor(public readonly test: InternalTestItem, public readonly parentItem: HierarchicalFolder | HierarchicalElement) {
...@@ -73,6 +74,7 @@ export class HierarchicalFolder implements ITestTreeElement { ...@@ -73,6 +74,7 @@ export class HierarchicalFolder implements ITestTreeElement {
return Iterable.concatNested(Iterable.map(this.children, c => c.debuggable)); return Iterable.concatNested(Iterable.map(this.children, c => c.debuggable));
} }
public retired = false;
public state = TestRunState.Unset; public state = TestRunState.Unset;
public ownState = TestRunState.Unset; public ownState = TestRunState.Unset;
......
...@@ -87,6 +87,11 @@ export interface ITestTreeElement { ...@@ -87,6 +87,11 @@ export interface ITestTreeElement {
*/ */
state: TestRunState; state: TestRunState;
/**
* Whether the node's test result is 'retired' -- from an outdated test run.
*/
readonly retired: boolean;
readonly ownState: TestRunState; readonly ownState: TestRunState;
readonly label: string; readonly label: string;
readonly parentItem: ITestTreeElement | null; readonly parentItem: ITestTreeElement | null;
......
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
margin-right: 0.25em; margin-right: 0.25em;
} }
.test-explorer .computed-state.retired {
opacity: 0.7;
}
.test-explorer .test-explorer-messages { .test-explorer .test-explorer-messages {
padding: 0 12px 8px; padding: 0 12px 8px;
} }
......
...@@ -628,10 +628,19 @@ class ListAccessibilityProvider implements IListAccessibilityProvider<ITestTreeE ...@@ -628,10 +628,19 @@ class ListAccessibilityProvider implements IListAccessibilityProvider<ITestTreeE
} }
getAriaLabel(element: ITestTreeElement): string { getAriaLabel(element: ITestTreeElement): string {
return localize({ let label = localize({
key: 'testing.treeElementLabel', key: 'testing.treeElementLabel',
comment: ['label then the unit tests state, for example "Addition Tests (Running)"'], comment: ['label then the unit tests state, for example "Addition Tests (Running)"'],
}, '{0} ({1})', element.label, testStateNames[element.state]); }, '{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 ...@@ -708,6 +717,10 @@ class TestsRenderer implements ITreeRenderer<ITestTreeElement, FuzzyScore, TestT
const icon = testingStatesToIcons.get(element.state); const icon = testingStatesToIcons.get(element.state);
data.icon.className = 'computed-state ' + (icon ? ThemeIcon.asClassName(icon) : ''); data.icon.className = 'computed-state ' + (icon ? ThemeIcon.asClassName(icon) : '');
if (element.retired) {
data.icon.className += ' retired';
}
const test = element.test; const test = element.test;
if (test) { if (test) {
if (test.item.location) { if (test.item.location) {
......
...@@ -93,6 +93,7 @@ const itemToNode = ( ...@@ -93,6 +93,7 @@ const itemToNode = (
item: { ...item.item }, item: { ...item.item },
state: unsetState, state: unsetState,
computedState: TestRunState.Unset, computedState: TestRunState.Unset,
retired: false,
}; };
byExtId.set(n.item.extId, n); byExtId.set(n.item.extId, n);
...@@ -154,6 +155,7 @@ interface ISerializedResults { ...@@ -154,6 +155,7 @@ interface ISerializedResults {
interface TestResultItem extends IncrementalTestCollectionItem { interface TestResultItem extends IncrementalTestCollectionItem {
state: ITestState; state: ITestState;
computedState: TestRunState; computedState: TestRunState;
retired: boolean;
} }
/** /**
...@@ -300,6 +302,28 @@ export class LiveTestResult implements ITestResult { ...@@ -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 * 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 * if tests were started while discovery was still happening, so initially
...@@ -363,6 +387,7 @@ class HydratedTestResult implements ITestResult { ...@@ -363,6 +387,7 @@ class HydratedTestResult implements ITestResult {
for (const [key, value] of serialized.items) { for (const [key, value] of serialized.items) {
this.map.set(key, value); this.map.set(key, value);
value.retired = true;
for (const message of value.state.messages) { for (const message of value.state.messages) {
if (message.location) { if (message.location) {
message.location.uri = URI.revive(message.location.uri); 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.
先完成此消息的编辑!
想要评论请 注册