From fe1d0437a112e17ce13e0e400ea951e8b9b09cff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:20:34 +0000 Subject: [PATCH 1/2] Initial plan From f1fb6ab0f6151359de77c80cb06a173720a8ce2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:28:54 +0000 Subject: [PATCH 2/2] Test pytest cancellation kills subprocess --- .../pytest/pytestExecutionAdapter.ts | 1 + .../pytestExecutionAdapter.unit.test.ts | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 5215a7248630..a982037d90e5 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -246,6 +246,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { }); const result = execService?.execObservable(runArgs, spawnOptions); + resultProc = result?.proc; // Take all output from the subprocess and add it to the test output channel. This will be the pytest output. // Displays output to user and ensure the subprocess doesn't run into buffer overflow. diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index 40c701b22641..9b83f1bbd088 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import * as assert from 'assert'; -import { TestRun, Uri, TestRunProfileKind, DebugSessionOptions } from 'vscode'; +import { CancellationTokenSource, TestRun, Uri, TestRunProfileKind, DebugSessionOptions } from 'vscode'; import * as typeMoq from 'typemoq'; import * as sinon from 'sinon'; import * as path from 'path'; @@ -182,6 +182,28 @@ suite('pytest test execution adapter', () => { typeMoq.Times.once(), ); }); + test('cancelling pytest execution kills the subprocess', async () => { + utilsWriteTestIdsFileStub.resolves('testIdPipe-mockName'); + utilsStartRunResultNamedPipeStub.callsFake((_callback, deferredTillServerClose, token) => { + token?.onCancellationRequested(() => deferredTillServerClose.resolve()); + return Promise.resolve('runResultPipe-mockName'); + }); + const cancellationToken = new CancellationTokenSource(); + const testRun = typeMoq.Mock.ofType(); + testRun.setup((t) => t.token).returns(() => cancellationToken.token); + const killStub = sinon.stub(mockProc, 'kill'); + const uri = Uri.file(myTestPath); + adapter = new PytestTestExecutionAdapter(configService); + + const execution = adapter.runTests(uri, [], TestRunProfileKind.Run, testRun.object, execFactory.object); + await deferred4.promise; + cancellationToken.cancel(); + + sinon.assert.calledOnce(killStub); + mockProc.emit('close', 0, null); + await execution; + cancellationToken.dispose(); + }); test('pytest execution respects settings.testing.cwd when present', async () => { const deferred2 = createDeferred(); const deferred3 = createDeferred();