Skip to content

Commit f40ba85

Browse files
authored
Send telemetry when auto-selection is triggered (microsoft#17008)
* News file * Update news file * Reuse existing event * Add tests
1 parent 3f9a046 commit f40ba85

4 files changed

Lines changed: 127 additions & 21 deletions

File tree

news/3 Code Health/16764.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add telemetry for when an interpreter gets auto-selected.

src/client/interpreter/autoSelection/index.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { IPersistentState, IPersistentStateFactory, Resource } from '../../commo
1212
import { createDeferred, Deferred } from '../../common/utils/async';
1313
import { compareSemVerLikeVersions } from '../../pythonEnvironments/base/info/pythonVersion';
1414
import { PythonEnvironment } from '../../pythonEnvironments/info';
15-
import { captureTelemetry, sendTelemetryEvent } from '../../telemetry';
15+
import { sendTelemetryEvent } from '../../telemetry';
1616
import { EventName } from '../../telemetry/constants';
1717
import { EnvTypeHeuristic, getEnvTypeHeuristic } from '../configuration/environmentTypeComparer';
1818
import { IInterpreterComparer } from '../configuration/types';
@@ -50,14 +50,14 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
5050
}
5151

5252
/**
53-
* If there's a cached auto-selected interpreter -> return it.
54-
* If not, check if we are in the env sorting experiment, and use the appropriate auto-selection logic.
53+
* Auto-select a Python environment from the list returned by environment discovery.
54+
* If there's a cached auto-selected environment -> return it.
5555
*/
56-
@captureTelemetry(EventName.PYTHON_INTERPRETER_AUTO_SELECTION, {}, true)
5756
public async autoSelectInterpreter(resource: Resource): Promise<void> {
5857
const key = this.getWorkspacePathKey(resource);
58+
const useCachedInterpreter = this.autoSelectedWorkspacePromises.has(key);
5959

60-
if (!this.autoSelectedWorkspacePromises.has(key)) {
60+
if (!useCachedInterpreter) {
6161
const deferred = createDeferred<void>();
6262
this.autoSelectedWorkspacePromises.set(key, deferred);
6363

@@ -68,6 +68,10 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
6868
deferred.resolve();
6969
}
7070

71+
sendTelemetryEvent(EventName.PYTHON_INTERPRETER_AUTO_SELECTION, undefined, {
72+
useCachedInterpreter,
73+
});
74+
7175
return this.autoSelectedWorkspacePromises.get(key)!.promise;
7276
}
7377

@@ -103,7 +107,6 @@ export class InterpreterAutoSelectionService implements IInterpreterAutoSelectio
103107
protected async clearWorkspaceStoreIfInvalid(resource: Resource): Promise<void> {
104108
const stateStore = this.getWorkspaceState(resource);
105109
if (stateStore && stateStore.value && !(await this.fs.fileExists(stateStore.value.path))) {
106-
sendTelemetryEvent(EventName.PYTHON_INTERPRETER_AUTO_SELECTION, {}, { interpreterMissing: true });
107110
await stateStore.updateValue(undefined);
108111
}
109112
}

src/client/telemetry/index.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,25 +1049,16 @@ export interface IEventNamePropertyMapping {
10491049
*/
10501050
interpreterType: EnvironmentType;
10511051
};
1052+
/**
1053+
* Telemetry event sent when auto-selection is called.
1054+
*/
10521055
[EventName.PYTHON_INTERPRETER_AUTO_SELECTION]: {
10531056
/**
1054-
* If cached interpreter no longer exists or is invalid
1055-
*
1056-
* @type {boolean}
1057-
*/
1058-
interpreterMissing?: boolean;
1059-
/**
1060-
* Carries `true` if next rule is identified for autoselecting interpreter
1061-
*
1062-
* @type {boolean}
1063-
*/
1064-
identified?: boolean;
1065-
/**
1066-
* Carries `true` if cached interpreter is updated to use the current interpreter, `false` otherwise
1057+
* If auto-selection has been run earlier in this session, and this call returned a cached value.
10671058
*
10681059
* @type {boolean}
10691060
*/
1070-
updated?: boolean;
1061+
useCachedInterpreter?: boolean;
10711062
};
10721063
/**
10731064
* Sends information regarding discovered python environments (virtualenv, conda, pipenv etc.)

src/test/interpreters/autoSelection/index.unit.test.ts

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { expect } from 'chai';
77
import * as path from 'path';
88
import { SemVer } from 'semver';
9+
import * as sinon from 'sinon';
910
import { anyString, anything, instance, mock, verify, when } from 'ts-mockito';
1011
import { Uri } from 'vscode';
1112
import { IWorkspaceService } from '../../../client/common/application/types';
@@ -28,6 +29,8 @@ import {
2829
import { InterpreterHelper } from '../../../client/interpreter/helpers';
2930
import { InterpreterService } from '../../../client/interpreter/interpreterService';
3031
import { EnvironmentType, PythonEnvironment } from '../../../client/pythonEnvironments/info';
32+
import * as Telemetry from '../../../client/telemetry';
33+
import { EventName } from '../../../client/telemetry/constants';
3134

3235
/* eslint-disable @typescript-eslint/no-explicit-any */
3336

@@ -42,7 +45,9 @@ suite('Interpreters - Auto Selection', () => {
4245
let helper: IInterpreterHelper;
4346
let proxy: IInterpreterAutoSelectionProxyService;
4447
let interpreterService: IInterpreterService;
48+
let sendTelemetryEventStub: sinon.SinonStub;
4549
let options: GetInterpreterOptions[] = [];
50+
let telemetryEvents: { eventName: string; properties: Record<string, unknown> }[] = [];
4651
class InterpreterAutoSelectionServiceTest extends InterpreterAutoSelectionService {
4752
public initializeStore(resource: Resource): Promise<void> {
4853
return super.initializeStore(resource);
@@ -93,17 +98,27 @@ suite('Interpreters - Auto Selection', () => {
9398
} as PythonEnvironment,
9499
]);
95100
});
101+
102+
sendTelemetryEventStub = sinon
103+
.stub(Telemetry, 'sendTelemetryEvent')
104+
.callsFake((eventName: string, _, properties: Record<string, unknown>) => {
105+
const telemetry = { eventName, properties };
106+
telemetryEvents.push(telemetry);
107+
});
96108
});
97109

