diff --git a/src/extension/noConfigDebugInit.ts b/src/extension/noConfigDebugInit.ts index 428c478..23ed379 100644 --- a/src/extension/noConfigDebugInit.ts +++ b/src/extension/noConfigDebugInit.ts @@ -77,7 +77,13 @@ export async function registerNoConfigDebug( const noConfigScriptsDir = path.join(extPath, 'bundled', 'scripts', 'noConfigScripts'); const pathSeparator = process.platform === 'win32' ? ';' : ':'; - collection.append('PATH', `${pathSeparator}${noConfigScriptsDir}`); + + // Check if the current PATH already ends with a path separator to avoid double separators + const currentPath = process.env.PATH || ''; + const needsSeparator = currentPath.length > 0 && !currentPath.endsWith(pathSeparator); + const pathValueToAppend = needsSeparator ? `${pathSeparator}${noConfigScriptsDir}` : noConfigScriptsDir; + + collection.append('PATH', pathValueToAppend); const bundledDebugPath = path.join(extPath, 'bundled', 'libs', 'debugpy'); collection.replace('BUNDLED_DEBUGPY_PATH', bundledDebugPath); diff --git a/src/test/unittest/noConfigDebugInit.unit.test.ts b/src/test/unittest/noConfigDebugInit.unit.test.ts index a4f9c79..3cf6e40 100644 --- a/src/test/unittest/noConfigDebugInit.unit.test.ts +++ b/src/test/unittest/noConfigDebugInit.unit.test.ts @@ -71,7 +71,7 @@ suite('setup for no-config debug scenario', function () { .setup((x) => x.append(TypeMoq.It.isAny(), TypeMoq.It.isAny())) .callback((key, value) => { if (key === 'PATH') { - assert(value === `:${noConfigScriptsDir}`); + assert(value.includes(noConfigScriptsDir)); } }) .returns(envVarCollectionAppendStub); @@ -88,6 +88,100 @@ suite('setup for no-config debug scenario', function () { sinon.assert.calledOnce(envVarCollectionAppendStub); }); + test('should not add extra separator when PATH already ends with separator', async () => { + const environmentVariableCollectionMock = TypeMoq.Mock.ofType(); + envVarCollectionReplaceStub = sinon.stub(); + envVarCollectionAppendStub = sinon.stub(); + + // Simulate a PATH that already ends with a separator to test the fix + const pathSeparator = process.platform === 'win32' ? ';' : ':'; + const originalPath = process.env.PATH; + process.env.PATH = `/some/path${pathSeparator}`; + + try { + environmentVariableCollectionMock + .setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(envVarCollectionReplaceStub); + + environmentVariableCollectionMock + .setup((x) => x.append(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .callback((key, value) => { + if (key === 'PATH') { + // Since PATH already ends with separator, we should NOT add another one + assert(value === noConfigScriptsDir); + assert(!value.startsWith(pathSeparator)); + } + }) + .returns(envVarCollectionAppendStub); + + context + .setup((c) => c.environmentVariableCollection) + .returns(() => environmentVariableCollectionMock.object); + + setupFileSystemWatchers(); + + // run init for no config debug + await registerNoConfigDebug(context.object.environmentVariableCollection, context.object.extensionPath); + + // assert that append was called for PATH + sinon.assert.calledOnce(envVarCollectionAppendStub); + } finally { + // Restore original PATH + if (originalPath !== undefined) { + process.env.PATH = originalPath; + } else { + delete process.env.PATH; + } + } + }); + + test('should add separator when PATH does not end with separator', async () => { + const environmentVariableCollectionMock = TypeMoq.Mock.ofType(); + envVarCollectionReplaceStub = sinon.stub(); + envVarCollectionAppendStub = sinon.stub(); + + // Simulate a PATH that does NOT end with a separator + const pathSeparator = process.platform === 'win32' ? ';' : ':'; + const originalPath = process.env.PATH; + process.env.PATH = '/some/path'; + + try { + environmentVariableCollectionMock + .setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(envVarCollectionReplaceStub); + + environmentVariableCollectionMock + .setup((x) => x.append(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .callback((key, value) => { + if (key === 'PATH') { + // Since PATH does NOT end with separator, we should add one + assert(value === `${pathSeparator}${noConfigScriptsDir}`); + assert(value.startsWith(pathSeparator)); + } + }) + .returns(envVarCollectionAppendStub); + + context + .setup((c) => c.environmentVariableCollection) + .returns(() => environmentVariableCollectionMock.object); + + setupFileSystemWatchers(); + + // run init for no config debug + await registerNoConfigDebug(context.object.environmentVariableCollection, context.object.extensionPath); + + // assert that append was called for PATH + sinon.assert.calledOnce(envVarCollectionAppendStub); + } finally { + // Restore original PATH + if (originalPath !== undefined) { + process.env.PATH = originalPath; + } else { + delete process.env.PATH; + } + } + }); + test('should create file system watcher for debuggerAdapterEndpointFolder', async () => { // Arrange const environmentVariableCollectionMock = TypeMoq.Mock.ofType();