Skip to content

Commit f46d9d1

Browse files
Copiloteleanorjboyd
andcommitted
Add comprehensive tests for resolveAndUpdatePythonPath prioritization logic
Co-authored-by: eleanorjboyd <[email protected]>
1 parent b435ab3 commit f46d9d1

File tree

1 file changed

+350
-0
lines changed

1 file changed

+350
-0
lines changed

src/test/unittest/configuration/resolvers/base.unit.test.ts

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,356 @@ suite('Debugging - Config Resolver', () => {
270270
expect(config).to.have.property('debugLauncherPython', pythonPath);
271271
});
272272

273+
// Tests for prioritization of python path configuration
274+
suite('resolveAndUpdatePythonPath prioritization tests', () => {
275+
test('When pythonPath is a concrete path and python is undefined, python should be set to pythonPath value', async () => {
276+
const expectedPath = path.join('path', 'to', 'custom', 'python');
277+
const config = {
278+
pythonPath: expectedPath,
279+
python: undefined,
280+
};
281+
282+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
283+
284+
expect(config).to.not.have.property('pythonPath');
285+
expect(config).to.have.property('python', expectedPath);
286+
});
287+
288+
test('When pythonPath is a concrete path and python is a different concrete path, python should take precedence', async () => {
289+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
290+
const pythonValue = path.join('path', 'to', 'python', 'python');
291+
const config = {
292+
pythonPath: pythonPathValue,
293+
python: pythonValue,
294+
};
295+
296+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
297+
298+
expect(config).to.not.have.property('pythonPath');
299+
expect(config).to.have.property('python', pythonValue);
300+
});
301+
302+
test('When pythonPath is ${command:python.interpreterPath} and python is a concrete path, python should take precedence', async () => {
303+
const pythonValue = path.join('path', 'to', 'python', 'python');
304+
const interpreterPath = path.join('path', 'from', 'interpreter');
305+
const config = {
306+
pythonPath: '${command:python.interpreterPath}',
307+
python: pythonValue,
308+
};
309+
310+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
311+
312+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
313+
314+
expect(config).to.not.have.property('pythonPath');
315+
expect(config).to.have.property('python', pythonValue);
316+
});
317+
318+
test('When pythonPath is a concrete path and python is ${command:python.interpreterPath}, python should resolve from interpreter', async () => {
319+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
320+
const interpreterPath = path.join('path', 'from', 'interpreter');
321+
const config = {
322+
pythonPath: pythonPathValue,
323+
python: '${command:python.interpreterPath}',
324+
};
325+
326+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
327+
328+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
329+
330+
expect(config).to.not.have.property('pythonPath');
331+
expect(config).to.have.property('python', interpreterPath);
332+
});
333+
334+
test('When both pythonPath and python are ${command:python.interpreterPath}, both should resolve to interpreter path', async () => {
335+
const interpreterPath = path.join('path', 'from', 'interpreter');
336+
const config = {
337+
pythonPath: '${command:python.interpreterPath}',
338+
python: '${command:python.interpreterPath}',
339+
};
340+
341+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
342+
343+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
344+
345+
expect(config).to.not.have.property('pythonPath');
346+
expect(config).to.have.property('python', interpreterPath);
347+
});
348+
349+
test('When pythonPath is not set and python is not set, both should resolve from interpreter', async () => {
350+
const interpreterPath = path.join('path', 'from', 'interpreter');
351+
const config = {};
352+
353+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
354+
355+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
356+
357+
expect(config).to.not.have.property('pythonPath');
358+
expect(config).to.have.property('python', interpreterPath);
359+
});
360+
361+
test('debugAdapterPython should use pythonPath when neither debugAdapterPython nor python are set', async () => {
362+
const pythonPathValue = path.join('path', 'to', 'custom', 'python');
363+
const config = {
364+
pythonPath: pythonPathValue,
365+
};
366+
367+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
368+
369+
expect(config).to.not.have.property('pythonPath');
370+
expect(config).to.have.property('python', pythonPathValue);
371+
expect(config).to.have.property('debugAdapterPython', pythonPathValue);
372+
});
373+
374+
test('debugAdapterPython should use python when pythonPath is set but python has different value', async () => {
375+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
376+
const pythonValue = path.join('path', 'to', 'python', 'python');
377+
const config = {
378+
pythonPath: pythonPathValue,
379+
python: pythonValue,
380+
};
381+
382+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
383+
384+
expect(config).to.not.have.property('pythonPath');
385+
expect(config).to.have.property('python', pythonValue);
386+
expect(config).to.have.property('debugAdapterPython', pythonValue);
387+
});
388+
389+
test('debugAdapterPython should prefer explicitly set debugAdapterPython over pythonPath', async () => {
390+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
391+
const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python');
392+
const config = {
393+
pythonPath: pythonPathValue,
394+
debugAdapterPython: debugAdapterValue,
395+
};
396+
397+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
398+
399+
expect(config).to.not.have.property('pythonPath');
400+
expect(config).to.have.property('python', pythonPathValue);
401+
expect(config).to.have.property('debugAdapterPython', debugAdapterValue);
402+
});
403+
404+
test('debugLauncherPython should use pythonPath when neither debugLauncherPython nor python are set', async () => {
405+
const pythonPathValue = path.join('path', 'to', 'custom', 'python');
406+
const config = {
407+
pythonPath: pythonPathValue,
408+
};
409+
410+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
411+
412+
expect(config).to.not.have.property('pythonPath');
413+
expect(config).to.have.property('python', pythonPathValue);
414+
expect(config).to.have.property('debugLauncherPython', pythonPathValue);
415+
});
416+
417+
test('debugLauncherPython should use python when pythonPath is set but python has different value', async () => {
418+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
419+
const pythonValue = path.join('path', 'to', 'python', 'python');
420+
const config = {
421+
pythonPath: pythonPathValue,
422+
python: pythonValue,
423+
};
424+
425+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
426+
427+
expect(config).to.not.have.property('pythonPath');
428+
expect(config).to.have.property('python', pythonValue);
429+
expect(config).to.have.property('debugLauncherPython', pythonValue);
430+
});
431+
432+
test('debugLauncherPython should prefer explicitly set debugLauncherPython over pythonPath', async () => {
433+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
434+
const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python');
435+
const config = {
436+
pythonPath: pythonPathValue,
437+
debugLauncherPython: debugLauncherValue,
438+
};
439+
440+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
441+
442+
expect(config).to.not.have.property('pythonPath');
443+
expect(config).to.have.property('python', pythonPathValue);
444+
expect(config).to.have.property('debugLauncherPython', debugLauncherValue);
445+
});
446+
447+
test('All three debug python fields can have different values when explicitly set', async () => {
448+
const pythonValue = path.join('path', 'to', 'python', 'python');
449+
const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python');
450+
const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python');
451+
const config = {
452+
python: pythonValue,
453+
debugAdapterPython: debugAdapterValue,
454+
debugLauncherPython: debugLauncherValue,
455+
};
456+
457+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
458+
459+
expect(config).to.not.have.property('pythonPath');
460+
expect(config).to.have.property('python', pythonValue);
461+
expect(config).to.have.property('debugAdapterPython', debugAdapterValue);
462+
expect(config).to.have.property('debugLauncherPython', debugLauncherValue);
463+
});
464+
465+
test('When debugAdapterPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => {
466+
const interpreterPath = path.join('path', 'from', 'interpreter');
467+
const config = {
468+
pythonPath: '${command:python.interpreterPath}',
469+
debugAdapterPython: '${command:python.interpreterPath}',
470+
};
471+
472+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
473+
474+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
475+
476+
expect(config).to.not.have.property('pythonPath');
477+
expect(config).to.have.property('python', interpreterPath);
478+
expect(config).to.have.property('debugAdapterPython', interpreterPath);
479+
});
480+
481+
test('When debugLauncherPython is ${command:python.interpreterPath}, it should fallback to resolved pythonPath', async () => {
482+
const interpreterPath = path.join('path', 'from', 'interpreter');
483+
const config = {
484+
pythonPath: '${command:python.interpreterPath}',
485+
debugLauncherPython: '${command:python.interpreterPath}',
486+
};
487+
488+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
489+
490+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
491+
492+
expect(config).to.not.have.property('pythonPath');
493+
expect(config).to.have.property('python', interpreterPath);
494+
expect(config).to.have.property('debugLauncherPython', interpreterPath);
495+
});
496+
497+
test('Complex scenario: pythonPath set, python differs, debugAdapterPython and debugLauncherPython both set differently', async () => {
498+
const pythonPathValue = path.join('path', 'to', 'pythonPath', 'python');
499+
const pythonValue = path.join('path', 'to', 'python', 'python');
500+
const debugAdapterValue = path.join('path', 'to', 'debugAdapter', 'python');
501+
const debugLauncherValue = path.join('path', 'to', 'debugLauncher', 'python');
502+
const config = {
503+
pythonPath: pythonPathValue,
504+
python: pythonValue,
505+
debugAdapterPython: debugAdapterValue,
506+
debugLauncherPython: debugLauncherValue,
507+
};
508+
509+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
510+
511+
expect(config).to.not.have.property('pythonPath');
512+
expect(config).to.have.property('python', pythonValue);
513+
expect(config).to.have.property('debugAdapterPython', debugAdapterValue);
514+
expect(config).to.have.property('debugLauncherPython', debugLauncherValue);
515+
});
516+
517+
test('When pythonPath is undefined and python is concrete path, debugAdapter and debugLauncher should use python', async () => {
518+
const pythonValue = path.join('path', 'to', 'python', 'python');
519+
const config = {
520+
python: pythonValue,
521+
};
522+
523+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
524+
525+
expect(config).to.not.have.property('pythonPath');
526+
expect(config).to.have.property('python', pythonValue);
527+
expect(config).to.have.property('debugAdapterPython', pythonValue);
528+
expect(config).to.have.property('debugLauncherPython', pythonValue);
529+
});
530+
531+
test('When pythonPath is empty string, it should be treated as not set and resolve from interpreter', async () => {
532+
const interpreterPath = path.join('path', 'from', 'interpreter');
533+
const config = {
534+
pythonPath: '',
535+
};
536+
537+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
538+
539+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
540+
541+
expect(config).to.not.have.property('pythonPath');
542+
expect(config).to.have.property('python', interpreterPath);
543+
});
544+
545+
// Tests for pythonPathSource field
546+
test('pythonPathSource should be settingsJson when python is ${command:python.interpreterPath}', async () => {
547+
const interpreterPath = path.join('path', 'from', 'interpreter');
548+
const config = {
549+
python: '${command:python.interpreterPath}',
550+
};
551+
552+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
553+
554+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
555+
556+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson);
557+
});
558+
559+
test('pythonPathSource should be settingsJson when python is undefined', async () => {
560+
const interpreterPath = path.join('path', 'from', 'interpreter');
561+
const config = {
562+
pythonPath: interpreterPath,
563+
python: undefined,
564+
};
565+
566+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
567+
568+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson);
569+
});
570+
571+
test('pythonPathSource should be launchJson when python is explicitly set to a concrete path', async () => {
572+
const pythonValue = path.join('path', 'to', 'python', 'python');
573+
const config = {
574+
python: pythonValue,
575+
};
576+
577+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
578+
579+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson);
580+
});
581+
582+
test('pythonPathSource should be launchJson when python is a concrete path even if pythonPath is ${command:python.interpreterPath}', async () => {
583+
const pythonValue = path.join('path', 'to', 'python', 'python');
584+
const interpreterPath = path.join('path', 'from', 'interpreter');
585+
const config = {
586+
pythonPath: '${command:python.interpreterPath}',
587+
python: pythonValue,
588+
};
589+
590+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
591+
592+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
593+
594+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.launchJson);
595+
});
596+
597+
test('pythonPathSource should be settingsJson when both pythonPath and python are ${command:python.interpreterPath}', async () => {
598+
const interpreterPath = path.join('path', 'from', 'interpreter');
599+
const config = {
600+
pythonPath: '${command:python.interpreterPath}',
601+
python: '${command:python.interpreterPath}',
602+
};
603+
604+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
605+
606+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
607+
608+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson);
609+
});
610+
611+
test('pythonPathSource should be settingsJson when neither pythonPath nor python are set', async () => {
612+
const interpreterPath = path.join('path', 'from', 'interpreter');
613+
const config = {};
614+
615+
getInterpreterDetailsStub.resolves({ path: [interpreterPath] } as unknown as PythonEnvironment);
616+
617+
await resolver.resolveAndUpdatePythonPath(undefined, config as LaunchRequestArguments);
618+
619+
expect(resolver.getPythonPathSource()).to.equal(PythonPathSource.settingsJson);
620+
});
621+
});
622+
273623
const localHostTestMatrix: Record<string, boolean> = {
274624
localhost: true,
275625
'127.0.0.1': true,

0 commit comments

Comments
 (0)