98110
teardown(() => {
111+
sinon.restore();
112+
Telemetry._resetSharedProperties();
99113
options = [];
114+
telemetryEvents = [];
100115
});
101116

102117
test('Instance is registered in proxy', () => {
103118
verify(proxy.registerInstance!(autoSelectionService)).once();
104119
});
105120

106-
suite('When using locator-based auto-selection', () => {
121+
suite('Test locator-based auto-selection method', () => {
107122
let workspacePath: string;
108123
let resource: Uri;
109124
let eventFired: boolean;
@@ -284,6 +299,102 @@ suite('Interpreters - Auto Selection', () => {
284299
verify(interpreterService.getInterpreters(resource, anything())).once();
285300
expect(options).to.deep.equal([{ ignoreCache: false }], 'getInterpreters options are different');
286301
});
302+
303+
test('Telemetry event is sent with useCachedInterpreter set to false if auto-selection has not been run before', async () => {
304+
const interpreterComparer = new EnvironmentTypeComparer(instance(helper));
305+
306+
when(interpreterService.getInterpreters(resource, anything())).thenCall(() =>
307+
Promise.resolve([
308+
{
309+
envType: EnvironmentType.Conda,
310+
envPath: path.join('some', 'conda', 'env'),
311+
version: { major: 3, minor: 7, patch: 2 },
312+
} as PythonEnvironment,
313+
{
314+
envType: EnvironmentType.Pipenv,
315+
envPath: path.join('some', 'pipenv', 'env'),
316+
version: { major: 3, minor: 10, patch: 0 },
317+
} as PythonEnvironment,
318+
]),
319+
);
320+
321+
autoSelectionService = new InterpreterAutoSelectionServiceTest(
322+
instance(workspaceService),
323+
instance(stateFactory),
324+
instance(fs),
325+
instance(interpreterService),
326+
interpreterComparer,
327+
instance(proxy),
328+
instance(helper),
329+
);
330+
331+
autoSelectionService.initializeStore = () => Promise.resolve();
332+
333+
await autoSelectionService.autoSelectInterpreter(resource);
334+
335+
verify(interpreterService.getInterpreters(resource, anything())).once();
336+
sinon.assert.calledOnce(sendTelemetryEventStub);
337+
expect(telemetryEvents).to.deep.equal(
338+
[
339+
{
340+
eventName: EventName.PYTHON_INTERPRETER_AUTO_SELECTION,
341+
properties: { useCachedInterpreter: false },
342+
},
343+
],
344+
'Telemetry event properties are different',
345+
);
346+
});
347+
348+
test('Telemetry event is sent with useCachedInterpreter set to true if auto-selection has been run before', async () => {
349+
const interpreterComparer = new EnvironmentTypeComparer(instance(helper));
350+
351+
when(interpreterService.getInterpreters(resource, anything())).thenCall(() =>
352+
Promise.resolve([
353+
{
354+
envType: EnvironmentType.Conda,
355+
envPath: path.join('some', 'conda', 'env'),
356+
version: { major: 3, minor: 7, patch: 2 },
357+
} as PythonEnvironment,
358+
{
359+
envType: EnvironmentType.Pipenv,
360+
envPath: path.join('some', 'pipenv', 'env'),
361+
version: { major: 3, minor: 10, patch: 0 },
362+
} as PythonEnvironment,
363+
]),
364+
);
365+
366+
autoSelectionService = new InterpreterAutoSelectionServiceTest(
367+
instance(workspaceService),
368+
instance(stateFactory),
369+
instance(fs),
370+
instance(interpreterService),
371+
interpreterComparer,
372+
instance(proxy),
373+
instance(helper),
374+
);
375+
376+
autoSelectionService.initializeStore = () => Promise.resolve();
377+
378+
await autoSelectionService.autoSelectInterpreter(resource);
379+
380+
await autoSelectionService.autoSelectInterpreter(resource);
381+
382+
verify(interpreterService.getInterpreters(resource, anything())).once();
383+
sinon.assert.calledTwice(sendTelemetryEventStub);
384+
expect(telemetryEvents).to.deep.equal(
385+
[
386+
{
387+
eventName: EventName.PYTHON_INTERPRETER_AUTO_SELECTION,
388+
properties: { useCachedInterpreter: false },
389+
},
390+
{
391+
eventName: EventName.PYTHON_INTERPRETER_AUTO_SELECTION,
392+
properties: { useCachedInterpreter: true },
393+
},
394+
],
395+
'Telemetry event properties are different',
396+
);
397+
});
287398
});
288399

289400
test('Initialize the store', async () => {

0 commit comments

Comments
 (0)