Skip to content

Commit ff6f70b

Browse files
authored
testing: move tests tool to core (microsoft#257350)
Allows it to be more reliable and also do incremental progress updates with proper cancellation. Closes microsoft/vscode-copilot-release#9899 Closes microsoft/vscode-copilot-release#10843 Closes microsoft/vscode-copilot-release#6757 Closes microsoft#256324 Closes microsoft#252179 (probably) Closes microsoft#250145 (probably)
1 parent 06785d0 commit ff6f70b

File tree

9 files changed

+413
-76
lines changed

9 files changed

+413
-76
lines changed

src/vs/workbench/contrib/testing/browser/testing.contribution.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { URI } from '../../../../base/common/uri.js';
67
import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js';
78
import { localize, localize2 } from '../../../../nls.js';
89
import { registerAction2 } from '../../../../platform/actions/common/actions.js';
@@ -17,17 +18,10 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js';
1718
import { IProgressService } from '../../../../platform/progress/common/progress.js';
1819
import { Registry } from '../../../../platform/registry/common/platform.js';
1920
import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js';
20-
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js';
21+
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
2122
import { IViewContainersRegistry, IViewsRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from '../../../common/views.js';
23+
import { IViewsService } from '../../../services/views/common/viewsService.js';
2224
import { REVEAL_IN_EXPLORER_COMMAND_ID } from '../../files/browser/fileConstants.js';
23-
import { CodeCoverageDecorations } from './codeCoverageDecorations.js';
24-
import { testingResultsIcon, testingViewIcon } from './icons.js';
25-
import { TestCoverageView } from './testCoverageView.js';
26-
import { TestingDecorationService, TestingDecorations } from './testingDecorations.js';
27-
import { TestingExplorerView } from './testingExplorerView.js';
28-
import { CloseTestPeek, CollapsePeekStack, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestResultsView, TestingOutputPeekController, TestingPeekOpener, ToggleTestingPeekHistory } from './testingOutputPeek.js';
29-
import { TestingProgressTrigger } from './testingProgressUiService.js';
30-
import { TestingViewPaneContainer } from './testingViewPaneContainer.js';
3125
import { testingConfiguration } from '../common/configuration.js';
3226
import { TestCommandId, Testing } from '../common/constants.js';
3327
import { ITestCoverageService, TestCoverageService } from '../common/testCoverageService.js';
@@ -39,16 +33,22 @@ import { ITestResultStorage, TestResultStorage } from '../common/testResultStora
3933
import { ITestService } from '../common/testService.js';
4034
import { TestService } from '../common/testServiceImpl.js';
4135
import { ITestItem, ITestRunProfileReference, TestRunProfileBitset } from '../common/testTypes.js';
36+
import { TestingChatAgentToolContribution } from '../common/testingChatAgentTool.js';
4237
import { TestingContentProvider } from '../common/testingContentProvider.js';
4338
import { TestingContextKeys } from '../common/testingContextKeys.js';
4439
import { ITestingContinuousRunService, TestingContinuousRunService } from '../common/testingContinuousRunService.js';
4540
import { ITestingDecorationsService } from '../common/testingDecorations.js';
4641
import { ITestingPeekOpener } from '../common/testingPeekOpener.js';
47-
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
48-
import { IViewsService } from '../../../services/views/common/viewsService.js';
42+
import { CodeCoverageDecorations } from './codeCoverageDecorations.js';
43+
import { testingResultsIcon, testingViewIcon } from './icons.js';
44+
import { TestCoverageView } from './testCoverageView.js';
4945
import { allTestActions, discoverAndRunTests } from './testExplorerActions.js';
5046
import './testingConfigurationUi.js';
51-
import { URI } from '../../../../base/common/uri.js';
47+
import { TestingDecorations, TestingDecorationService } from './testingDecorations.js';
48+
import { TestingExplorerView } from './testingExplorerView.js';
49+
import { CloseTestPeek, CollapsePeekStack, GoToNextMessageAction, GoToPreviousMessageAction, OpenMessageInEditorAction, TestingOutputPeekController, TestingPeekOpener, TestResultsView, ToggleTestingPeekHistory } from './testingOutputPeek.js';
50+
import { TestingProgressTrigger } from './testingProgressUiService.js';
51+
import { TestingViewPaneContainer } from './testingViewPaneContainer.js';
5252

5353
registerSingleton(ITestService, TestService, InstantiationType.Delayed);
5454
registerSingleton(ITestResultStorage, TestResultStorage, InstantiationType.Delayed);
@@ -139,9 +139,10 @@ registerAction2(CloseTestPeek);
139139
registerAction2(ToggleTestingPeekHistory);
140140
registerAction2(CollapsePeekStack);
141141

142-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingContentProvider, LifecyclePhase.Restored);
143-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingPeekOpener, LifecyclePhase.Eventually);
144-
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TestingProgressTrigger, LifecyclePhase.Eventually);
142+
registerWorkbenchContribution2(TestingContentProvider.ID, TestingContentProvider, WorkbenchPhase.AfterRestored);
143+
registerWorkbenchContribution2(TestingPeekOpener.ID, TestingPeekOpener, WorkbenchPhase.Eventually);
144+
registerWorkbenchContribution2(TestingProgressTrigger.ID, TestingProgressTrigger, WorkbenchPhase.Eventually);
145+
registerWorkbenchContribution2(TestingChatAgentToolContribution.ID, TestingChatAgentToolContribution, WorkbenchPhase.Eventually);
145146

