From 3ee2aba7d9ad5fc3caf6cdd238b70f4d9b9fbfb4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:56:30 +0000 Subject: [PATCH 1/3] Initial plan From 2794490ab9636db7b33112c4f585985b159c03dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:03:58 +0000 Subject: [PATCH 2/3] Add cancellation support for dependency search progress Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> --- src/managers/builtin/pipUtils.ts | 1 + src/test/managers/builtin/pipUtils.unit.test.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/managers/builtin/pipUtils.ts b/src/managers/builtin/pipUtils.ts index d221f5a8..4cb4549c 100644 --- a/src/managers/builtin/pipUtils.ts +++ b/src/managers/builtin/pipUtils.ts @@ -178,6 +178,7 @@ export async function getProjectInstallable( { location: ProgressLocation.Notification, title: VenvManagerStrings.searchingDependencies, + cancellable: true, }, async (_progress, token) => { const results: Uri[] = ( diff --git a/src/test/managers/builtin/pipUtils.unit.test.ts b/src/test/managers/builtin/pipUtils.unit.test.ts index 076596e8..d08755c2 100644 --- a/src/test/managers/builtin/pipUtils.unit.test.ts +++ b/src/test/managers/builtin/pipUtils.unit.test.ts @@ -199,4 +199,19 @@ suite('Pip Utils - getProjectInstallable', () => { assert.ok(firstResult.uri, 'Should have a URI'); assert.ok(firstResult.uri.fsPath.startsWith(workspacePath), 'Should be in workspace directory'); }); + + test('should show cancellable progress notification', async () => { + // Arrange: Mock findFiles to return empty results + findFilesStub.resolves([]); + + // Act: Call getProjectInstallable + const workspacePath = Uri.file('/test/path/root').fsPath; + const projects = [{ name: 'workspace', uri: Uri.file(workspacePath) }]; + await getProjectInstallable(mockApi as PythonEnvironmentApi, projects); + + // Assert: Verify withProgress was called with cancellable option + assert.ok(withProgressStub.calledOnce, 'Should call withProgress once'); + const progressOptions = withProgressStub.firstCall.args[0] as ProgressOptions; + assert.strictEqual(progressOptions.cancellable, true, 'Progress should be cancellable'); + }); }); From 9caba3f9a5eb43ad9e3db64abb7b440ea32aee38 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:07:38 +0000 Subject: [PATCH 3/3] Add comprehensive cancellation test for dependency search Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> --- .../managers/builtin/pipUtils.unit.test.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/managers/builtin/pipUtils.unit.test.ts b/src/test/managers/builtin/pipUtils.unit.test.ts index d08755c2..fb484ad2 100644 --- a/src/test/managers/builtin/pipUtils.unit.test.ts +++ b/src/test/managers/builtin/pipUtils.unit.test.ts @@ -214,4 +214,41 @@ suite('Pip Utils - getProjectInstallable', () => { const progressOptions = withProgressStub.firstCall.args[0] as ProgressOptions; assert.strictEqual(progressOptions.cancellable, true, 'Progress should be cancellable'); }); + + test('should handle cancellation during file search', async () => { + // Arrange: Create a cancellation token that is already cancelled + const cancelledToken: CancellationToken = { + isCancellationRequested: true, + onCancellationRequested: () => ({ dispose: () => {} }), + }; + + // Mock withProgress to immediately call the callback with the cancelled token + withProgressStub.callsFake( + async ( + _options: ProgressOptions, + callback: ( + progress: Progress<{ message?: string; increment?: number }>, + token: CancellationToken, + ) => Thenable, + ) => { + return await callback({} as Progress<{ message?: string; increment?: number }>, cancelledToken); + }, + ); + + // Mock findFiles to check that token is passed + findFilesStub.callsFake((_pattern: string, _exclude: string, _maxResults: number, token: CancellationToken) => { + // Verify the cancellation token is passed to findFiles + assert.strictEqual(token, cancelledToken, 'Cancellation token should be passed to findFiles'); + return Promise.resolve([]); + }); + + // Act: Call getProjectInstallable + const workspacePath = Uri.file('/test/path/root').fsPath; + const projects = [{ name: 'workspace', uri: Uri.file(workspacePath) }]; + await getProjectInstallable(mockApi as PythonEnvironmentApi, projects); + + // Assert: Verify findFiles was called with the cancellation token + assert.ok(findFilesStub.called, 'findFiles should be called'); + assert.strictEqual(findFilesStub.callCount, 4, 'Should call findFiles 4 times for different patterns'); + }); });