@@ -7,28 +7,11 @@ import { TestProvider } from '../../types';
77import { traceInfo } from '../../../logging' ;
88import { sendTelemetryEvent } from '../../../telemetry' ;
99import { EventName } from '../../../telemetry/constants' ;
10- import { StopWatch } from '../../../common/utils/stopWatch' ;
1110import { TestItemIndex } from './testItemIndex' ;
1211import { TestDiscoveryHandler } from './testDiscoveryHandler' ;
1312import { TestExecutionHandler } from './testExecutionHandler' ;
1413import { TestCoverageHandler } from './testCoverageHandler' ;
15- import { DiscoveredTestNode , DiscoveredTestItem } from './types' ;
16-
17- /**
18- * Trigger source label for the current discovery cycle.
19- */
20- export type DiscoveryTriggerKind = 'auto' | 'ui' | 'commandpalette' | 'watching' | 'interpreter' ;
21-
22- /**
23- * Per-cycle context the controller passes to the resolver so DISCOVERY_DONE can
24- * include trigger source, mode, and wall-clock duration without having to plumb
25- * these through every adapter call.
26- */
27- export interface DiscoveryCycleContext {
28- mode : 'project' | 'legacy' ;
29- trigger ?: DiscoveryTriggerKind ;
30- stopWatch : StopWatch ;
31- }
14+ import { DiscoveryTelemetryState } from './discoveryTelemetry' ;
3215
3316export class PythonResultResolver implements ITestResultResolver {
3417 testController : TestController ;
@@ -44,6 +27,8 @@ export class PythonResultResolver implements ITestResultResolver {
4427
4528 public detailedCoverageMap = new Map < string , FileCoverageDetail [ ] > ( ) ;
4629
30+ public readonly discoveryTelemetry : DiscoveryTelemetryState ;
31+
4732 /**
4833 * Optional project ID for scoping test IDs.
4934 * When set, all test IDs are prefixed with `{projectId}@@vsc @@` for project-based testing.
@@ -57,12 +42,6 @@ export class PythonResultResolver implements ITestResultResolver {
5742 */
5843 private projectName ?: string ;
5944
60- /**
61- * Per-cycle telemetry context set by the controller before invoking discovery.
62- * Consumed (and cleared) by resolveDiscovery to emit UNITTEST_DISCOVERY_DONE.
63- */
64- private discoveryCycle ?: DiscoveryCycleContext ;
65-
6645 constructor (
6746 testController : TestController ,
6847 testProvider : TestProvider ,
@@ -76,6 +55,7 @@ export class PythonResultResolver implements ITestResultResolver {
7655 this . projectName = projectName ;
7756 // Initialize a new TestItemIndex which will be used to track test items in this workspace/project
7857 this . testItemIndex = new TestItemIndex ( ) ;
58+ this . discoveryTelemetry = new DiscoveryTelemetryState ( projectId ? 'project' : 'legacy' ) ;
7959 }
8060
8161 // Expose for backward compatibility (WorkspaceTestAdapter accesses these)
@@ -99,41 +79,8 @@ export class PythonResultResolver implements ITestResultResolver {
9979 return this . projectId ;
10080 }
10181
102- /**
103- * Set per-discovery-cycle telemetry context. Called by the controller right
104- * before invoking the discovery adapter so resolveDiscovery / failure paths
105- * can include trigger, mode, and duration in UNITTEST_DISCOVERY_DONE.
106- */
107- public beginDiscoveryCycle ( ctx : Omit < DiscoveryCycleContext , 'stopWatch' > ) : void {
108- this . discoveryCycle = { ...ctx , stopWatch : new StopWatch ( ) } ;
109- }
110-
111- /**
112- * Returns and clears the current discovery cycle context, if any.
113- */
114- private takeDiscoveryCycle ( ) : DiscoveryCycleContext | undefined {
115- const cycle = this . discoveryCycle ;
116- this . discoveryCycle = undefined ;
117- return cycle ;
118- }
119-
120- /**
121- * Returns the current discovery cycle context without clearing it.
122- * Used by error paths that still want to clear via takeDiscoveryCycle.
123- */
124- public peekDiscoveryCycle ( ) : DiscoveryCycleContext | undefined {
125- return this . discoveryCycle ;
126- }
127-
128- /**
129- * Clears the current discovery cycle context.
130- */
131- public clearDiscoveryCycle ( ) : void {
132- this . discoveryCycle = undefined ;
133- }
134-
13582 public resolveDiscovery ( payload : DiscoveredTestPayload , token ?: CancellationToken ) : void {
136- PythonResultResolver . discoveryHandler . processDiscovery (
83+ const testCount = PythonResultResolver . discoveryHandler . processDiscovery (
13784 payload ,
13885 this . testController ,
13986 this . testItemIndex ,
@@ -143,15 +90,15 @@ export class PythonResultResolver implements ITestResultResolver {
14390 this . projectId ,
14491 this . projectName ,
14592 ) ;
146- const cycle = this . takeDiscoveryCycle ( ) ;
147- const mode = cycle ?. mode ?? ( this . projectId ? 'project' : 'legacy' ) ;
93+ const cycle = this . discoveryTelemetry . complete ( ) ;
94+ const mode = cycle ?. mode ?? this . discoveryTelemetry . defaultMode ;
14895 sendTelemetryEvent ( EventName . UNITTEST_DISCOVERY_DONE , undefined , {
14996 tool : this . testProvider ,
15097 failed : false ,
15198 mode,
15299 trigger : cycle ?. trigger ,
153100 totalDurationMs : cycle ?. stopWatch . elapsedTime ,
154- testCount : payload ?. tests ? countDiscoveredTests ( payload . tests ) : 0 ,
101+ testCount,
155102 } ) ;
156103 }
157104
@@ -198,19 +145,3 @@ export class PythonResultResolver implements ITestResultResolver {
198145 this . testItemIndex . cleanupStaleReferences ( this . testController ) ;
199146 }
200147}
201-
202- /**
203- * Recursively counts leaf test items in a discovered test tree.
204- * Used to populate UNITTEST_DISCOVERY_DONE.testCount.
205- */
206- function countDiscoveredTests ( node : DiscoveredTestNode | DiscoveredTestItem ) : number {
207- if ( ( node as DiscoveredTestNode ) . children === undefined ) {
208- // No children -> leaf (DiscoveredTestItem).
209- return 1 ;
210- }
211- let total = 0 ;
212- for ( const child of ( node as DiscoveredTestNode ) . children ) {
213- total += countDiscoveredTests ( child ) ;
214- }
215- return total ;
216- }
0 commit comments