From ec44f60fcbc3529070123d9e7e3937711040a93e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 1 Jul 2024 14:54:46 -0700 Subject: [PATCH 01/16] start adding test for repl command --- .vscode/launch.json | 2 +- src/client/common/application/commands.ts | 6 ++-- src/client/extensionActivation.ts | 5 ++-- src/client/repl/replCommands.ts | 7 +++-- src/test/.vscode/settings.json | 3 +- src/test/repl/replCommand.test.ts | 34 +++++++++++++++++++++++ 6 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 src/test/repl/replCommand.test.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 4dc107853fc6..3745b890dac7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -90,7 +90,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "VSC_PYTHON_CI_TEST_GREP": "" // Modify this to run a subset of the single workspace tests + "VSC_PYTHON_CI_TEST_GREP": "REPL - repl utility tests" // Modify this to run a subset of the single workspace tests }, "sourceMaps": true, "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index 4c00971ffdd5..a6ef615ddc78 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -38,8 +38,8 @@ interface ICommandNameWithoutArgumentTypeMapping { [Commands.Enable_SourceMap_Support]: []; [Commands.Exec_Selection_In_Terminal]: []; [Commands.Exec_Selection_In_Django_Shell]: []; - [Commands.Exec_In_REPL]: []; - [Commands.Exec_In_REPL_Enter]: []; + // [Commands.Exec_In_REPL]: []; + // [Commands.Exec_In_REPL_Enter]: []; [Commands.Create_Terminal]: []; [Commands.PickLocalProcess]: []; [Commands.ClearStorage]: []; @@ -98,6 +98,8 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu ['workbench.action.openIssueReporter']: [{ extensionId: string; issueBody: string }]; [Commands.GetSelectedInterpreterPath]: [{ workspaceFolder: string } | string[]]; [Commands.TriggerEnvironmentSelection]: [undefined | Uri]; + [Commands.Exec_In_REPL]: [undefined | Uri]; + [Commands.Exec_In_REPL_Enter]: [undefined | Uri]; [Commands.Exec_In_Terminal]: [undefined, Uri]; [Commands.Exec_In_Terminal_Icon]: [undefined, Uri]; [Commands.Debug_In_Terminal]: [Uri]; diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index f401f2493eed..6f2a4565299f 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -107,8 +107,9 @@ export function activateFeatures(ext: ExtensionState, _components: Components): pathUtils, ); const executionHelper = ext.legacyIOC.serviceContainer.get(ICodeExecutionHelper); - registerReplCommands(ext.disposables, interpreterService, executionHelper); - registerReplExecuteOnEnter(ext.disposables, interpreterService); + const commandManager = ext.legacyIOC.serviceContainer.get(ICommandManager); + registerReplCommands(ext.disposables, interpreterService, executionHelper, commandManager); + registerReplExecuteOnEnter(ext.disposables, interpreterService, commandManager); } /// ////////////////////////// diff --git a/src/client/repl/replCommands.ts b/src/client/repl/replCommands.ts index 7c4977b1aeff..c3f167ff51cc 100644 --- a/src/client/repl/replCommands.ts +++ b/src/client/repl/replCommands.ts @@ -1,5 +1,6 @@ import { commands, Uri, window } from 'vscode'; import { Disposable } from 'vscode-jsonrpc'; +import { ICommandManager } from '../common/application/types'; import { Commands } from '../common/constants'; import { noop } from '../common/utils/misc'; import { IInterpreterService } from '../interpreter/contracts'; @@ -24,9 +25,10 @@ export async function registerReplCommands( disposables: Disposable[], interpreterService: IInterpreterService, executionHelper: ICodeExecutionHelper, + commandManager: ICommandManager, ): Promise { disposables.push( - commands.registerCommand(Commands.Exec_In_REPL, async (uri: Uri) => { + commandManager.registerCommand(Commands.Exec_In_REPL, async (uri: Uri) => { const nativeREPLSetting = getSendToNativeREPLSetting(); if (!nativeREPLSetting) { @@ -64,9 +66,10 @@ export async function registerReplCommands( export async function registerReplExecuteOnEnter( disposables: Disposable[], interpreterService: IInterpreterService, + commandManager: ICommandManager, ): Promise { disposables.push( - commands.registerCommand(Commands.Exec_In_REPL_Enter, async (uri: Uri) => { + commandManager.registerCommand(Commands.Exec_In_REPL_Enter, async (uri: Uri) => { const interpreter = await interpreterService.getActiveInterpreter(uri); if (!interpreter) { commands.executeCommand(Commands.TriggerEnvironmentSelection, uri).then(noop, noop); diff --git a/src/test/.vscode/settings.json b/src/test/.vscode/settings.json index faeb48ffa29c..65764d5f6132 100644 --- a/src/test/.vscode/settings.json +++ b/src/test/.vscode/settings.json @@ -13,5 +13,6 @@ "python.linting.banditEnabled": false, // Don't set this to `Pylance`, for CI we want to use the LS that ships with the extension. "python.languageServer": "Jedi", - "python.pythonPath": "C:\\GIT\\s p\\vscode-python\\.venv\\Scripts\\python.exe" + "python.pythonPath": "C:\\GIT\\s p\\vscode-python\\.venv\\Scripts\\python.exe", + "python.defaultInterpreterPath": "/Users/anthonykim/Desktop/vscode-python/.venv/bin/python" } diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts new file mode 100644 index 000000000000..20b017ebe1a7 --- /dev/null +++ b/src/test/repl/replCommand.test.ts @@ -0,0 +1,34 @@ +// Create test suite and test cases for the `replUtils` module +import * as vscode from 'vscode'; +import * as TypeMoq from 'typemoq'; +import { Disposable } from 'vscode'; +import { IInterpreterService } from '../../client/interpreter/contracts'; +import { ICommandManager } from '../../client/common/application/types'; +import { ICodeExecutionHelper } from '../../client/terminals/types'; +import { registerReplCommands } from '../../client/repl/replCommands'; + +suite('REPL - register native repl command', () => { + let interpreterService: TypeMoq.IMock; + let commandManager: TypeMoq.IMock; + let executionHelper: TypeMoq.IMock; + setup(() => { + interpreterService = TypeMoq.Mock.ofType(); + commandManager = TypeMoq.Mock.ofType(); + executionHelper = TypeMoq.Mock.ofType(); + }); + + test('Ensure repl command is registered', async () => { + const disposable = TypeMoq.Mock.ofType(); + const disposableArray: Disposable[] = [disposable.object]; + + await registerReplCommands( + disposableArray, + interpreterService.object, + executionHelper.object, + commandManager.object, + ); + + // Check to see if the command was registered + commandManager.verify((c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + }); +}); From efd820d14b20c33286a04700d7bdbfba32eadc0c Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 1 Jul 2024 14:59:10 -0700 Subject: [PATCH 02/16] remove unused install diff match - local test --- package-lock.json | 11 +++++++++++ package.json | 3 ++- src/test/repl/replCommand.test.ts | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6419b9f3393e..e2cbf9909aef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@iarna/toml": "^2.2.5", "@vscode/extension-telemetry": "^0.8.4", "arch": "^2.1.0", + "diff-match-patch": "^1.0.5", "fs-extra": "^10.0.1", "glob": "^7.2.0", "hash.js": "^1.1.7", @@ -4636,6 +4637,11 @@ "node": ">=0.3.1" } }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "node_modules/diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -17602,6 +17608,11 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", diff --git a/package.json b/package.json index 3de58d434ec4..de00b4ba72e4 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "description": "%walkthrough.pythonWelcome.description%", "when": "workspacePlatform != webworker", "steps": [ - { + { "id": "python.createPythonFolder", "title": "%walkthrough.step.python.createPythonFolder.title%", "description": "%walkthrough.step.python.createPythonFolder.description%", @@ -1515,6 +1515,7 @@ "@iarna/toml": "^2.2.5", "@vscode/extension-telemetry": "^0.8.4", "arch": "^2.1.0", + "diff-match-patch": "^1.0.5", "fs-extra": "^10.0.1", "glob": "^7.2.0", "hash.js": "^1.1.7", diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 20b017ebe1a7..e5341db8de24 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -1,5 +1,4 @@ // Create test suite and test cases for the `replUtils` module -import * as vscode from 'vscode'; import * as TypeMoq from 'typemoq'; import { Disposable } from 'vscode'; import { IInterpreterService } from '../../client/interpreter/contracts'; From 542f5f151ba33a1a2b81176ef9fbb8f6f14460eb Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 2 Jul 2024 14:50:26 -0700 Subject: [PATCH 03/16] trials --- .vscode/launch.json | 2 +- src/test/repl/replCommand.test.ts | 85 ++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3745b890dac7..72b4cbc3492a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -90,7 +90,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "VSC_PYTHON_CI_TEST_GREP": "REPL - repl utility tests" // Modify this to run a subset of the single workspace tests + "VSC_PYTHON_CI_TEST_GREP": "REPL - register native repl command" // Modify this to run a subset of the single workspace tests }, "sourceMaps": true, "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index e5341db8de24..ae12184be35e 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -1,26 +1,45 @@ // Create test suite and test cases for the `replUtils` module import * as TypeMoq from 'typemoq'; import { Disposable } from 'vscode'; +import { It, Mock } from 'typemoq'; +import * as sinon from 'sinon'; import { IInterpreterService } from '../../client/interpreter/contracts'; import { ICommandManager } from '../../client/common/application/types'; import { ICodeExecutionHelper } from '../../client/terminals/types'; -import { registerReplCommands } from '../../client/repl/replCommands'; +import * as replCommands from '../../client/repl/replCommands'; +import * as replUtils from '../../client/repl/replUtils'; +// import { executeInTerminal, getActiveInterpreter, getSendToNativeREPLSetting } from '../../client/repl/replUtils'; suite('REPL - register native repl command', () => { let interpreterService: TypeMoq.IMock; let commandManager: TypeMoq.IMock; let executionHelper: TypeMoq.IMock; + let getSendToNativeREPLSettingStub: sinon.SinonStub; + let registerCommandSpy: sinon.SinonSpy; setup(() => { interpreterService = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); executionHelper = TypeMoq.Mock.ofType(); + // Define the registerCommand method on the mock object + commandManager + .setup((cm) => cm.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) + .returns(() => TypeMoq.Mock.ofType().object); + + getSendToNativeREPLSettingStub = sinon.stub(replUtils, 'getSendToNativeREPLSetting'); + getSendToNativeREPLSettingStub.returns(false); + + registerCommandSpy = sinon.spy(commandManager.object, 'registerCommand'); + }); + + teardown(() => { + sinon.restore(); }); test('Ensure repl command is registered', async () => { const disposable = TypeMoq.Mock.ofType(); const disposableArray: Disposable[] = [disposable.object]; - await registerReplCommands( + await replCommands.registerReplCommands( disposableArray, interpreterService.object, executionHelper.object, @@ -30,4 +49,66 @@ suite('REPL - register native repl command', () => { // Check to see if the command was registered commandManager.verify((c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); }); + + // test('Ensure execInTerminal is not called if REPL setting is true', async () => { + // const disposable = TypeMoq.Mock.ofType(); + // const disposableArray: Disposable[] = [disposable.object]; + // const getSendToNativeREPLSettingStub = Mock.ofInstance(getSendToNativeREPLSetting); + // const getActiveInterpreterStub = Mock.ofInstance(getActiveInterpreter); + // getSendToNativeREPLSettingStub.setup((x) => x()).returns(() => true); + // getActiveInterpreterStub + // .setup((x) => x(TypeMoq.It.isAny(), TypeMoq.It.isAny())) + // .returns(async () => Promise.resolve(undefined)); + + // const executeInTerminalStub = Mock.ofInstance(executeInTerminal); + + // // Inject the stubs into the module + // await replCommands.registerReplCommands( + // disposableArray, + // interpreterService.object, + // executionHelper.object, + // commandManager.object, + // ); + + // // Extract the registered command handler and invoke it + // commandManager.verify((c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + + // // Verify the function interactions + + // executeInTerminalStub.verify((x) => x(), TypeMoq.Times.never()); + // }); + + test('Ensure execInTerminal is called if REPL setting is false', async () => { + const disposable = TypeMoq.Mock.ofType(); + const disposableArray: Disposable[] = [disposable.object]; + + // const executeInTerminalStub = sinon.stub(replUtils, 'executeInTerminal').returns(Promise.resolve()); + + // Replace the original functions with the stubs + // sinon.replace(replUtils, 'getSendToNativeREPLSetting', getSendToNativeREPLSettingStub); + // sinon.replace(replUtils, 'executeInTerminal', executeInTerminalStub); + + await replCommands.registerReplCommands( + disposableArray, + interpreterService.object, + executionHelper.object, + commandManager.object, + ); + + // Extract the registered command handler and invoke it + const call = registerCommandSpy.getCall(0); + if (call) { + const commandHandler = call.args[1]; + await commandHandler(); + } else { + throw new Error('registerCommand was not called'); + } + + // Verify the function interactions + sinon.assert.calledOnce(getSendToNativeREPLSettingStub); + // sinon.assert.calledOnce(executeInTerminalStub); + + // Restore the original functions + sinon.restore(); + }); }); From 236d41f7d81b68368bcff20db12941146c47e1d6 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 2 Jul 2024 15:02:44 -0700 Subject: [PATCH 04/16] got two working --- src/test/repl/replCommand.test.ts | 75 ++++++++++--------------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index ae12184be35e..a9ff91214c63 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -3,11 +3,13 @@ import * as TypeMoq from 'typemoq'; import { Disposable } from 'vscode'; import { It, Mock } from 'typemoq'; import * as sinon from 'sinon'; +import { expect } from 'chai'; import { IInterpreterService } from '../../client/interpreter/contracts'; import { ICommandManager } from '../../client/common/application/types'; import { ICodeExecutionHelper } from '../../client/terminals/types'; import * as replCommands from '../../client/repl/replCommands'; import * as replUtils from '../../client/repl/replUtils'; +import { Commands } from '../../client/common/constants'; // import { executeInTerminal, getActiveInterpreter, getSendToNativeREPLSetting } from '../../client/repl/replUtils'; suite('REPL - register native repl command', () => { @@ -47,68 +49,39 @@ suite('REPL - register native repl command', () => { ); // Check to see if the command was registered - commandManager.verify((c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); + commandManager.verify( + (c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), + TypeMoq.Times.atLeastOnce(), + ); }); - // test('Ensure execInTerminal is not called if REPL setting is true', async () => { - // const disposable = TypeMoq.Mock.ofType(); - // const disposableArray: Disposable[] = [disposable.object]; - // const getSendToNativeREPLSettingStub = Mock.ofInstance(getSendToNativeREPLSetting); - // const getActiveInterpreterStub = Mock.ofInstance(getActiveInterpreter); - // getSendToNativeREPLSettingStub.setup((x) => x()).returns(() => true); - // getActiveInterpreterStub - // .setup((x) => x(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - // .returns(async () => Promise.resolve(undefined)); - - // const executeInTerminalStub = Mock.ofInstance(executeInTerminal); - - // // Inject the stubs into the module - // await replCommands.registerReplCommands( - // disposableArray, - // interpreterService.object, - // executionHelper.object, - // commandManager.object, - // ); - - // // Extract the registered command handler and invoke it - // commandManager.verify((c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once()); - - // // Verify the function interactions - - // executeInTerminalStub.verify((x) => x(), TypeMoq.Times.never()); - // }); - - test('Ensure execInTerminal is called if REPL setting is false', async () => { + test('Ensure getSendToNativeREPLSetting is called', async () => { const disposable = TypeMoq.Mock.ofType(); const disposableArray: Disposable[] = [disposable.object]; - // const executeInTerminalStub = sinon.stub(replUtils, 'executeInTerminal').returns(Promise.resolve()); - - // Replace the original functions with the stubs - // sinon.replace(replUtils, 'getSendToNativeREPLSetting', getSendToNativeREPLSettingStub); - // sinon.replace(replUtils, 'executeInTerminal', executeInTerminalStub); - - await replCommands.registerReplCommands( - disposableArray, + let commandHandler: undefined | (() => Promise); + commandManager + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .setup((c) => c.registerCommand as any) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .returns(() => (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { + if (command === Commands.Exec_In_REPL) { + commandHandler = callback; + } + // eslint-disable-next-line no-void + return { dispose: () => void 0 }; + }); + replCommands.registerReplCommands( + [TypeMoq.Mock.ofType().object], interpreterService.object, executionHelper.object, commandManager.object, ); - // Extract the registered command handler and invoke it - const call = registerCommandSpy.getCall(0); - if (call) { - const commandHandler = call.args[1]; - await commandHandler(); - } else { - throw new Error('registerCommand was not called'); - } + expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); - // Verify the function interactions - sinon.assert.calledOnce(getSendToNativeREPLSettingStub); - // sinon.assert.calledOnce(executeInTerminalStub); + await commandHandler!(); - // Restore the original functions - sinon.restore(); + sinon.assert.calledOnce(getSendToNativeREPLSettingStub); }); }); From d247a920b6855c7056c68a4118920cbc53d70985 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Thu, 11 Jul 2024 23:24:24 -0700 Subject: [PATCH 05/16] stop complaining about variable --- src/test/repl/replCommand.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index a9ff91214c63..5ce0a5d8de72 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -1,7 +1,6 @@ // Create test suite and test cases for the `replUtils` module import * as TypeMoq from 'typemoq'; import { Disposable } from 'vscode'; -import { It, Mock } from 'typemoq'; import * as sinon from 'sinon'; import { expect } from 'chai'; import { IInterpreterService } from '../../client/interpreter/contracts'; @@ -17,7 +16,8 @@ suite('REPL - register native repl command', () => { let commandManager: TypeMoq.IMock; let executionHelper: TypeMoq.IMock; let getSendToNativeREPLSettingStub: sinon.SinonStub; - let registerCommandSpy: sinon.SinonSpy; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test setup(() => { interpreterService = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); @@ -56,8 +56,8 @@ suite('REPL - register native repl command', () => { }); test('Ensure getSendToNativeREPLSetting is called', async () => { - const disposable = TypeMoq.Mock.ofType(); - const disposableArray: Disposable[] = [disposable.object]; + // const disposable = TypeMoq.Mock.ofType(); + // const disposableArray: Disposable[] = [disposable.object]; let commandHandler: undefined | (() => Promise); commandManager From 22898cbcc405e6050a85435ee849d5a4c0b7f617 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 12:24:44 -0400 Subject: [PATCH 06/16] registerCommandSpy is not unused --- src/test/repl/replCommand.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 5ce0a5d8de72..6fb5169f3594 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -17,7 +17,7 @@ suite('REPL - register native repl command', () => { let executionHelper: TypeMoq.IMock; let getSendToNativeREPLSettingStub: sinon.SinonStub; // eslint-disable-next-line @typescript-eslint/no-unused-vars - let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test + let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line #tslint:disable-line setup(() => { interpreterService = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); From 913a1ecd835ba4bfbcbcd0c9c264f0e1edb8644b Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 12:27:45 -0400 Subject: [PATCH 07/16] why is it not suppressing --- src/test/repl/replCommand.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 6fb5169f3594..74a999783906 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -17,7 +17,7 @@ suite('REPL - register native repl command', () => { let executionHelper: TypeMoq.IMock; let getSendToNativeREPLSettingStub: sinon.SinonStub; // eslint-disable-next-line @typescript-eslint/no-unused-vars - let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line #tslint:disable-line + let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line // @ts-ignore: TS6133 setup(() => { interpreterService = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); From d04d83dfa464012b3f2331c83e8ee10c30ba1b36 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 12:32:33 -0400 Subject: [PATCH 08/16] ignore please --- src/test/repl/replCommand.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 74a999783906..f1c82777fed0 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -16,6 +16,7 @@ suite('REPL - register native repl command', () => { let commandManager: TypeMoq.IMock; let executionHelper: TypeMoq.IMock; let getSendToNativeREPLSettingStub: sinon.SinonStub; + // @ts-ignore: TS6133 // eslint-disable-next-line @typescript-eslint/no-unused-vars let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line // @ts-ignore: TS6133 setup(() => { From f0bd1e9c8541bf3a1b7f137288248267f29dccca Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 15:15:23 -0400 Subject: [PATCH 09/16] test sending to terminal when native setting is false --- src/test/repl/replCommand.test.ts | 34 ++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index f1c82777fed0..55aee3eaaff4 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -19,6 +19,7 @@ suite('REPL - register native repl command', () => { // @ts-ignore: TS6133 // eslint-disable-next-line @typescript-eslint/no-unused-vars let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line // @ts-ignore: TS6133 + let executeInTerminalStub: sinon.SinonStub; setup(() => { interpreterService = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); @@ -30,7 +31,8 @@ suite('REPL - register native repl command', () => { getSendToNativeREPLSettingStub = sinon.stub(replUtils, 'getSendToNativeREPLSetting'); getSendToNativeREPLSettingStub.returns(false); - + executeInTerminalStub = sinon.stub(replUtils, 'executeInTerminal'); + executeInTerminalStub.returns(Promise.resolve()); registerCommandSpy = sinon.spy(commandManager.object, 'registerCommand'); }); @@ -85,4 +87,34 @@ suite('REPL - register native repl command', () => { sinon.assert.calledOnce(getSendToNativeREPLSettingStub); }); + + test('Ensure executeInTerminal is called when getSendToNativeREPLSetting returns false', async () => { + getSendToNativeREPLSettingStub.returns(false); + + let commandHandler: undefined | (() => Promise); + commandManager + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .setup((c) => c.registerCommand as any) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .returns(() => (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { + if (command === Commands.Exec_In_REPL) { + commandHandler = callback; + } + // eslint-disable-next-line no-void + return { dispose: () => void 0 }; + }); + replCommands.registerReplCommands( + [TypeMoq.Mock.ofType().object], + interpreterService.object, + executionHelper.object, + commandManager.object, + ); + + expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); + + await commandHandler!(); + + // Check to see if executeInTerminal was called + sinon.assert.calledOnce(executeInTerminalStub); + }); }); From 7d339e90da871e15f765080db118d93709c53981 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 17:02:31 -0400 Subject: [PATCH 10/16] ensure getNativeREPL gets called --- src/test/repl/replCommand.test.ts | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 55aee3eaaff4..62c24d9b6143 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -8,7 +8,9 @@ import { ICommandManager } from '../../client/common/application/types'; import { ICodeExecutionHelper } from '../../client/terminals/types'; import * as replCommands from '../../client/repl/replCommands'; import * as replUtils from '../../client/repl/replUtils'; +import * as nativeRepl from '../../client/repl/nativeRepl'; import { Commands } from '../../client/common/constants'; +import { PythonEnvironment } from '../../client/pythonEnvironments/info'; // import { executeInTerminal, getActiveInterpreter, getSendToNativeREPLSetting } from '../../client/repl/replUtils'; suite('REPL - register native repl command', () => { @@ -20,8 +22,12 @@ suite('REPL - register native repl command', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line // @ts-ignore: TS6133 let executeInTerminalStub: sinon.SinonStub; + let getNativeReplStub: sinon.SinonStub; setup(() => { interpreterService = TypeMoq.Mock.ofType(); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); commandManager = TypeMoq.Mock.ofType(); executionHelper = TypeMoq.Mock.ofType(); // Define the registerCommand method on the mock object @@ -117,4 +123,33 @@ suite('REPL - register native repl command', () => { // Check to see if executeInTerminal was called sinon.assert.calledOnce(executeInTerminalStub); }); + + test('Ensure we call getNativeREPL() when interpreter exist', async () => { + getSendToNativeREPLSettingStub.returns(true); + getNativeReplStub = sinon.stub(nativeRepl, 'getNativeRepl'); + + let commandHandler: undefined | ((uri: string) => Promise); + commandManager + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .setup((c) => c.registerCommand as any) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .returns(() => (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { + if (command === Commands.Exec_In_REPL) { + commandHandler = callback; + } + // eslint-disable-next-line no-void + return { dispose: () => void 0 }; + }); + replCommands.registerReplCommands( + [TypeMoq.Mock.ofType().object], + interpreterService.object, + executionHelper.object, + commandManager.object, + ); + + expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); + + await commandHandler!('uri'); + sinon.assert.calledOnce(getNativeReplStub); + }); }); From 90b729bd5052fca99ebc89e46b1e6d6faf45d161 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Mon, 29 Jul 2024 17:23:23 -0400 Subject: [PATCH 11/16] clean up abit --- src/test/repl/replCommand.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 62c24d9b6143..4869e0f2184b 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -11,7 +11,6 @@ import * as replUtils from '../../client/repl/replUtils'; import * as nativeRepl from '../../client/repl/nativeRepl'; import { Commands } from '../../client/common/constants'; import { PythonEnvironment } from '../../client/pythonEnvironments/info'; -// import { executeInTerminal, getActiveInterpreter, getSendToNativeREPLSetting } from '../../client/repl/replUtils'; suite('REPL - register native repl command', () => { let interpreterService: TypeMoq.IMock; @@ -20,7 +19,7 @@ suite('REPL - register native repl command', () => { let getSendToNativeREPLSettingStub: sinon.SinonStub; // @ts-ignore: TS6133 // eslint-disable-next-line @typescript-eslint/no-unused-vars - let registerCommandSpy: sinon.SinonSpy; // Need this for getSendToNativeREPLSetting test #ignore unused variable error on this line // @ts-ignore: TS6133 + let registerCommandSpy: sinon.SinonSpy; let executeInTerminalStub: sinon.SinonStub; let getNativeReplStub: sinon.SinonStub; setup(() => { @@ -30,7 +29,6 @@ suite('REPL - register native repl command', () => { .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); commandManager = TypeMoq.Mock.ofType(); executionHelper = TypeMoq.Mock.ofType(); - // Define the registerCommand method on the mock object commandManager .setup((cm) => cm.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) .returns(() => TypeMoq.Mock.ofType().object); From db89a9cdb7a293c0803dd331755ba10a59c7367d Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Tue, 30 Jul 2024 15:06:35 -0400 Subject: [PATCH 12/16] ensure getNativeREPL doesnt get called when interpreter does not exist --- src/test/repl/replCommand.test.ts | 62 +++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 4869e0f2184b..2555d6bed0cb 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -24,9 +24,9 @@ suite('REPL - register native repl command', () => { let getNativeReplStub: sinon.SinonStub; setup(() => { interpreterService = TypeMoq.Mock.ofType(); - interpreterService - .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); + // interpreterService + // .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + // .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); commandManager = TypeMoq.Mock.ofType(); executionHelper = TypeMoq.Mock.ofType(); commandManager @@ -42,9 +42,19 @@ suite('REPL - register native repl command', () => { teardown(() => { sinon.restore(); + // disposables.forEach((disposable) => { + // if (disposable) { + // disposable.dispose(); + // } + // }); + + // disposables = []; }); test('Ensure repl command is registered', async () => { + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); const disposable = TypeMoq.Mock.ofType(); const disposableArray: Disposable[] = [disposable.object]; @@ -63,6 +73,9 @@ suite('REPL - register native repl command', () => { }); test('Ensure getSendToNativeREPLSetting is called', async () => { + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); // const disposable = TypeMoq.Mock.ofType(); // const disposableArray: Disposable[] = [disposable.object]; @@ -93,6 +106,9 @@ suite('REPL - register native repl command', () => { }); test('Ensure executeInTerminal is called when getSendToNativeREPLSetting returns false', async () => { + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); getSendToNativeREPLSettingStub.returns(false); let commandHandler: undefined | (() => Promise); @@ -123,6 +139,9 @@ suite('REPL - register native repl command', () => { }); test('Ensure we call getNativeREPL() when interpreter exist', async () => { + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); getSendToNativeREPLSettingStub.returns(true); getNativeReplStub = sinon.stub(nativeRepl, 'getNativeRepl'); @@ -150,4 +169,41 @@ suite('REPL - register native repl command', () => { await commandHandler!('uri'); sinon.assert.calledOnce(getNativeReplStub); }); + + test('Ensure we do not call getNativeREPL() when interpreter does not exist', async () => { + getNativeReplStub = sinon.stub(nativeRepl, 'getNativeRepl'); + getSendToNativeREPLSettingStub.returns(true); + + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(undefined)); + + let commandHandler: undefined | ((uri: string) => Promise); + commandManager + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .setup((c) => c.registerCommand as any) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .returns(() => (command: string, callback: (...args: any[]) => any, _thisArg?: any) => { + if (command === Commands.Exec_In_REPL) { + commandHandler = callback; + } + // eslint-disable-next-line no-void + return { dispose: () => void 0 }; + }); + interpreterService + .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) + .returns(() => Promise.resolve(undefined)); + + replCommands.registerReplCommands( + [TypeMoq.Mock.ofType().object], + interpreterService.object, + executionHelper.object, + commandManager.object, + ); + + expect(commandHandler).not.to.be.an('undefined', 'Command handler not initialized'); + + await commandHandler!('uri'); + sinon.assert.notCalled(getNativeReplStub); + }); }); From ca412c6623cb0eae5c3c5a00b4f15b8b26c8c48e Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 31 Jul 2024 13:31:59 -0400 Subject: [PATCH 13/16] handle disposable properly --- src/test/repl/replCommand.test.ts | 33 +++++++++++++------------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/test/repl/replCommand.test.ts b/src/test/repl/replCommand.test.ts index 2555d6bed0cb..444b8e5f16b9 100644 --- a/src/test/repl/replCommand.test.ts +++ b/src/test/repl/replCommand.test.ts @@ -22,11 +22,10 @@ suite('REPL - register native repl command', () => { let registerCommandSpy: sinon.SinonSpy; let executeInTerminalStub: sinon.SinonStub; let getNativeReplStub: sinon.SinonStub; + let disposable: TypeMoq.IMock; + let disposableArray: Disposable[] = []; setup(() => { interpreterService = TypeMoq.Mock.ofType(); - // interpreterService - // .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - // .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); commandManager = TypeMoq.Mock.ofType(); executionHelper = TypeMoq.Mock.ofType(); commandManager @@ -38,25 +37,25 @@ suite('REPL - register native repl command', () => { executeInTerminalStub = sinon.stub(replUtils, 'executeInTerminal'); executeInTerminalStub.returns(Promise.resolve()); registerCommandSpy = sinon.spy(commandManager.object, 'registerCommand'); + disposable = TypeMoq.Mock.ofType(); + disposableArray = [disposable.object]; }); teardown(() => { sinon.restore(); - // disposables.forEach((disposable) => { - // if (disposable) { - // disposable.dispose(); - // } - // }); + disposableArray.forEach((d) => { + if (d) { + d.dispose(); + } + }); - // disposables = []; + disposableArray = []; }); test('Ensure repl command is registered', async () => { interpreterService .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); - const disposable = TypeMoq.Mock.ofType(); - const disposableArray: Disposable[] = [disposable.object]; await replCommands.registerReplCommands( disposableArray, @@ -65,7 +64,6 @@ suite('REPL - register native repl command', () => { commandManager.object, ); - // Check to see if the command was registered commandManager.verify( (c) => c.registerCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce(), @@ -76,8 +74,6 @@ suite('REPL - register native repl command', () => { interpreterService .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); - // const disposable = TypeMoq.Mock.ofType(); - // const disposableArray: Disposable[] = [disposable.object]; let commandHandler: undefined | (() => Promise); commandManager @@ -92,7 +88,7 @@ suite('REPL - register native repl command', () => { return { dispose: () => void 0 }; }); replCommands.registerReplCommands( - [TypeMoq.Mock.ofType().object], + disposableArray, interpreterService.object, executionHelper.object, commandManager.object, @@ -124,7 +120,7 @@ suite('REPL - register native repl command', () => { return { dispose: () => void 0 }; }); replCommands.registerReplCommands( - [TypeMoq.Mock.ofType().object], + disposableArray, interpreterService.object, executionHelper.object, commandManager.object, @@ -134,7 +130,6 @@ suite('REPL - register native repl command', () => { await commandHandler!(); - // Check to see if executeInTerminal was called sinon.assert.calledOnce(executeInTerminalStub); }); @@ -158,7 +153,7 @@ suite('REPL - register native repl command', () => { return { dispose: () => void 0 }; }); replCommands.registerReplCommands( - [TypeMoq.Mock.ofType().object], + disposableArray, interpreterService.object, executionHelper.object, commandManager.object, @@ -195,7 +190,7 @@ suite('REPL - register native repl command', () => { .returns(() => Promise.resolve(undefined)); replCommands.registerReplCommands( - [TypeMoq.Mock.ofType().object], + disposableArray, interpreterService.object, executionHelper.object, commandManager.object, From dea7dd691b8740cceb658c4d28ca8ad159c92223 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 31 Jul 2024 14:55:42 -0400 Subject: [PATCH 14/16] clean up --- .vscode/launch.json | 2 +- package.json | 1 - src/test/.vscode/settings.json | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 72b4cbc3492a..4dc107853fc6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -90,7 +90,7 @@ "--extensionTestsPath=${workspaceFolder}/out/test" ], "env": { - "VSC_PYTHON_CI_TEST_GREP": "REPL - register native repl command" // Modify this to run a subset of the single workspace tests + "VSC_PYTHON_CI_TEST_GREP": "" // Modify this to run a subset of the single workspace tests }, "sourceMaps": true, "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], diff --git a/package.json b/package.json index de00b4ba72e4..8ce734a6ba0a 100644 --- a/package.json +++ b/package.json @@ -1515,7 +1515,6 @@ "@iarna/toml": "^2.2.5", "@vscode/extension-telemetry": "^0.8.4", "arch": "^2.1.0", - "diff-match-patch": "^1.0.5", "fs-extra": "^10.0.1", "glob": "^7.2.0", "hash.js": "^1.1.7", diff --git a/src/test/.vscode/settings.json b/src/test/.vscode/settings.json index 65764d5f6132..36d3fd77195e 100644 --- a/src/test/.vscode/settings.json +++ b/src/test/.vscode/settings.json @@ -14,5 +14,4 @@ // Don't set this to `Pylance`, for CI we want to use the LS that ships with the extension. "python.languageServer": "Jedi", "python.pythonPath": "C:\\GIT\\s p\\vscode-python\\.venv\\Scripts\\python.exe", - "python.defaultInterpreterPath": "/Users/anthonykim/Desktop/vscode-python/.venv/bin/python" } From 580b8e008ee3bf778f37ec9bc2981f6effa9ab7f Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 31 Jul 2024 14:57:43 -0400 Subject: [PATCH 15/16] more cleanup --- src/client/common/application/commands.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index a6ef615ddc78..388bcf8052fa 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -38,8 +38,6 @@ interface ICommandNameWithoutArgumentTypeMapping { [Commands.Enable_SourceMap_Support]: []; [Commands.Exec_Selection_In_Terminal]: []; [Commands.Exec_Selection_In_Django_Shell]: []; - // [Commands.Exec_In_REPL]: []; - // [Commands.Exec_In_REPL_Enter]: []; [Commands.Create_Terminal]: []; [Commands.PickLocalProcess]: []; [Commands.ClearStorage]: []; From 14675b1c6011d4f05aee58b31957f4e2c512b122 Mon Sep 17 00:00:00 2001 From: Anthony Kim Date: Wed, 31 Jul 2024 14:59:53 -0400 Subject: [PATCH 16/16] remove urnncessary comma --- src/test/.vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/.vscode/settings.json b/src/test/.vscode/settings.json index 36d3fd77195e..faeb48ffa29c 100644 --- a/src/test/.vscode/settings.json +++ b/src/test/.vscode/settings.json @@ -13,5 +13,5 @@ "python.linting.banditEnabled": false, // Don't set this to `Pylance`, for CI we want to use the LS that ships with the extension. "python.languageServer": "Jedi", - "python.pythonPath": "C:\\GIT\\s p\\vscode-python\\.venv\\Scripts\\python.exe", + "python.pythonPath": "C:\\GIT\\s p\\vscode-python\\.venv\\Scripts\\python.exe" }