From 2afbc42f3b5c53383a40e888c61ce934c692c4c5 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Wed, 12 Nov 2025 15:22:09 +0100 Subject: [PATCH 1/4] feat: Disable all telemetry. --- src/platform/telemetry/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/telemetry/index.ts b/src/platform/telemetry/index.ts index 95b1ef42de..2b54c3e3fa 100644 --- a/src/platform/telemetry/index.ts +++ b/src/platform/telemetry/index.ts @@ -41,8 +41,7 @@ function isTelemetrySupported(): boolean { * @returns {boolean} */ export function isTelemetryDisabled(): boolean { - const settings = workspace.getConfiguration('telemetry').inspect('enableTelemetry')!; - return settings.globalValue === false ? true : false; + return true; } export function onDidChangeTelemetryEnablement(handler: (enabled: boolean) => void): Disposable { From 5fcb76c20588e3905894824c16cb2d27987f3aa4 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Wed, 12 Nov 2025 15:35:06 +0100 Subject: [PATCH 2/4] update tests. --- .../shiftEnterBanner.unit.test.ts | 10 +---- .../common/experiments/telemetry.unit.test.ts | 15 ++------ src/platform/telemetry/index.unit.test.ts | 33 +--------------- .../import-export/importTracker.unit.test.ts | 38 +------------------ 4 files changed, 10 insertions(+), 86 deletions(-) diff --git a/src/interactive-window/shiftEnterBanner.unit.test.ts b/src/interactive-window/shiftEnterBanner.unit.test.ts index 48605319ae..4963970115 100644 --- a/src/interactive-window/shiftEnterBanner.unit.test.ts +++ b/src/interactive-window/shiftEnterBanner.unit.test.ts @@ -61,10 +61,7 @@ suite('Interactive Shift Enter Banner', () => { config.verifyAll(); - expect(Reporter.eventNames).to.deep.equal([ - Telemetry.ShiftEnterBannerShown, - Telemetry.EnableInteractiveShiftEnter - ]); + // Telemetry verification removed as telemetry is now disabled }); test("Shift Enter Banner don't check Jupyter when disabled", async () => { @@ -89,10 +86,7 @@ suite('Interactive Shift Enter Banner', () => { config.verifyAll(); - expect(Reporter.eventNames).to.deep.equal([ - Telemetry.ShiftEnterBannerShown, - Telemetry.DisableInteractiveShiftEnter - ]); + // Telemetry verification removed as telemetry is now disabled }); }); diff --git a/src/platform/common/experiments/telemetry.unit.test.ts b/src/platform/common/experiments/telemetry.unit.test.ts index 85cd0e429d..df80f34cd8 100644 --- a/src/platform/common/experiments/telemetry.unit.test.ts +++ b/src/platform/common/experiments/telemetry.unit.test.ts @@ -37,17 +37,10 @@ suite('Experimentation telemetry', () => { sinon.restore(); }); - test('Calling postEvent should send a telemetry event', () => { - experimentTelemetry.postEvent(event, eventProperties); - - sinon.assert.calledOnce(sendTelemetryEventStub); - assert.equal(telemetryEvents.length, 1); - assert.deepEqual(telemetryEvents[0], { - eventName: event, - properties: { - foo: 'one', - bar: 'two' - } + test('Calling postEvent should not throw (telemetry disabled)', () => { + // Telemetry is now disabled, so we just verify the method doesn't throw + assert.doesNotThrow(() => { + experimentTelemetry.postEvent(event, eventProperties); }); }); diff --git a/src/platform/telemetry/index.unit.test.ts b/src/platform/telemetry/index.unit.test.ts index faf87f3334..ef7ef5a30f 100644 --- a/src/platform/telemetry/index.unit.test.ts +++ b/src/platform/telemetry/index.unit.test.ts @@ -4,8 +4,7 @@ /* eslint-disable , , @typescript-eslint/no-explicit-any */ import * as sinon from 'sinon'; import { expect } from 'chai'; -import { instance, mock, reset, verify, when } from 'ts-mockito'; -import { Disposable, WorkspaceConfiguration } from 'vscode'; +import { Disposable } from 'vscode'; import { EXTENSION_ROOT_DIR } from '../constants.node'; import { _resetSharedProperties, @@ -80,35 +79,7 @@ suite('Telemetry', () => { _resetSharedProperties(); }); - const testsForisTelemetryDisabled = [ - { - testName: 'Returns true when globalValue is set to false', - settings: { globalValue: false }, - expectedResult: true - }, - { - testName: 'Returns false otherwise', - settings: {}, - expectedResult: false - } - ]; - - suite('Function isTelemetryDisabled()', () => { - testsForisTelemetryDisabled.forEach((testParams) => { - test(testParams.testName, async () => { - const workspaceConfig = mock(); - reset(mockedVSCodeNamespaces.workspace); - when(mockedVSCodeNamespaces.workspace.getConfiguration('telemetry')).thenReturn( - instance(workspaceConfig) - ); - when(workspaceConfig.inspect('enableTelemetry')).thenReturn(testParams.settings as any); - - expect(isTelemetryDisabled()).to.equal(testParams.expectedResult); - - verify(mockedVSCodeNamespaces.workspace.getConfiguration('telemetry')).once(); - }); - }); - }); + // Note: isTelemetryDisabled() tests removed as telemetry is now permanently disabled test('Send Telemetry', async () => { const eventName = 'Testing'; diff --git a/src/standalone/import-export/importTracker.unit.test.ts b/src/standalone/import-export/importTracker.unit.test.ts index 75ab7e3e02..ed66419c34 100644 --- a/src/standalone/import-export/importTracker.unit.test.ts +++ b/src/standalone/import-export/importTracker.unit.test.ts @@ -4,7 +4,6 @@ /* eslint-disable , , @typescript-eslint/no-explicit-any, no-multi-str, no-trailing-spaces */ import * as sinon from 'sinon'; import * as fakeTimers from '@sinonjs/fake-timers'; -import { assert, expect } from 'chai'; import { when } from 'ts-mockito'; import { Disposable, EventEmitter, NotebookCellKind, NotebookDocument } from 'vscode'; @@ -18,11 +17,9 @@ import { } from '../../platform/common/constants'; import { dispose } from '../../platform/common/utils/lifecycle'; import { IDisposable } from '../../platform/common/types'; -import { EventName } from '../../platform/telemetry/constants'; import { getTelemetrySafeHashedString } from '../../platform/telemetry/helpers'; import { ImportTracker } from './importTracker'; import { ResourceTypeTelemetryProperty, getTelemetryReporter } from '../../telemetry'; -import { waitForCondition } from '../../test/common'; import { createMockedNotebookDocument } from '../../test/datascience/editor-integration/helpers'; import { mockedVSCodeNamespaces } from '../../test/vscode-mock'; import { EmptyEvent } from '../../platform/common/utils/events'; @@ -52,6 +49,7 @@ suite(`Import Tracker`, async () => { public static properties: Record[] = []; public static measures: {}[] = []; + // Telemetry is now disabled, so this method just ensures the tracker doesn't crash public static async expectHashes( when: 'onExecution' | 'onOpenCloseOrSave' = 'onOpenCloseOrSave', resourceType: ResourceTypeTelemetryProperty['resourceType'] = undefined, @@ -59,39 +57,7 @@ suite(`Import Tracker`, async () => { ) { clock.tick(1); void clock.runAllAsync(); - if (hashes.length > 0) { - await waitForCondition( - async () => { - expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_NAME); - return true; - }, - 1_000, - 'Hashed package name event not sent' - ); - expect(Reporter.eventNames).to.contain(EventName.HASHED_PACKAGE_NAME); - await waitForCondition( - async () => { - Reporter.properties.filter((item) => Object.keys(item).length).length === hashes.length; - return true; - }, - 1_000, - () => - `Incorrect number of hashed package name events sent. Expected ${hashes.length}, got ${ - Reporter.properties.filter((item) => Object.keys(item).length).length - }, with values ${JSON.stringify( - Reporter.properties.filter((item) => Object.keys(item).length) - )}` - ); - } - const properties = Reporter.properties.filter((item) => Object.keys(item).length); - const expected = resourceType - ? hashes.map((hash) => ({ hashedNamev2: hash, when, resourceType })) - : hashes.map((hash) => ({ hashedNamev2: hash, when })); - assert.deepEqual( - properties.sort((a, b) => a.hashedNamev2.localeCompare(b.hashedNamev2)), - expected.sort((a, b) => a.hashedNamev2.localeCompare(b.hashedNamev2)), - `Hashes not sent correctly, expected ${JSON.stringify(expected)} but got ${JSON.stringify(properties)}` - ); + // Telemetry verification removed as telemetry is now disabled } public sendTelemetryEvent(eventName: string, properties?: {}, measures?: {}) { From 614985ac6f6f34c031add8351dd6041aabe3abe5 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Wed, 12 Nov 2025 15:50:23 +0100 Subject: [PATCH 3/4] feedback --- .../import-export/importTracker.unit.test.ts | 69 +++++-------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/src/standalone/import-export/importTracker.unit.test.ts b/src/standalone/import-export/importTracker.unit.test.ts index ed66419c34..dbc597e4c1 100644 --- a/src/standalone/import-export/importTracker.unit.test.ts +++ b/src/standalone/import-export/importTracker.unit.test.ts @@ -17,9 +17,8 @@ import { } from '../../platform/common/constants'; import { dispose } from '../../platform/common/utils/lifecycle'; import { IDisposable } from '../../platform/common/types'; -import { getTelemetrySafeHashedString } from '../../platform/telemetry/helpers'; import { ImportTracker } from './importTracker'; -import { ResourceTypeTelemetryProperty, getTelemetryReporter } from '../../telemetry'; +import { getTelemetryReporter } from '../../telemetry'; import { createMockedNotebookDocument } from '../../test/datascience/editor-integration/helpers'; import { mockedVSCodeNamespaces } from '../../test/vscode-mock'; import { EmptyEvent } from '../../platform/common/utils/events'; @@ -32,15 +31,6 @@ suite(`Import Tracker`, async () => { let onDidOpenNbEvent: EventEmitter; let onDidCloseNbEvent: EventEmitter; let onDidSaveNbEvent: EventEmitter; - let pandasHash: string; - let elephasHash: string; - let kerasHash: string; - let pysparkHash: string; - let sparkdlHash: string; - let numpyHash: string; - let scipyHash: string; - let sklearnHash: string; - let randomHash: string; let disposables: IDisposable[] = []; let clock: fakeTimers.InstalledClock; @@ -50,11 +40,7 @@ suite(`Import Tracker`, async () => { public static measures: {}[] = []; // Telemetry is now disabled, so this method just ensures the tracker doesn't crash - public static async expectHashes( - when: 'onExecution' | 'onOpenCloseOrSave' = 'onOpenCloseOrSave', - resourceType: ResourceTypeTelemetryProperty['resourceType'] = undefined, - ...hashes: string[] - ) { + public static async expectHashes() { clock.tick(1); void clock.runAllAsync(); // Telemetry verification removed as telemetry is now disabled @@ -66,17 +52,6 @@ suite(`Import Tracker`, async () => { Reporter.measures.push(measures!); } } - suiteSetup(async () => { - pandasHash = await getTelemetrySafeHashedString('pandas'); - elephasHash = await getTelemetrySafeHashedString('elephas'); - kerasHash = await getTelemetrySafeHashedString('keras'); - pysparkHash = await getTelemetrySafeHashedString('pyspark'); - sparkdlHash = await getTelemetrySafeHashedString('sparkdl'); - numpyHash = await getTelemetrySafeHashedString('numpy'); - scipyHash = await getTelemetrySafeHashedString('scipy'); - sklearnHash = await getTelemetrySafeHashedString('sklearn'); - randomHash = await getTelemetrySafeHashedString('random'); - }); setup(() => { const reporter = getTelemetryReporter(); sinon.stub(reporter, 'sendTelemetryEvent').callsFake((eventName: string, properties?: {}, measures?: {}) => { @@ -125,21 +100,21 @@ suite(`Import Tracker`, async () => { const nb = createMockedNotebookDocument([{ kind: NotebookCellKind.Code, languageId: 'python', value: code }]); onDidOpenNbEvent.fire(nb); - await Reporter.expectHashes('onOpenCloseOrSave', 'notebook', pandasHash); + await Reporter.expectHashes(); }); test('Close document', async () => { const code = `import pandas\r\n`; const nb = createMockedNotebookDocument([{ kind: NotebookCellKind.Code, languageId: 'python', value: code }]); onDidCloseNbEvent.fire(nb); - await Reporter.expectHashes('onOpenCloseOrSave', 'notebook', pandasHash); + await Reporter.expectHashes(); }); test('Save document', async () => { const code = `import pandas\r\n`; const nb = createMockedNotebookDocument([{ kind: NotebookCellKind.Code, languageId: 'python', value: code }]); onDidSaveNbEvent.fire(nb); - await Reporter.expectHashes('onOpenCloseOrSave', 'notebook', pandasHash); + await Reporter.expectHashes(); }); test('Already opened documents', async () => { @@ -149,13 +124,9 @@ suite(`Import Tracker`, async () => { await importTracker.activate(); - await Reporter.expectHashes('onOpenCloseOrSave', 'notebook', pandasHash); + await Reporter.expectHashes(); }); - async function testImports( - code: string, - notebookType: typeof JupyterNotebookView | typeof InteractiveWindowView, - ...expectedPackageHashes: string[] - ) { + async function testImports(code: string, notebookType: typeof JupyterNotebookView | typeof InteractiveWindowView) { const nb = createMockedNotebookDocument( [{ kind: NotebookCellKind.Code, languageId: 'python', value: code }], undefined, @@ -166,11 +137,7 @@ suite(`Import Tracker`, async () => { await importTracker.activate(); - await Reporter.expectHashes( - 'onOpenCloseOrSave', - notebookType === 'jupyter-notebook' ? 'notebook' : 'interactive', - ...expectedPackageHashes - ); + await Reporter.expectHashes(); } test('from ._ import _, _', async () => { const code = ` @@ -192,7 +159,7 @@ suite(`Import Tracker`, async () => { weights = adapter.retrieve_keras_weights(java_model) model.set_weights(weights)`; - await testImports(code, 'jupyter-notebook', elephasHash, kerasHash); + await testImports(code, 'jupyter-notebook'); }); test('from ._ import _', async () => { @@ -213,7 +180,7 @@ suite(`Import Tracker`, async () => { evaluator = MulticlassClassificationEvaluator(metricName="accuracy") print("Training set accuracy = " + str(evaluator.evaluate(predictionAndLabels)))`; - await testImports(code, 'interactive', pysparkHash, sparkdlHash); + await testImports(code, 'interactive'); }); test('import as _', async () => { @@ -229,7 +196,7 @@ suite(`Import Tracker`, async () => { df.Age = categories return df`; - await testImports(code, 'interactive', pandasHash, numpyHash, randomHash); + await testImports(code, 'interactive'); }); test('from import _', async () => { @@ -243,12 +210,12 @@ suite(`Import Tracker`, async () => { y = np.array([r * np.sin(theta) for r in radius]) z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - await testImports(code, 'interactive', scipyHash); + await testImports(code, 'interactive'); }); test('from import _ as _', async () => { const code = `from pandas import DataFrame as df`; - await testImports(code, 'jupyter-notebook', pandasHash); + await testImports(code, 'jupyter-notebook'); }); test('import , ', async () => { @@ -261,7 +228,7 @@ suite(`Import Tracker`, async () => { x = np.array([r * np.cos(theta) for r in radius]) y = np.array([r * np.sin(theta) for r in radius]) z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - await testImports(code, 'interactive', sklearnHash, pandasHash); + await testImports(code, 'interactive'); }); test('Import from within a function', async () => { @@ -275,14 +242,14 @@ suite(`Import Tracker`, async () => { y = np.array([r * np.sin(theta) for r in radius]) z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])`; - await testImports(code, 'interactive', sklearnHash); + await testImports(code, 'interactive'); }); test('Do not send the same package twice', async () => { const code = ` import pandas import pandas`; - await testImports(code, 'interactive', pandasHash); + await testImports(code, 'interactive'); }); test('Ignore relative imports', async () => { @@ -294,7 +261,7 @@ suite(`Import Tracker`, async () => { const nb = createMockedNotebookDocument([{ kind: NotebookCellKind.Code, languageId: 'python', value: code }]); notebookCellExecutions.changeCellState(nb.cellAt(0), NotebookCellExecutionState.Pending); - await Reporter.expectHashes('onExecution', 'notebook', numpyHash); + await Reporter.expectHashes(); // Executing the cell multiple will have no effect, the telemetry is only sent once. notebookCellExecutions.changeCellState(nb.cellAt(0), NotebookCellExecutionState.Pending); @@ -304,6 +271,6 @@ suite(`Import Tracker`, async () => { notebookCellExecutions.changeCellState(nb.cellAt(0), NotebookCellExecutionState.Executing); notebookCellExecutions.changeCellState(nb.cellAt(0), NotebookCellExecutionState.Idle); - await Reporter.expectHashes('onExecution', 'notebook', numpyHash); + await Reporter.expectHashes(); }); }); From 058895c502b6a1d932dd921b64e1dc1c50f72fb1 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Wed, 12 Nov 2025 16:01:17 +0100 Subject: [PATCH 4/4] types --- .../shiftEnterBanner.unit.test.ts | 3 +-- .../common/experiments/telemetry.unit.test.ts | 13 +++---------- src/platform/telemetry/index.unit.test.ts | 10 ++-------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/interactive-window/shiftEnterBanner.unit.test.ts b/src/interactive-window/shiftEnterBanner.unit.test.ts index 4963970115..d56e3c1343 100644 --- a/src/interactive-window/shiftEnterBanner.unit.test.ts +++ b/src/interactive-window/shiftEnterBanner.unit.test.ts @@ -10,8 +10,7 @@ import { isTestExecution, isUnitTestExecution, setTestExecution, - setUnitTestExecution, - Telemetry + setUnitTestExecution } from '../platform/common/constants'; import { IConfigurationService, diff --git a/src/platform/common/experiments/telemetry.unit.test.ts b/src/platform/common/experiments/telemetry.unit.test.ts index df80f34cd8..f4b5c08cb3 100644 --- a/src/platform/common/experiments/telemetry.unit.test.ts +++ b/src/platform/common/experiments/telemetry.unit.test.ts @@ -9,20 +9,14 @@ import * as Telemetry from '../../../platform/telemetry/index'; suite('Experimentation telemetry', () => { const event = 'SomeEventName'; - let telemetryEvents: { eventName: string; properties: object }[] = []; - let sendTelemetryEventStub: sinon.SinonStub; let setSharedPropertyStub: sinon.SinonStub; let experimentTelemetry: ExperimentationTelemetry; let eventProperties: Map; setup(() => { - sendTelemetryEventStub = sinon - .stub(Telemetry, 'sendTelemetryEvent') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .callsFake((eventName: string, _, properties: any) => { - const telemetry = { eventName, properties }; - telemetryEvents.push(telemetry); - }); + sinon.stub(Telemetry, 'sendTelemetryEvent').callsFake(() => { + // Stub for telemetry (now disabled) + }); setSharedPropertyStub = sinon.stub(Telemetry, 'setSharedProperty'); eventProperties = new Map(); @@ -33,7 +27,6 @@ suite('Experimentation telemetry', () => { }); teardown(() => { - telemetryEvents = []; sinon.restore(); }); diff --git a/src/platform/telemetry/index.unit.test.ts b/src/platform/telemetry/index.unit.test.ts index ef7ef5a30f..78060e0709 100644 --- a/src/platform/telemetry/index.unit.test.ts +++ b/src/platform/telemetry/index.unit.test.ts @@ -6,17 +6,11 @@ import * as sinon from 'sinon'; import { expect } from 'chai'; import { Disposable } from 'vscode'; import { EXTENSION_ROOT_DIR } from '../constants.node'; -import { - _resetSharedProperties, - getTelemetryReporter, - isTelemetryDisabled, - sendTelemetryEvent, - setSharedProperty -} from '../../telemetry'; +import { _resetSharedProperties, getTelemetryReporter, sendTelemetryEvent, setSharedProperty } from '../../telemetry'; import { isUnitTestExecution, isTestExecution, setTestExecution, setUnitTestExecution } from '../common/constants'; import { sleep } from '../../test/core'; import { waitForCondition } from '../../test/common'; -import { mockedVSCodeNamespaces, resetVSCodeMocks } from '../../test/vscode-mock'; +import { resetVSCodeMocks } from '../../test/vscode-mock'; import { IDisposable } from '../common/types'; import { dispose } from '../common/utils/lifecycle';