diff --git a/src/client/testing/testController/pytest/pytestHelpers.ts b/src/client/testing/testController/pytest/pytestHelpers.ts index c6e748fb85a7..1eca4c0ff16a 100644 --- a/src/client/testing/testController/pytest/pytestHelpers.ts +++ b/src/client/testing/testController/pytest/pytestHelpers.ts @@ -4,6 +4,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { traceInfo, traceWarn } from '../../../logging'; import { addValueIfKeyNotExist, hasSymlinkParent } from '../common/utils'; +import { arePathsSame } from '../../../common/platform/fs-paths'; /** * Checks if the current working directory contains a symlink and ensures --rootdir is set in pytest args. @@ -16,7 +17,7 @@ export async function handleSymlinkAndRootDir(cwd: string, pytestArgs: string[]) if (stats.isSymbolicLink()) { isSymbolicLink = true; traceWarn(`Working directory is a symbolic link: ${cwd} -> ${resolvedPath}`); - } else if (resolvedPath !== cwd) { + } else if (!arePathsSame(resolvedPath, cwd)) { traceWarn( `Working directory resolves to different path: ${cwd} -> ${resolvedPath}. Checking for symlinks in parent directories.`, ); diff --git a/src/test/testing/testController/pytest/pytestHelpers.unit.test.ts b/src/test/testing/testController/pytest/pytestHelpers.unit.test.ts new file mode 100644 index 000000000000..daf8a49a1a77 --- /dev/null +++ b/src/test/testing/testController/pytest/pytestHelpers.unit.test.ts @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as sinon from 'sinon'; +import { handleSymlinkAndRootDir } from '../../../../client/testing/testController/pytest/pytestHelpers'; +import * as utils from '../../../../client/testing/testController/common/utils'; +import * as fsPaths from '../../../../client/common/platform/fs-paths'; + +suite('pytestHelpers handleSymlinkAndRootDir', () => { + teardown(() => { + sinon.restore(); + }); + + test('does not check symlink parent when cwd and resolvedPath are considered the same path', async () => { + sinon.stub(fs.promises, 'lstat').resolves({ + isSymbolicLink: () => false, + } as fs.Stats); + sinon.stub(fs.promises, 'realpath').resolves('C:\\clones\\python-soundfile'); + sinon.stub(fsPaths, 'arePathsSame').callsFake((p1, p2) => p1.toLowerCase() === p2.toLowerCase()); + + const hasSymlinkParentStub = sinon.stub(utils, 'hasSymlinkParent'); + + const result = await handleSymlinkAndRootDir('c:\\clones\\python-soundfile', ['arg1']); + + assert.strictEqual(hasSymlinkParentStub.called, false); + assert.deepStrictEqual(result, ['arg1', '--rootdir=c:\\clones\\python-soundfile']); + }); + + test('checks symlink parent when resolvedPath differs from cwd', async () => { + sinon.stub(fs.promises, 'lstat').resolves({ + isSymbolicLink: () => false, + } as fs.Stats); + sinon.stub(fs.promises, 'realpath').resolves('/real/path/to/project'); + sinon.stub(fsPaths, 'arePathsSame').returns(false); + + const hasSymlinkParentStub = sinon.stub(utils, 'hasSymlinkParent').resolves(true); + + const result = await handleSymlinkAndRootDir('/symlink/to/project', ['arg1']); + + assert.strictEqual(hasSymlinkParentStub.calledOnceWith('/symlink/to/project'), true); + assert.deepStrictEqual(result, ['arg1', '--rootdir=/symlink/to/project']); + }); + + test('detects direct symlink without checking parent directories', async () => { + sinon.stub(fs.promises, 'lstat').resolves({ + isSymbolicLink: () => true, + } as fs.Stats); + sinon.stub(fs.promises, 'realpath').resolves('/real/path'); + + const hasSymlinkParentStub = sinon.stub(utils, 'hasSymlinkParent'); + + const result = await handleSymlinkAndRootDir('/symlink/path', ['arg1']); + + assert.strictEqual(hasSymlinkParentStub.called, false); + assert.deepStrictEqual(result, ['arg1', '--rootdir=/symlink/path']); + }); +});