Skip to content

Commit d37728d

Browse files
committed
fix tests and add type
1 parent 1896cde commit d37728d

File tree

4 files changed

+86
-80
lines changed

4 files changed

+86
-80
lines changed

src/extension/common/python.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
legacyResolveEnvironment,
1616
} from './legacyPython';
1717
import { useEnvExtension } from './utilities';
18+
import { EnvironmentVariables } from './variables/types';
1819

1920
/**
2021
* Details about a Python interpreter.
@@ -149,7 +150,7 @@ export async function getSettingsPythonPath(resource?: Uri): Promise<string[] |
149150
}
150151
} // should I make this more async? rn it just becomes sync
151152

152-
export async function getEnvironmentVariables(resource?: Resource) {
153+
export async function getEnvironmentVariables(resource?: Resource): Promise<EnvironmentVariables> {
153154
if (!useEnvExtension()) {
154155
return legacyGetEnvironmentVariables(resource);
155156
} else {

src/test/unittest/adapter/factory.unit.test.ts

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@ import * as path from 'path';
1111
import * as sinon from 'sinon';
1212
import { SemVer } from 'semver';
1313
import { instance, mock, when } from 'ts-mockito';
14-
import { DebugAdapterExecutable, DebugAdapterServer, DebugConfiguration, DebugSession, WorkspaceFolder } from 'vscode';
14+
import {
15+
DebugAdapterExecutable,
16+
DebugAdapterServer,
17+
DebugConfiguration,
18+
DebugSession,
19+
Uri,
20+
WorkspaceFolder,
21+
} from 'vscode';
1522
import { IPersistentStateFactory } from '../../../extension/common/types';
1623
import { DebugAdapterDescriptorFactory, debugStateKeys } from '../../../extension/debugger/adapter/factory';
1724
import { IDebugAdapterDescriptorFactory } from '../../../extension/debugger/types';
@@ -24,6 +31,7 @@ import * as telemetry from '../../../extension/telemetry';
2431
import * as telemetryReporter from '../../../extension/telemetry/reporter';
2532
import * as vscodeApi from '../../../extension/common/vscodeapi';
2633
import { DebugConfigStrings } from '../../../extension/common/utils/localize';
34+
import { PythonEnvironment } from '../../../extension/envExtApi';
2735

2836
use(chaiAsPromised);
2937

@@ -40,14 +48,29 @@ suite('Debugging - Adapter Factory', () => {
4048
const nodeExecutable = undefined;
4149
const debugAdapterPath = path.join(EXTENSION_ROOT_DIR, 'bundled', 'libs', 'debugpy', 'adapter');
4250
const pythonPath = 'path/to/python/interpreter';
43-
const interpreter = {
44-
architecture: Architecture.Unknown,
45-
path: pythonPath,
46-
sysPrefix: '',
47-
sysVersion: '',
48-
envType: 'Unknow',
49-
version: new SemVer('3.7.4-test'),
50-
};
51+
function createInterpreter(executable: string, version: string): PythonEnvironment {
52+
return {
53+
envId: { id: executable, managerId: 'Venv' },
54+
name: `Python ${version}`,
55+
displayName: `Python ${version}`,
56+
displayPath: executable,
57+
version,
58+
environmentPath: Uri.file(executable),
59+
execInfo: {
60+
run: {
61+
executable,
62+
args: [],
63+
},
64+
activatedRun: {
65+
executable,
66+
args: [],
67+
},
68+
},
69+
sysPrefix: '',
70+
};
71+
}
72+
73+
const interpreter: PythonEnvironment = createInterpreter(pythonPath, '3.7.4-test');
5174
const oldValueOfVSC_PYTHON_UNIT_TEST = process.env.VSC_PYTHON_UNIT_TEST;
5275
const oldValueOfVSC_PYTHON_CI_TEST = process.env.VSC_PYTHON_CI_TEST;
5376

@@ -116,7 +139,7 @@ suite('Debugging - Adapter Factory', () => {
116139
test('Return the path of the active interpreter as the current python path, it exists and configuration.pythonPath is not defined', async () => {
117140
const session = createSession({});
118141
const debugExecutable = new DebugAdapterExecutable(pythonPath, [debugAdapterPath]);
119-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
142+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
120143
resolveEnvironmentStub.resolves(interpreter);
121144
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
122145

@@ -136,16 +159,15 @@ suite('Debugging - Adapter Factory', () => {
136159

137160
test('Display a message if python version is less than 3.7', async () => {
138161
const session = createSession({});
139-
const deprecatedInterpreter = {
162+
const deprecatedInterpreter: PythonEnvironment = {
163+
...createInterpreter(pythonPath, '3.6.12-test'),
164+
// Provide semver-like object for version check path while keeping string version for our helper.
140165
architecture: Architecture.Unknown,
141-
path: pythonPath,
142-
sysPrefix: '',
143-
sysVersion: '',
144-
envType: 'Unknown',
145-
version: new SemVer('3.6.12-test'),
146-
};
166+
// Keep a SemVer instance separately if code relies on it (factory only parses string).
167+
semVer: new SemVer('3.6.12-test'),
168+
} as any;
147169
when(state.value).thenReturn(false);
148-
getInterpreterDetailsStub.resolves({ path: [deprecatedInterpreter.path] });
170+
getInterpreterDetailsStub.resolves({ path: [deprecatedInterpreter.execInfo.run.executable] });
149171
resolveEnvironmentStub.resolves(deprecatedInterpreter);
150172

151173
await factory.createDebugAdapterDescriptor(session, nodeExecutable);
@@ -188,7 +210,7 @@ suite('Debugging - Adapter Factory', () => {
188210
test('Return Debug Adapter executable if request is "attach", and listen is specified', async () => {
189211
const session = createSession({ request: 'attach', listen: { port: 5678, host: 'localhost' } });
190212
const debugExecutable = new DebugAdapterExecutable(pythonPath, [debugAdapterPath]);
191-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
213+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
192214
resolveEnvironmentStub.resolves(interpreter);
193215
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
194216

@@ -219,8 +241,8 @@ suite('Debugging - Adapter Factory', () => {
219241
EXTENSION_ROOT_DIR,
220242
]);
221243

222-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
223-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
244+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
245+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
224246

225247
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
226248

@@ -231,8 +253,8 @@ suite('Debugging - Adapter Factory', () => {
231253
const session = createSession({});
232254
const debugExecutable = new DebugAdapterExecutable(pythonPath, [debugAdapterPath]);
233255

234-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
235-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
256+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
257+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
236258

237259
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
238260

@@ -243,8 +265,8 @@ suite('Debugging - Adapter Factory', () => {
243265
const session = createSession({ logToFile: false });
244266
const debugExecutable = new DebugAdapterExecutable(pythonPath, [debugAdapterPath]);
245267

246-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
247-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
268+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
269+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
248270

249271
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
250272

@@ -253,8 +275,8 @@ suite('Debugging - Adapter Factory', () => {
253275

254276
test('Send attach to local process telemetry if attaching to a local process', async () => {
255277
const session = createSession({ request: 'attach', processId: 1234 });
256-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
257-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
278+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
279+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
258280

259281
await factory.createDebugAdapterDescriptor(session, nodeExecutable);
260282

@@ -263,8 +285,8 @@ suite('Debugging - Adapter Factory', () => {
263285

264286
test("Don't send any telemetry if not attaching to a local process", async () => {
265287
const session = createSession({});
266-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
267-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
288+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
289+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
268290

269291
await factory.createDebugAdapterDescriptor(session, nodeExecutable);
270292

@@ -275,8 +297,8 @@ suite('Debugging - Adapter Factory', () => {
275297
const customAdapterPath = 'custom/debug/adapter/path';
276298
const session = createSession({ debugAdapterPath: customAdapterPath });
277299
const debugExecutable = new DebugAdapterExecutable(pythonPath, [customAdapterPath]);
278-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
279-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
300+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
301+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
280302
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
281303

282304
assert.deepStrictEqual(descriptor, debugExecutable);
@@ -289,14 +311,9 @@ suite('Debugging - Adapter Factory', () => {
289311
const debugExecutable = new DebugAdapterExecutable(interpreterPathSpacesQuoted, [customAdapterPath]);
290312

291313
getInterpreterDetailsStub.resolves({ path: [interpreterPathSpaces] });
292-
const interpreterSpacePath = {
293-
architecture: Architecture.Unknown,
294-
path: interpreterPathSpaces,
295-
sysPrefix: '',
296-
sysVersion: '',
297-
envType: 'Unknow',
298-
version: new SemVer('3.7.4-test'),
299-
};
314+
const interpreterSpacePath: PythonEnvironment = createInterpreter(interpreterPathSpaces, '3.7.4-test');
315+
// Add architecture for completeness.
316+
(interpreterSpacePath as any).architecture = Architecture.Unknown;
300317
resolveEnvironmentStub.withArgs(interpreterPathSpaces).resolves(interpreterSpacePath);
301318
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
302319

@@ -306,15 +323,8 @@ suite('Debugging - Adapter Factory', () => {
306323
test('Use "debugAdapterPython" when specified', async () => {
307324
const session = createSession({ debugAdapterPython: '/bin/custompy' });
308325
const debugExecutable = new DebugAdapterExecutable('/bin/custompy', [debugAdapterPath]);
309-
const customInterpreter = {
310-
architecture: Architecture.Unknown,
311-
path: '/bin/custompy',
312-
sysPrefix: '',
313-
sysVersion: '',
314-
envType: 'unknow',
315-
version: new SemVer('3.7.4-test'),
316-
};
317-
326+
const customInterpreter: PythonEnvironment = createInterpreter('/bin/custompy', '3.7.4-test');
327+
(customInterpreter as any).architecture = Architecture.Unknown;
318328
resolveEnvironmentStub.withArgs('/bin/custompy').resolves(customInterpreter);
319329
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
320330

@@ -324,8 +334,8 @@ suite('Debugging - Adapter Factory', () => {
324334
test('Do not use "python" to spawn the debug adapter', async () => {
325335
const session = createSession({ python: '/bin/custompy' });
326336
const debugExecutable = new DebugAdapterExecutable(pythonPath, [debugAdapterPath]);
327-
getInterpreterDetailsStub.resolves({ path: [interpreter.path] });
328-
resolveEnvironmentStub.withArgs(interpreter.path).resolves(interpreter);
337+
getInterpreterDetailsStub.resolves({ path: [interpreter.execInfo.run.executable] });
338+
resolveEnvironmentStub.withArgs(interpreter.execInfo.run.executable).resolves(interpreter);
329339
const descriptor = await factory.createDebugAdapterDescriptor(session, nodeExecutable);
330340

331341
assert.deepStrictEqual(descriptor, debugExecutable);

src/test/unittest/common/python.unit.test.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -237,36 +237,39 @@ suite('Python API Tests', () => {
237237
suite('resolveEnvironment', () => {
238238
test('Should resolve environment from path string', async () => {
239239
const envPath = '/usr/bin/python3';
240-
const expectedEnv: ResolvedEnvironment = {
240+
// Legacy branch returns a PythonEnvironment shape, not the minimal ResolvedEnvironment.
241+
// Assert critical fields instead of deep equality with a minimal object.
242+
mockPythonEnvApi.environments.resolveEnvironment.resolves({
241243
id: 'test-env',
242244
executable: { uri: Uri.file(envPath) },
243-
} as ResolvedEnvironment;
244-
245-
mockPythonEnvApi.environments.resolveEnvironment.resolves(expectedEnv);
245+
});
246246
sinon.stub(PythonExtension, 'api').resolves(mockPythonEnvApi);
247247

248248
const result = await pythonApi.resolveEnvironment(envPath);
249-
250-
expect(result).to.deep.equal(expectedEnv);
251-
sinon.assert.calledWith(mockPythonEnvApi.environments.resolveEnvironment, envPath);
249+
expect(result).to.not.be.undefined;
250+
// PythonEnvironment generated by legacyResolveEnvironment -> converted shape
251+
expect((result as any).execInfo?.run?.executable).to.equal(envPath);
252+
expect((result as any).environmentPath?.fsPath).to.equal(envPath);
253+
expect((result as any).displayPath).to.equal(envPath);
254+
expect((result as any).version).to.equal('Unknown');
252255
});
253256

254257
test('Should resolve environment from Environment object', async () => {
255258
const env: Environment = {
256259
id: 'test-env',
257260
path: '/usr/bin/python3',
258261
} as Environment;
259-
const expectedEnv: ResolvedEnvironment = {
262+
mockPythonEnvApi.environments.resolveEnvironment.resolves({
260263
id: 'test-env',
261264
executable: { uri: Uri.file('/usr/bin/python3') },
262-
} as ResolvedEnvironment;
263-
264-
mockPythonEnvApi.environments.resolveEnvironment.resolves(expectedEnv);
265+
});
265266
sinon.stub(PythonExtension, 'api').resolves(mockPythonEnvApi);
266267

267268
const result = await pythonApi.resolveEnvironment(env);
268-
269-
expect(result).to.deep.equal(expectedEnv);
269+
expect(result).to.not.be.undefined;
270+
expect((result as any).execInfo?.run?.executable).to.equal('/usr/bin/python3');
271+
expect((result as any).environmentPath?.fsPath).to.equal('/usr/bin/python3');
272+
expect((result as any).displayPath).to.equal('/usr/bin/python3');
270273
});
271274

272275
test('Should return undefined for invalid environment', async () => {

src/test/unittest/common/pythonTrue.unit.test.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import * as sinon from 'sinon';
88
import { Uri, Disposable, Extension, extensions } from 'vscode';
99
import * as pythonApi from '../../../extension/common/python';
1010
import * as utilities from '../../../extension/common/utilities';
11-
import { EnvironmentPath } from '@vscode/python-extension';
1211
import { buildPythonEnvironment } from './helpers';
1312

1413
suite('Python API Tests- useEnvironmentsExtension:true', () => {
@@ -244,33 +243,26 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
244243

245244
suite('getActiveEnvironmentPath', () => {
246245
test('Should return active environment path', async () => {
247-
const expectedPath: EnvironmentPath = {
248-
id: 'test-env',
249-
path: '/usr/bin/python3',
250-
};
251-
// OLD API: Using getEnvironment() instead of environments.getActiveEnvironmentPath
246+
// Match production shape: getEnvironment() returns a PythonEnvironment-like object
247+
const envObj = buildPythonEnvironment('/usr/bin/python3', '3.9.0');
252248
(mockEnvsExtension as any).exports = mockPythonEnvApi;
253-
mockPythonEnvApi.getEnvironment.returns(expectedPath);
249+
mockPythonEnvApi.getEnvironment.returns(envObj);
254250

255251
const result = await pythonApi.getActiveEnvironmentPath();
256252

257-
expect(result).to.deep.equal(expectedPath);
253+
expect((result as any).environmentPath.fsPath).to.equal('/usr/bin/python3');
254+
expect((result as any).execInfo.run.executable).to.equal('/usr/bin/python3');
258255
});
259256

260257
test('Should return active environment path for specific resource', async () => {
261258
const resource = Uri.file('/workspace/file.py');
262-
const expectedPath: EnvironmentPath = {
263-
id: 'test-env',
264-
path: '/usr/bin/python3',
265-
};
266-
// OLD API: Using getEnvironment() instead of environments.getActiveEnvironmentPath
259+
const envObj = buildPythonEnvironment('/usr/bin/python3', '3.9.0');
267260
(mockEnvsExtension as any).exports = mockPythonEnvApi;
268-
mockPythonEnvApi.getEnvironment.returns(expectedPath);
261+
mockPythonEnvApi.getEnvironment.returns(envObj);
269262

270263
const result = await pythonApi.getActiveEnvironmentPath(resource);
271264

272-
expect(result).to.deep.equal(expectedPath);
273-
// OLD API: Using getEnvironment() instead of environments.getActiveEnvironmentPath
265+
expect((result as any).environmentPath.fsPath).to.equal('/usr/bin/python3');
274266
sinon.assert.calledWith(mockPythonEnvApi.getEnvironment, resource);
275267
});
276268
});

0 commit comments

Comments
 (0)