diff --git a/build/ci/templates/steps/build.yml b/build/ci/templates/steps/build.yml index fc953c7bfb60..e1cf783c93b3 100644 --- a/build/ci/templates/steps/build.yml +++ b/build/ci/templates/steps/build.yml @@ -22,6 +22,7 @@ steps: python -m pip install -U pip python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade -r requirements.txt python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python/old_ptvsd --no-cache-dir --implementation py --no-deps --upgrade 'ptvsd==4.3.2' + python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python/new_ptvsd/no_wheels --no-cache-dir --implementation py --no-deps --upgrade 'ptvsd==5.0.0a7' failOnStderr: true displayName: "pip install requirements" diff --git a/build/ci/templates/test_phases.yml b/build/ci/templates/test_phases.yml index 546167a2e463..66b001c32656 100644 --- a/build/ci/templates/test_phases.yml +++ b/build/ci/templates/test_phases.yml @@ -96,6 +96,7 @@ steps: python -m pip install --upgrade -r build/test-requirements.txt python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python --no-cache-dir --implementation py --no-deps --upgrade -r requirements.txt python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python/old_ptvsd --no-cache-dir --implementation py --no-deps --upgrade 'ptvsd==4.3.2' + python -m pip --disable-pip-version-check install -t ./pythonFiles/lib/python/new_ptvsd/no_wheels --no-cache-dir --implementation py --no-deps --upgrade 'ptvsd==5.0.0a7' displayName: 'pip install system test requirements' condition: and(succeeded(), eq(variables['NeedsPythonTestReqs'], 'true')) diff --git a/pythonFiles/install_ptvsd.py b/pythonFiles/install_ptvsd.py index bfa7332a1956..8384910d6c86 100644 --- a/pythonFiles/install_ptvsd.py +++ b/pythonFiles/install_ptvsd.py @@ -6,7 +6,7 @@ EXTENSION_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -DEBUGGER_DEST = os.path.join(EXTENSION_ROOT, "pythonFiles", "lib", "python") +DEBUGGER_DEST = os.path.join(EXTENSION_ROOT, "pythonFiles", "lib", "python", "new_ptvsd", "wheels") DEBUGGER_PACKAGE = "ptvsd" DEBUGGER_VERSION = "5.0.0a7" DEBUGGER_PYTHON_VERSIONS = ("cp37",) diff --git a/src/client/datascience/jupyter/jupyterDebugger.ts b/src/client/datascience/jupyter/jupyterDebugger.ts index 40eb56172b06..706d26755317 100644 --- a/src/client/datascience/jupyter/jupyterDebugger.ts +++ b/src/client/datascience/jupyter/jupyterDebugger.ts @@ -17,16 +17,7 @@ import { EXTENSION_ROOT_DIR } from '../../constants'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { concatMultilineStringOutput } from '../common'; import { Identifiers, Telemetry } from '../constants'; -import { - CellState, - ICell, - ICellHashListener, - IConnection, - IFileHashes, - IJupyterDebugger, - INotebook, - ISourceMapRequest -} from '../types'; +import { CellState, ICell, ICellHashListener, IConnection, IFileHashes, IJupyterDebugger, INotebook, ISourceMapRequest } from '../types'; import { JupyterDebuggerNotInstalledError } from './jupyterDebuggerNotInstalledError'; import { JupyterDebuggerRemoteNotSupported } from './jupyterDebuggerRemoteNotSupported'; import { ILiveShareHasRole } from './liveshare/types'; @@ -45,8 +36,7 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { @inject(IPlatformService) private platform: IPlatformService, @inject(IWorkspaceService) private workspace: IWorkspaceService, @inject(IExperimentsManager) private readonly experimentsManager: IExperimentsManager - ) { - } + ) {} public async startDebugging(notebook: INotebook): Promise { traceInfo('start debugging'); @@ -114,9 +104,11 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { public async hashesUpdated(hashes: IFileHashes[]): Promise { // Make sure that we have an active debugging session at this point if (this.debugService.activeDebugSession) { - await Promise.all(hashes.map((fileHash) => { - return this.debugService.activeDebugSession!.customRequest('setPydevdSourceMap', this.buildSourceMap(fileHash)); - })); + await Promise.all( + hashes.map(fileHash => { + return this.debugService.activeDebugSession!.customRequest('setPydevdSourceMap', this.buildSourceMap(fileHash)); + }) + ); } } @@ -184,18 +176,19 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { */ private async getPtvsdPath(notebook: INotebook): Promise { const oldPtvsd = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'old_ptvsd'); - if (!this.experimentsManager.inExperiment(DebugAdapterDescriptorFactory.experiment) || - !this.experimentsManager.inExperiment(DebugAdapterNewPtvsd.experiment)){ + if (!this.experimentsManager.inExperiment(DebugAdapterDescriptorFactory.experiment) || !this.experimentsManager.inExperiment(DebugAdapterNewPtvsd.experiment)) { return oldPtvsd; } const pythonVersion = await this.getKernelPythonVersion(notebook); - // The new debug adapter is only supported in 3.7 + // The new debug adapter with wheels is only supported in 3.7 // Code can be found here (src/client/debugger/extension/adapter/factory.ts). - if (!pythonVersion || pythonVersion.major < 3 || pythonVersion.minor < 7){ - return oldPtvsd; + if (pythonVersion && pythonVersion.major === 3 && pythonVersion.minor === 7) { + // Return debugger with wheels + return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd', 'wheels'); } - return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python'); + // We are here so this is NOT python 3.7, return debugger without wheels + return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd', 'no_wheels'); } private async calculatePtvsdPathList(notebook: INotebook): Promise { const extraPaths: string[] = []; @@ -311,7 +304,12 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { const minor = parseInt(packageVersionMatch[2], 10); const patch = parseInt(packageVersionMatch[3], 10); return { - major, minor, patch, build: [], prerelease: [], raw: `${major}.${minor}.${patch}` + major, + minor, + patch, + build: [], + prerelease: [], + raw: `${major}.${minor}.${patch}` }; } } @@ -335,7 +333,11 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { @captureTelemetry(Telemetry.PtvsdPromptToInstall) private async promptToInstallPtvsd(notebook: INotebook, oldVersion: Version | undefined): Promise { const promptMessage = oldVersion ? localize.DataScience.jupyterDebuggerInstallPtvsdUpdate() : localize.DataScience.jupyterDebuggerInstallPtvsdNew(); - const result = await this.appShell.showInformationMessage(promptMessage, localize.DataScience.jupyterDebuggerInstallPtvsdYes(), localize.DataScience.jupyterDebuggerInstallPtvsdNo()); + const result = await this.appShell.showInformationMessage( + promptMessage, + localize.DataScience.jupyterDebuggerInstallPtvsdYes(), + localize.DataScience.jupyterDebuggerInstallPtvsdNo() + ); if (result === localize.DataScience.jupyterDebuggerInstallPtvsdYes()) { await this.installPtvsd(notebook); @@ -424,7 +426,7 @@ export class JupyterDebugger implements IJupyterDebugger, ICellHashListener { const data = outputs[0].data; if (data && data.hasOwnProperty('text/plain')) { // tslint:disable-next-line:no-any - return ((data as any)['text/plain']); + return (data as any)['text/plain']; } if (outputs[0].output_type === 'stream') { const stream = outputs[0] as nbformat.IStream; diff --git a/src/client/debugger/extension/adapter/factory.ts b/src/client/debugger/extension/adapter/factory.ts index b591a7194a68..06391f8aa7db 100644 --- a/src/client/debugger/extension/adapter/factory.ts +++ b/src/client/debugger/extension/adapter/factory.ts @@ -12,6 +12,8 @@ import { traceVerbose } from '../../../common/logger'; import { IExperimentsManager } from '../../../common/types'; import { EXTENSION_ROOT_DIR } from '../../../constants'; import { IInterpreterService } from '../../../interpreter/contracts'; +import { sendTelemetryEvent } from '../../../telemetry'; +import { EventName } from '../../../telemetry/constants'; import { RemoteDebugOptions } from '../../debugAdapter/types'; import { AttachRequestArguments, LaunchRequestArguments } from '../../types'; import { IDebugAdapterDescriptorFactory } from '../types'; @@ -24,7 +26,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, @inject(IApplicationShell) private readonly appShell: IApplicationShell, @inject(IExperimentsManager) private readonly experimentsManager: IExperimentsManager - ) { } + ) {} public async createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): Promise { const configuration = session.configuration as (LaunchRequestArguments | AttachRequestArguments); @@ -37,11 +39,17 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac return new DebugAdapterServer(port, configuration.host); } else { const pythonPath = await this.getPythonPath(configuration, session.workspaceFolder); - if (await this.useNewPtvsd(pythonPath)) { - // If logToFile is set in the debug config then pass --log-dir when launching the debug adapter. - const logArgs = configuration.logToFile ? ['--log-dir', EXTENSION_ROOT_DIR] : []; - const ptvsdPathToUse = this.getPtvsdPath(); - return new DebugAdapterExecutable(pythonPath, [path.join(ptvsdPathToUse, 'adapter'), ...logArgs]); + // If logToFile is set in the debug config then pass --log-dir when launching the debug adapter. + const logArgs = configuration.logToFile ? ['--log-dir', EXTENSION_ROOT_DIR] : []; + const ptvsdPathToUse = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd'); + if (pythonPath.length !== 0) { + if (await this.useNewPtvsd(pythonPath)) { + sendTelemetryEvent(EventName.DEBUG_ADAPTER_USING_WHEELS_PATH, undefined, { usingWheels: true }); + return new DebugAdapterExecutable(pythonPath, [path.join(ptvsdPathToUse, 'wheels', 'ptvsd', 'adapter'), ...logArgs]); + } else { + sendTelemetryEvent(EventName.DEBUG_ADAPTER_USING_WHEELS_PATH, undefined, { usingWheels: false }); + return new DebugAdapterExecutable(pythonPath, [path.join(ptvsdPathToUse, 'no_wheels', 'ptvsd', 'adapter'), ...logArgs]); + } } } } else { @@ -73,7 +81,7 @@ export class DebugAdapterDescriptorFactory implements IDebugAdapterDescriptorFac } public getPtvsdPath(): string { - return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'ptvsd'); + return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd', 'no_wheels', 'ptvsd'); } public getRemotePtvsdArgs(remoteDebugOptions: RemoteDebugOptions): string[] { diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index a8dbbce7bb99..89e960ebfe87 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -39,6 +39,7 @@ export enum EventName { WORKSPACE_SYMBOLS_GO_TO = 'WORKSPACE_SYMBOLS.GO_TO', EXECUTION_CODE = 'EXECUTION_CODE', EXECUTION_DJANGO = 'EXECUTION_DJANGO', + DEBUG_ADAPTER_USING_WHEELS_PATH = 'DEBUG_ADAPTER.USING_WHEELS_PATH', DEBUG_SESSION_ERROR = 'DEBUG_SESSION.ERROR', DEBUG_SESSION_START = 'DEBUG_SESSION.START', DEBUG_SESSION_STOP = 'DEBUG_SESSION.STOP', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 2a8409154759..f0b720856da2 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -264,6 +264,17 @@ export interface IEventNamePropertyMapping { */ enabled: boolean; }; + /** + * Telemetry event captured when debug adapter executable is created + */ + [EventName.DEBUG_ADAPTER_USING_WHEELS_PATH]: { + /** + * Carries boolean + * - `true` if path used for the adapter is the debugger with wheels. + * - `false` if path used for the adapter is the source only version of the debugger. + */ + usingWheels: boolean; + }; /** * Telemetry captured before starting debug session. */ diff --git a/src/test/debugger/extension/adapter/factory.unit.test.ts b/src/test/debugger/extension/adapter/factory.unit.test.ts index 717c755c91eb..bc17d1e60a05 100644 --- a/src/test/debugger/extension/adapter/factory.unit.test.ts +++ b/src/test/debugger/extension/adapter/factory.unit.test.ts @@ -45,7 +45,8 @@ suite('Debugging - Adapter Factory', () => { let spiedInstance: ExperimentsManager; const nodeExecutable = { command: 'node', args: [] }; - const ptvsdAdapterPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'ptvsd', 'adapter'); + const ptvsdAdapterPathWithWheels = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd', 'wheels', 'ptvsd', 'adapter'); + const ptvsdAdapterPathWithoutWheels = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'lib', 'python', 'new_ptvsd', 'no_wheels', 'ptvsd', 'adapter'); const pythonPath = path.join('path', 'to', 'python', 'interpreter'); const interpreter = { architecture: Architecture.Unknown, @@ -55,6 +56,15 @@ suite('Debugging - Adapter Factory', () => { type: InterpreterType.Unknown, version: new SemVer('3.7.4-test') }; + const python36Path = path.join('path', 'to', 'active', 'interpreter'); + const interpreterPython36Details = { + architecture: Architecture.Unknown, + path: python36Path, + sysPrefix: '', + sysVersion: '', + type: InterpreterType.Unknown, + version: new SemVer('3.6.8-test') + }; const oldValueOfVSC_PYTHON_UNIT_TEST = process.env.VSC_PYTHON_UNIT_TEST; const oldValueOfVSC_PYTHON_CI_TEST = process.env.VSC_PYTHON_CI_TEST; @@ -103,11 +113,7 @@ suite('Debugging - Adapter Factory', () => { when(interpreterService.getInterpreterDetails(pythonPath)).thenResolve(interpreter); when(interpreterService.getInterpreters(anything())).thenResolve([interpreter]); - factory = new DebugAdapterDescriptorFactory( - instance(interpreterService), - instance(appShell), - experimentsManager - ); + factory = new DebugAdapterDescriptorFactory(instance(interpreterService), instance(appShell), experimentsManager); }); teardown(() => { @@ -133,7 +139,7 @@ suite('Debugging - Adapter Factory', () => { test('Return the value of configuration.pythonPath as the current python path if it exists and if we are in the experiment', async () => { const session = createSession({ pythonPath }); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); @@ -144,7 +150,7 @@ suite('Debugging - Adapter Factory', () => { test('Return the path of the active interpreter as the current python path if we are in the experiment, it exists and configuration.pythonPath is not defined', async () => { const session = createSession({}); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter); @@ -156,7 +162,7 @@ suite('Debugging - Adapter Factory', () => { test('Return the path of the first available interpreter as the current python path if we are in the experiment, configuration.pythonPath is not defined and there is no active interpreter', async () => { const session = createSession({}); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); @@ -213,27 +219,21 @@ suite('Debugging - Adapter Factory', () => { assert.deepEqual(descriptor, nodeExecutable); }); - test('Return old node debugger when the active interpreter is not Python 3.7', async () => { - const python36Path = path.join('path', 'to', 'active', 'interpreter'); - const interpreterPython36Details = { - architecture: Architecture.Unknown, - path: pythonPath, - sysPrefix: '', - sysVersion: '', - type: InterpreterType.Unknown, - version: new SemVer('3.6.8-test') - }; + test('Return Python debug adapter without wheels executable when the active interpreter is not Python 3.7', async () => { + const debugExecutable = new DebugAdapterExecutable(python36Path, [ptvsdAdapterPathWithoutWheels]); const session = createSession({}); + when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); + when(interpreterService.getInterpreters(anything())).thenResolve([interpreterPython36Details]); when(interpreterService.getInterpreterDetails(python36Path)).thenResolve(interpreterPython36Details); const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); - assert.deepEqual(descriptor, nodeExecutable); + assert.deepEqual(descriptor, debugExecutable); }); - test('Return Python debug adapter executable when in the experiment and with the active interpreter being Python 3.7', async () => { - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + test('Return Python debug adapter with wheels executable when in the experiment and with the active interpreter being Python 3.7', async () => { + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); const session = createSession({}); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); @@ -250,9 +250,9 @@ suite('Debugging - Adapter Factory', () => { await expect(promise).to.eventually.be.rejectedWith('Debug Adapter Executable not provided'); }); - test('Pass the --log-dir argument to PTVSD is configuration.logToFile is set', async () => { + test('Pass the --log-dir argument to PTVSD if configuration.logToFile is set, with active interpreter Python 3.7', async () => { const session = createSession({ logToFile: true }); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath, '--log-dir', EXTENSION_ROOT_DIR]); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels, '--log-dir', EXTENSION_ROOT_DIR]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); @@ -261,9 +261,22 @@ suite('Debugging - Adapter Factory', () => { assert.deepEqual(descriptor, debugExecutable); }); - test('Don\'t pass the --log-dir argument to PTVSD is configuration.logToFile is not set', async () => { + test('Pass the --log-dir argument to PTVSD if configuration.logToFile is set, with active interpreter not Python 3.7', async () => { + const session = createSession({ logToFile: true }); + const debugExecutable = new DebugAdapterExecutable(python36Path, [ptvsdAdapterPathWithoutWheels, '--log-dir', EXTENSION_ROOT_DIR]); + + when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); + when(interpreterService.getInterpreters(anything())).thenResolve([interpreterPython36Details]); + when(interpreterService.getInterpreterDetails(python36Path)).thenResolve(interpreterPython36Details); + + const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); + + assert.deepEqual(descriptor, debugExecutable); + }); + + test('Don\'t pass the --log-dir argument to PTVSD if configuration.logToFile is not set, with active interpreter Python 3.7', async () => { const session = createSession({}); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); @@ -272,25 +285,63 @@ suite('Debugging - Adapter Factory', () => { assert.deepEqual(descriptor, debugExecutable); }); - test('Don\'t pass the --log-dir argument to PTVSD is configuration.logToFile is set but false', async () => { + test('Don\'t pass the --log-dir argument to PTVSD if configuration.logToFile is not set, with active interpreter not Python 3.7', async () => { + const session = createSession({}); + const debugExecutable = new DebugAdapterExecutable(python36Path, [ptvsdAdapterPathWithoutWheels]); + + when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); + when(interpreterService.getInterpreters(anything())).thenResolve([interpreterPython36Details]); + when(interpreterService.getInterpreterDetails(python36Path)).thenResolve(interpreterPython36Details); + + const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); + + assert.deepEqual(descriptor, debugExecutable); + }); + + test('Don\'t pass the --log-dir argument to PTVSD if configuration.logToFile is set but false, with active interpreter Python 3.7', async () => { + const session = createSession({ logToFile: false }); + const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPathWithWheels]); + + when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); + + const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); + + assert.deepEqual(descriptor, debugExecutable); + }); + + test('Don\'t pass the --log-dir argument to PTVSD if configuration.logToFile is set but false, with active interpreter not Python 3.7', async () => { const session = createSession({ logToFile: false }); - const debugExecutable = new DebugAdapterExecutable(pythonPath, [ptvsdAdapterPath]); + const debugExecutable = new DebugAdapterExecutable(python36Path, [ptvsdAdapterPathWithoutWheels]); when(spiedInstance.inExperiment(DebugAdapterNewPtvsd.experiment)).thenReturn(true); + when(interpreterService.getInterpreters(anything())).thenResolve([interpreterPython36Details]); + when(interpreterService.getInterpreterDetails(python36Path)).thenResolve(interpreterPython36Details); const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable); assert.deepEqual(descriptor, debugExecutable); }); - test('Send experiment group telemetry if inside the wheels experiment', async () => { + test('Send experiment group telemetry if inside the wheels experiment, with active interpreter Python 3.7', async () => { const session = createSession({}); when(spiedInstance.userExperiments).thenReturn([{ name: DebugAdapterNewPtvsd.experiment, salt: DebugAdapterNewPtvsd.experiment, min: 0, max: 0 }]); await factory.createDebugAdapterDescriptor(session, nodeExecutable); - assert.deepEqual(Reporter.eventNames, [EventName.PYTHON_EXPERIMENTS]); - assert.deepEqual(Reporter.properties, [{ expName: DebugAdapterNewPtvsd.experiment }]); + assert.deepEqual(Reporter.eventNames, [EventName.PYTHON_EXPERIMENTS, EventName.DEBUG_ADAPTER_USING_WHEELS_PATH]); + assert.deepEqual(Reporter.properties, [{ expName: DebugAdapterNewPtvsd.experiment }, { usingWheels: 'true' }]); + }); + + test('Send experiment group telemetry if inside the wheels experiment, with active interpreter not Python 3.6', async () => { + const session = createSession({}); + when(spiedInstance.userExperiments).thenReturn([{ name: DebugAdapterNewPtvsd.experiment, salt: DebugAdapterNewPtvsd.experiment, min: 0, max: 0 }]); + when(interpreterService.getInterpreters(anything())).thenResolve([interpreterPython36Details]); + when(interpreterService.getInterpreterDetails(python36Path)).thenResolve(interpreterPython36Details); + + await factory.createDebugAdapterDescriptor(session, nodeExecutable); + + assert.deepEqual(Reporter.eventNames, [EventName.PYTHON_EXPERIMENTS, EventName.DEBUG_ADAPTER_USING_WHEELS_PATH]); + assert.deepEqual(Reporter.properties, [{ expName: DebugAdapterNewPtvsd.experiment }, { usingWheels: 'false' }]); }); test('Send control group telemetry if inside the DA experiment control group', async () => {