Skip to content

Commit 202c876

Browse files
Copilotkarthiknadig
andcommitted
Implement telemetry tracking for CLI test usage
Co-authored-by: karthiknadig <3840081+karthiknadig@users.noreply.github.com>
1 parent 2244473 commit 202c876

File tree

4 files changed

+176
-2
lines changed

4 files changed

+176
-2
lines changed

src/client/telemetry/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export enum EventName {
4949
UNITTEST_RUN = 'UNITTEST.RUN',
5050
UNITTEST_RUN_ALL_FAILED = 'UNITTEST.RUN_ALL_FAILED',
5151
UNITTEST_DISABLED = 'UNITTEST.DISABLED',
52+
UNITTEST_RUN_CLI = 'UNITTEST.RUN.CLI',
5253

5354
PYTHON_EXPERIMENTS_INIT_PERFORMANCE = 'PYTHON_EXPERIMENTS_INIT_PERFORMANCE',
5455
PYTHON_EXPERIMENTS_LSP_NOTEBOOKS = 'PYTHON_EXPERIMENTS_LSP_NOTEBOOKS',

src/client/telemetry/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,13 @@ export interface IEventNamePropertyMapping {
21742174
"unittest.disabled" : { "owner": "eleanorjboyd" }
21752175
*/
21762176
[EventName.UNITTEST_DISABLED]: never | undefined;
2177+
/**
2178+
* Telemetry event sent when a user runs tests via the CLI in terminal.
2179+
*/
2180+
/* __GDPR__
2181+
"unittest.run.cli" : { "owner": "eleanorjboyd" }
2182+
*/
2183+
[EventName.UNITTEST_RUN_CLI]: never | undefined;
21772184
/*
21782185
Telemetry event sent to provide information on whether we have successfully identify the type of shell used.
21792186
This information is useful in determining how well we identify shells on users machines.

src/client/terminals/codeExecution/terminalReplWatcher.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,24 @@ import { onDidStartTerminalShellExecution } from '../../common/vscodeApis/window
33
import { sendTelemetryEvent } from '../../telemetry';
44
import { EventName } from '../../telemetry/constants';
55

6-
function checkREPLCommand(command: string): undefined | 'manualTerminal' | `runningScript` {
6+
function checkREPLCommand(command: string): undefined | 'manualTerminal' | `runningScript` | 'runningTest' {
77
const lower = command.toLowerCase().trimStart();
8+
9+
// Check for test commands
10+
if (
11+
lower.includes('pytest') ||
12+
(lower.startsWith('python') && lower.includes(' -m pytest')) ||
13+
(lower.startsWith('py ') && lower.includes(' -m pytest')) ||
14+
(lower.startsWith('python') && lower.includes(' -m unittest')) ||
15+
(lower.startsWith('py ') && lower.includes(' -m unittest')) ||
16+
(lower.startsWith('python') && lower.includes(' -m nose')) ||
17+
(lower.startsWith('py ') && lower.includes(' -m nose')) ||
18+
lower.includes('py.test')
19+
) {
20+
return 'runningTest';
21+
}
22+
23+
// Regular Python commands
824
if (lower.startsWith('python') || lower.startsWith('py ')) {
925
const parts = lower.split(' ');
1026
if (parts.length === 1) {
@@ -20,7 +36,12 @@ export function registerTriggerForTerminalREPL(disposables: Disposable[]): void
2036
onDidStartTerminalShellExecution(async (e: TerminalShellExecutionStartEvent) => {
2137
const replType = checkREPLCommand(e.execution.commandLine.value);
2238
if (e.execution.commandLine.isTrusted && replType) {
23-
sendTelemetryEvent(EventName.REPL, undefined, { replType });
39+
// Send test-specific telemetry if it's a test command
40+
if (replType === 'runningTest') {
41+
sendTelemetryEvent(EventName.UNITTEST_RUN_CLI);
42+
} else {
43+
sendTelemetryEvent(EventName.REPL, undefined, { replType });
44+
}
2445
}
2546
}),
2647
);
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
import { expect } from 'chai';
7+
import * as sinon from 'sinon';
8+
import * as windowApis from '../../../client/common/vscodeApis/windowApis';
9+
import * as telemetryModule from '../../../client/telemetry';
10+
import { EventName } from '../../../client/telemetry/constants';
11+
import { registerTriggerForTerminalREPL } from '../../../client/terminals/codeExecution/terminalReplWatcher';
12+
13+
suite('Terminal REPL Watcher', () => {
14+
let windowApisStub: sinon.SinonStub;
15+
let telemetryStub: sinon.SinonStub;
16+
17+
setup(() => {
18+
windowApisStub = sinon.stub(windowApis, 'onDidStartTerminalShellExecution').returns({
19+
dispose: () => {
20+
// Do nothing
21+
}
22+
});
23+
telemetryStub = sinon.stub(telemetryModule, 'sendTelemetryEvent');
24+
});
25+
26+
teardown(() => {
27+
sinon.restore();
28+
});
29+
30+
test('Should send REPL telemetry when Python is invoked', () => {
31+
windowApisStub.callsFake((callback) => {
32+
callback({
33+
execution: {
34+
commandLine: {
35+
value: 'python script.py',
36+
isTrusted: true
37+
}
38+
}
39+
});
40+
return {
41+
dispose: () => {
42+
// Do nothing
43+
}
44+
};
45+
});
46+
47+
registerTriggerForTerminalREPL([]);
48+
49+
expect(telemetryStub.calledOnce).to.be.true;
50+
expect(telemetryStub.args[0][0]).to.equal(EventName.REPL);
51+
expect(telemetryStub.args[0][2]).to.deep.equal({ replType: 'runningScript' });
52+
});
53+
54+
test('Should send unittest CLI telemetry when pytest is invoked', () => {
55+
windowApisStub.callsFake((callback) => {
56+
callback({
57+
execution: {
58+
commandLine: {
59+
value: 'python -m pytest',
60+
isTrusted: true
61+
}
62+
}
63+
});
64+
return {
65+
dispose: () => {
66+
// Do nothing
67+
}
68+
};
69+
});
70+
71+
registerTriggerForTerminalREPL([]);
72+
73+
expect(telemetryStub.calledOnce).to.be.true;
74+
expect(telemetryStub.args[0][0]).to.equal(EventName.UNITTEST_RUN_CLI);
75+
});
76+
77+
test('Should send unittest CLI telemetry when unittest is invoked', () => {
78+
windowApisStub.callsFake((callback) => {
79+
callback({
80+
execution: {
81+
commandLine: {
82+
value: 'python -m unittest discover',
83+
isTrusted: true
84+
}
85+
}
86+
});
87+
return {
88+
dispose: () => {
89+
// Do nothing
90+
}
91+
};
92+
});
93+
94+
registerTriggerForTerminalREPL([]);
95+
96+
expect(telemetryStub.calledOnce).to.be.true;
97+
expect(telemetryStub.args[0][0]).to.equal(EventName.UNITTEST_RUN_CLI);
98+
});
99+
100+
test('Should send unittest CLI telemetry when nose is invoked', () => {
101+
windowApisStub.callsFake((callback) => {
102+
callback({
103+
execution: {
104+
commandLine: {
105+
value: 'python -m nose',
106+
isTrusted: true
107+
}
108+
}
109+
});
110+
return {
111+
dispose: () => {
112+
// Do nothing
113+
}
114+
};
115+
});
116+
117+
registerTriggerForTerminalREPL([]);
118+
119+
expect(telemetryStub.calledOnce).to.be.true;
120+
expect(telemetryStub.args[0][0]).to.equal(EventName.UNITTEST_RUN_CLI);
121+
});
122+
123+
test('Should send unittest CLI telemetry when py.test is invoked', () => {
124+
windowApisStub.callsFake((callback) => {
125+
callback({
126+
execution: {
127+
commandLine: {
128+
value: 'py.test',
129+
isTrusted: true
130+
}
131+
}
132+
});
133+
return {
134+
dispose: () => {
135+
// Do nothing
136+
}
137+
};
138+
});
139+
140+
registerTriggerForTerminalREPL([]);
141+
142+
expect(telemetryStub.calledOnce).to.be.true;
143+
expect(telemetryStub.args[0][0]).to.equal(EventName.UNITTEST_RUN_CLI);
144+
});
145+
});

0 commit comments

Comments
 (0)