146147
registerEditorContribution(Testing.OutputPeekContributionId, TestingOutputPeekController, EditorContributionInstantiation.AfterFirstRender);
147148
registerEditorContribution(Testing.DecorationsContributionId, TestingDecorations, EditorContributionInstantiation.AfterFirstRender);

src/vs/workbench/contrib/testing/browser/testingExplorerView.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import { ITestRunProfile, InternalTestItem, TestControllerCapability, TestItemEx
7171
import { TestingContextKeys } from '../common/testingContextKeys.js';
7272
import { ITestingContinuousRunService } from '../common/testingContinuousRunService.js';
7373
import { ITestingPeekOpener } from '../common/testingPeekOpener.js';
74+
import { CountSummary, collectTestStateCounts, getTestProgressText } from '../common/testingProgressMessages.js';
7475
import { cmpPriority, isFailedState, isStateWithResult, statesInOrder } from '../common/testingStates.js';
7576
import { ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage } from './explorerProjections/index.js';
7677
import { ListProjection } from './explorerProjections/listProjection.js';
@@ -82,7 +83,6 @@ import * as icons from './icons.js';
8283
import './media/testing.css';
8384
import { DebugLastRun, ReRunLastRun } from './testExplorerActions.js';
8485
import { TestingExplorerFilter } from './testingExplorerFilter.js';
85-
import { CountSummary, collectTestStateCounts, getTestProgressText } from './testingProgressUiService.js';
8686

8787
const enum LastFocusState {
8888
Input,

src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ function messageItReferenceToUri({ result, test, taskIndex, messageIndex }: IMes
111111
type TestUriWithDocument = ParsedTestUri & { documentUri: URI };
112112

113113
export class TestingPeekOpener extends Disposable implements ITestingPeekOpener {
114+
public static readonly ID = 'workbench.contrib.testing.peekOpener';
115+
114116
declare _serviceBrand: undefined;
115117

116118
private lastUri?: TestUriWithDocument;

src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55

66
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
77
import { autorun } from '../../../../base/common/observable.js';
8-
import { localize } from '../../../../nls.js';
98
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
10-
import { ExplorerTestCoverageBars } from './testCoverageBars.js';
9+
import { IViewsService } from '../../../services/views/common/viewsService.js';
1110
import { AutoOpenTesting, getTestingConfiguration, TestingConfigKeys } from '../common/configuration.js';
1211
import { Testing } from '../common/constants.js';
1312
import { ITestCoverageService } from '../common/testCoverageService.js';
1413
import { isFailedState } from '../common/testingStates.js';
15-
import { ITestResult, LiveTestResult, TestResultItemChangeReason } from '../common/testResult.js';
14+
import { LiveTestResult, TestResultItemChangeReason } from '../common/testResult.js';
1615
import { ITestResultService } from '../common/testResultService.js';
17-
import { TestResultState } from '../common/testTypes.js';
18-
import { IViewsService } from '../../../services/views/common/viewsService.js';
16+
import { ExplorerTestCoverageBars } from './testCoverageBars.js';
1917

2018
/** Workbench contribution that triggers updates in the TestingProgressUi service */
2119
export class TestingProgressTrigger extends Disposable {
20+
public static readonly ID = 'workbench.contrib.testing.progressTrigger';
21+
2222
constructor(
2323
@ITestResultService resultService: ITestResultService,
2424
@ITestCoverageService testCoverageService: ITestCoverageService,
@@ -83,57 +83,3 @@ export class TestingProgressTrigger extends Disposable {
8383
this.viewsService.openView(Testing.ResultsViewId, false);
8484
}
8585
}
86-
87-
export type CountSummary = ReturnType<typeof collectTestStateCounts>;
88-
89-
export const collectTestStateCounts = (isRunning: boolean, results: ReadonlyArray<ITestResult>) => {
90-
let passed = 0;
91-
let failed = 0;
92-
let skipped = 0;
93-
let running = 0;
94-
let queued = 0;
95-
96-
for (const result of results) {
97-
const count = result.counts;
98-
failed += count[TestResultState.Errored] + count[TestResultState.Failed];
99-
passed += count[TestResultState.Passed];
100-
skipped += count[TestResultState.Skipped];
101-
running += count[TestResultState.Running];
102-
queued += count[TestResultState.Queued];
103-
}
104-
105-
return {
106-
isRunning,
107-
passed,
108-
failed,
109-
runSoFar: passed + failed,
110-
totalWillBeRun: passed + failed + queued + running,
111-
skipped,
112-
};
113-
};
114-
115-
export const getTestProgressText = ({ isRunning, passed, runSoFar, totalWillBeRun, skipped, failed }: CountSummary) => {
116-
let percent = passed / runSoFar * 100;
117-
if (failed > 0) {
118-
// fix: prevent from rounding to 100 if there's any failed test
119-
percent = Math.min(percent, 99.9);
120-
} else if (runSoFar === 0) {
121-
percent = 0;
122-
}
123-
124-
if (isRunning) {
125-
if (runSoFar === 0) {
126-
return localize('testProgress.runningInitial', 'Running tests...');
127-
} else if (skipped === 0) {
128-
return localize('testProgress.running', 'Running tests, {0}/{1} passed ({2}%)', passed, totalWillBeRun, percent.toPrecision(3));
129-
} else {
130-
return localize('testProgressWithSkip.running', 'Running tests, {0}/{1} tests passed ({2}%, {3} skipped)', passed, totalWillBeRun, percent.toPrecision(3), skipped);
131-
}
132-
} else {
133-
if (skipped === 0) {
134-
return localize('testProgress.completed', '{0}/{1} tests passed ({2}%)', passed, runSoFar, percent.toPrecision(3));
135-
} else {
136-
return localize('testProgressWithSkip.completed', '{0}/{1} tests passed ({2}%, {3} skipped)', passed, runSoFar, percent.toPrecision(3), skipped);
137-
}
138-
}
139-
};

src/vs/workbench/contrib/testing/common/testService.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export const expandAndGetTestById = async (collection: IMainThreadTestCollection
153153
/**
154154
* Waits for the test to no longer be in the "busy" state.
155155
*/
156-
const waitForTestToBeIdle = (testService: ITestService, test: IncrementalTestCollectionItem) => {
156+
export const waitForTestToBeIdle = (testService: ITestService, test: IncrementalTestCollectionItem) => {
157157
if (!test.item.busy) {
158158
return;
159159
}
@@ -318,6 +318,8 @@ export interface AmbiguousRunTestsRequest {
318318
exclude?: InternalTestItem[];
319319
/** Whether this was triggered from an auto run. */
320320
continuous?: boolean;
321+
/** Whether this was trigged by a user action in UI. Default=true */
322+
preserveFocus?: boolean;
321323
}
322324

323325
export interface ITestFollowup {

src/vs/workbench/contrib/testing/common/testServiceImpl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ export class TestService extends Disposable implements ITestService {
179179
group: req.group,
180180
exclude: req.exclude?.map(t => t.item.extId),
181181
continuous: req.continuous,
182+
preserveFocus: req.preserveFocus,
182183
};
183184

184185
// If no tests are covered by the defaults, just use whatever the defaults

0 commit comments

Comments
 (0)