diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index eff333a4cdd9..1550323ff8f8 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -15,6 +15,7 @@ import { CancellationTokenSource, Uri, EventEmitter, + TextDocument, } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; import { ICommandManager, IWorkspaceService } from '../../common/application/types'; @@ -48,6 +49,7 @@ import { WorkspaceTestAdapter } from './workspaceTestAdapter'; import { ITestDebugLauncher } from '../common/types'; import { IServiceContainer } from '../../ioc/types'; import { PythonResultResolver } from './common/resultResolver'; +import { onDidSaveTextDocument } from '../../common/vscodeApis/workspaceApis'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER]; @@ -209,7 +211,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (settings.testing.autoTestDiscoverOnSaveEnabled) { traceVerbose(`Testing: Setting up watcher for ${workspace.uri.fsPath}`); this.watchForSettingsChanges(workspace); - this.watchForTestContentChanges(workspace); + this.watchForTestContentChangeOnSave(); } }); } @@ -493,12 +495,23 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.disposables.push(watcher); this.disposables.push( - watcher.onDidChange((uri) => { - traceVerbose(`Testing: Trigger refresh after change in ${uri.fsPath}`); - this.sendTriggerTelemetry('watching'); - this.refreshData.trigger(uri, false); + onDidSaveTextDocument(async (doc: TextDocument) => { + const file = doc.fileName; + // refresh on any settings file save + if ( + file.includes('settings.json') || + file.includes('pytest.ini') || + file.includes('setup.cfg') || + file.includes('pyproject.toml') + ) { + traceVerbose(`Testing: Trigger refresh after saving ${doc.uri.fsPath}`); + this.sendTriggerTelemetry('watching'); + this.refreshData.trigger(doc.uri, false); + } }), ); + /* Keep both watchers for create and delete since config files can change test behavior without content + due to their impact on pythonPath. */ this.disposables.push( watcher.onDidCreate((uri) => { traceVerbose(`Testing: Trigger refresh after creating ${uri.fsPath}`); @@ -515,31 +528,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } - private watchForTestContentChanges(workspace: WorkspaceFolder): void { - const pattern = new RelativePattern(workspace, '**/*.py'); - const watcher = this.workspaceService.createFileSystemWatcher(pattern); - this.disposables.push(watcher); - - this.disposables.push( - watcher.onDidChange((uri) => { - traceVerbose(`Testing: Trigger refresh after change in ${uri.fsPath}`); - this.sendTriggerTelemetry('watching'); - // We want to invalidate tests for code change - this.refreshData.trigger(uri, true); - }), - ); - this.disposables.push( - watcher.onDidCreate((uri) => { - traceVerbose(`Testing: Trigger refresh after creating ${uri.fsPath}`); - this.sendTriggerTelemetry('watching'); - this.refreshData.trigger(uri, false); - }), - ); + private watchForTestContentChangeOnSave(): void { this.disposables.push( - watcher.onDidDelete((uri) => { - traceVerbose(`Testing: Trigger refresh after deleting in ${uri.fsPath}`); - this.sendTriggerTelemetry('watching'); - this.refreshData.trigger(uri, false); + onDidSaveTextDocument(async (doc: TextDocument) => { + if (doc.fileName.endsWith('.py')) { + traceVerbose(`Testing: Trigger refresh after saving ${doc.uri.fsPath}`); + this.sendTriggerTelemetry('watching'); + this.refreshData.trigger(doc.uri, false); + } }), ); }