diff --git a/package.json b/package.json index 716335f73767..6342e1327205 100644 --- a/package.json +++ b/package.json @@ -603,88 +603,6 @@ "scope": "window", "type": "string" }, - "python.linting.banditArgs": { - "default": [], - "description": "%python.linting.banditArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.banditArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.banditArgs.deprecationMessage%" - }, - "python.linting.banditEnabled": { - "default": false, - "description": "%python.linting.banditEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.banditArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.banditArgs.deprecationMessage%" - }, - "python.linting.banditPath": { - "default": "bandit", - "description": "%python.linting.banditPath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.banditPath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.banditPath.deprecationMessage%" - }, - "python.linting.cwd": { - "default": null, - "description": "%python.linting.cwd.description%", - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.cwd.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.cwd.deprecationMessage%" - }, - "python.linting.enabled": { - "default": true, - "description": "%python.linting.enabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.enabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.enabled.deprecationMessage%" - }, - "python.linting.flake8Args": { - "default": [], - "description": "%python.linting.flake8Args.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.flake8Args.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8Args.deprecationMessage%" - }, - "python.linting.flake8CategorySeverity.E": { - "default": "Error", - "description": "%python.linting.flake8CategorySeverity.E.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.flake8CategorySeverity.E.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8CategorySeverity.E.deprecationMessage%" - }, - "python.linting.flake8CategorySeverity.F": { - "default": "Error", - "description": "%python.linting.flake8CategorySeverity.F.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.flake8CategorySeverity.F.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8CategorySeverity.F.deprecationMessage%" - }, "python.interpreter.infoVisibility": { "default": "onPythonRelated", "description": "%python.interpreter.infoVisibility.description%", @@ -701,360 +619,6 @@ "scope": "machine", "type": "string" }, - "python.linting.flake8CategorySeverity.W": { - "default": "Warning", - "description": "%python.linting.flake8CategorySeverity.W.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.flake8CategorySeverity.W.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8CategorySeverity.W.deprecationMessage%" - }, - "python.linting.flake8Enabled": { - "default": false, - "description": "%python.linting.flake8Enabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.flake8Enabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8Enabled.deprecationMessage%" - }, - "python.linting.flake8Path": { - "default": "flake8", - "description": "%python.linting.flake8Path.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.flake8Path.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.flake8Path.deprecationMessage%" - }, - "python.linting.ignorePatterns": { - "default": [ - "**/site-packages/**/*.py", - ".vscode/*.py" - ], - "description": "%python.linting.ignorePatterns.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "uniqueItems": true, - "markdownDeprecationMessage": "%python.linting.ignorePatterns.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.ignorePatterns.deprecationMessage%" - }, - "python.linting.lintOnSave": { - "default": true, - "description": "%python.linting.lintOnSave.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.lintOnSave.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.lintOnSave.deprecationMessage%" - }, - "python.linting.maxNumberOfProblems": { - "default": 100, - "description": "%python.linting.maxNumberOfProblems.description%", - "scope": "resource", - "type": "number", - "markdownDeprecationMessage": "%python.linting.maxNumberOfProblems.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.maxNumberOfProblems.deprecationMessage%" - }, - "python.linting.mypyArgs": { - "default": [ - "--follow-imports=silent", - "--ignore-missing-imports", - "--show-column-numbers", - "--no-pretty" - ], - "description": "%python.linting.mypyArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.mypyArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.mypyArgs.deprecationMessage%" - }, - "python.linting.mypyCategorySeverity.error": { - "default": "Error", - "description": "%python.linting.mypyCategorySeverity.error.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.mypyCategorySeverity.error.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.mypyCategorySeverity.error.deprecationMessage%" - }, - "python.linting.mypyCategorySeverity.note": { - "default": "Information", - "description": "%python.linting.mypyCategorySeverity.note.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.mypyCategorySeverity.note.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.mypyCategorySeverity.note.deprecationMessage%" - }, - "python.linting.mypyEnabled": { - "default": false, - "description": "%python.linting.mypyEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.mypyEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.mypyEnabled.deprecationMessage%" - }, - "python.linting.mypyPath": { - "default": "mypy", - "description": "%python.linting.mypyPath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.mypyPath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.mypyPath.deprecationMessage%" - }, - "python.linting.prospectorArgs": { - "default": [], - "description": "%python.linting.prospectorArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.prospectorArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.prospectorArgs.deprecationMessage%" - }, - "python.linting.prospectorEnabled": { - "default": false, - "description": "%python.linting.prospectorEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.prospectorEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.prospectorEnabled.deprecationMessage%" - }, - "python.linting.prospectorPath": { - "default": "prospector", - "description": "%python.linting.prospectorPath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.prospectorPath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.prospectorPath.deprecationMessage%" - }, - "python.linting.pycodestyleArgs": { - "default": [], - "description": "%python.linting.pycodestyleArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.pycodestyleArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pycodestyleArgs.deprecationMessage%" - }, - "python.linting.pycodestyleCategorySeverity.E": { - "default": "Error", - "description": "%python.linting.pycodestyleCategorySeverity.E.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pycodestyleCategorySeverity.E.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pycodestyleCategorySeverity.E.deprecationMessage%" - }, - "python.linting.pycodestyleCategorySeverity.W": { - "default": "Warning", - "description": "%python.linting.pycodestyleCategorySeverity.W.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pycodestyleCategorySeverity.W.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pycodestyleCategorySeverity.W.deprecationMessage%" - }, - "python.linting.pycodestyleEnabled": { - "default": false, - "description": "%python.linting.pycodestyleEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.pycodestyleEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pycodestyleEnabled.deprecationMessage%" - }, - "python.linting.pycodestylePath": { - "default": "pycodestyle", - "description": "%python.linting.pycodestylePath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pycodestylePath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pycodestylePath.deprecationMessage%" - }, - "python.linting.pydocstyleArgs": { - "default": [], - "description": "%python.linting.pydocstyleArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.pydocstyleArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pydocstyleArgs.deprecationMessage%" - }, - "python.linting.pydocstyleEnabled": { - "default": false, - "description": "%python.linting.pydocstyleEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.pydocstyleEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pydocstyleEnabled.deprecationMessage%" - }, - "python.linting.pydocstylePath": { - "default": "pydocstyle", - "description": "%python.linting.pydocstylePath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pydocstylePath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pydocstylePath.deprecationMessage%" - }, - "python.linting.pylamaArgs": { - "default": [], - "description": "%python.linting.pylamaArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.pylamaArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylamaArgs.deprecationMessage%" - }, - "python.linting.pylamaEnabled": { - "default": false, - "description": "%python.linting.pylamaEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.pylamaEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylamaEnabled.deprecationMessage%" - }, - "python.linting.pylamaPath": { - "default": "pylama", - "description": "%python.linting.pylamaPath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylamaPath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylamaPath.deprecationMessage%" - }, - "python.linting.pylintArgs": { - "default": [], - "description": "%python.linting.pylintArgs.description%", - "items": { - "type": "string" - }, - "scope": "resource", - "type": "array", - "markdownDeprecationMessage": "%python.linting.pylintArgs.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintArgs.deprecationMessage%" - }, - "python.linting.pylintCategorySeverity.convention": { - "default": "Information", - "description": "%python.linting.pylintCategorySeverity.convention.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintCategorySeverity.convention.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintCategorySeverity.convention.deprecationMessage%" - }, - "python.linting.pylintCategorySeverity.error": { - "default": "Error", - "description": "%python.linting.pylintCategorySeverity.error.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintCategorySeverity.error.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintCategorySeverity.error.deprecationMessage%" - }, - "python.linting.pylintCategorySeverity.fatal": { - "default": "Error", - "description": "%python.linting.pylintCategorySeverity.fatal.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintCategorySeverity.fatal.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintCategorySeverity.fatal.deprecationMessage%" - }, - "python.linting.pylintCategorySeverity.refactor": { - "default": "Hint", - "description": "%python.linting.pylintCategorySeverity.refactor.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintCategorySeverity.refactor.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintCategorySeverity.refactor.deprecationMessage%" - }, - "python.linting.pylintCategorySeverity.warning": { - "default": "Warning", - "description": "%python.linting.pylintCategorySeverity.warning.description%", - "enum": [ - "Error", - "Hint", - "Information", - "Warning" - ], - "scope": "resource", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintCategorySeverity.warning.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintCategorySeverity.warning.deprecationMessage%" - }, - "python.linting.pylintEnabled": { - "default": false, - "description": "%python.linting.pylintEnabled.description%", - "scope": "resource", - "type": "boolean", - "markdownDeprecationMessage": "%python.linting.pylintEnabled.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintEnabled.deprecationMessage%" - }, - "python.linting.pylintPath": { - "default": "pylint", - "description": "%python.linting.pylintPath.description%", - "scope": "machine-overridable", - "type": "string", - "markdownDeprecationMessage": "%python.linting.pylintPath.markdownDeprecationMessage%", - "deprecationMessage": "%python.linting.pylintPath.deprecationMessage%" - }, "python.logging.level": { "default": "error", "deprecationMessage": "%python.logging.level.deprecation%", diff --git a/package.nls.json b/package.nls.json index c738b3692daf..f328ee613ba9 100644 --- a/package.nls.json +++ b/package.nls.json @@ -49,133 +49,10 @@ "python.languageServer.jediDescription": "Use Jedi behind the Language Server Protocol (LSP) as a language server.", "python.languageServer.pylanceDescription": "Use Pylance as a language server.", "python.languageServer.noneDescription": "Disable language server capabilities.", - "python.linting.banditArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.banditArgs.markdownDeprecationMessage": "Bandit support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.banditArgs.deprecationMessage": "Bandit support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.banditEnabled.description": "Whether to lint Python files using bandit.", - "python.linting.banditEnabled.markdownDeprecationMessage": "Bandit support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.banditEnabled.deprecationMessage": "Bandit support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.banditPath.description": "Path to bandit, you can use a custom version of bandit by modifying this setting to include the full path.", - "python.linting.banditPath.markdownDeprecationMessage": "Bandit support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.banditPath.deprecationMessage": "Bandit support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.cwd.description": "Optional working directory for linters.", - "python.linting.cwd.markdownDeprecationMessage": "This setting will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.cwd.deprecationMessage": "This setting will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.enabled.description": "Whether to lint Python files.", - "python.linting.enabled.markdownDeprecationMessage": "This setting will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.enabled.deprecationMessage": "This setting will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8Args.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.flake8Args.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8Args.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8CategorySeverity.E.description": "Severity of Flake8 message type 'E'.", - "python.linting.flake8CategorySeverity.E.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8CategorySeverity.E.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8CategorySeverity.F.description": "Severity of Flake8 message type 'F'.", - "python.linting.flake8CategorySeverity.F.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8CategorySeverity.F.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8CategorySeverity.W.description": "Severity of Flake8 message type 'W'.", - "python.linting.flake8CategorySeverity.W.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8CategorySeverity.W.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8Enabled.description": "Whether to lint Python files using flake8.", - "python.linting.flake8Enabled.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8Enabled.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.flake8Path.description": "Path to flake8, you can use a custom version of flake8 by modifying this setting to include the full path.", - "python.linting.flake8Path.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Flake8 extension](https://marketplace.visualstudio.com/items?itemName=ms-python.flake8).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.flake8Path.deprecationMessage": "This setting will soon be deprecated. Please use the Flake8 extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.ignorePatterns.description": "Patterns used to exclude files or folders from being linted.", - "python.linting.ignorePatterns.markdownDeprecationMessage": "This setting will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.ignorePatterns.deprecationMessage": "This setting will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", "python.interpreter.infoVisibility.description": "Controls when to display information of selected interpreter in the status bar.", "python.interpreter.infoVisibility.never.description": "Never display information.", "python.interpreter.infoVisibility.onPythonRelated.description": "Only display information if Python-related files are opened.", "python.interpreter.infoVisibility.always.description": "Always display information.", - "python.linting.lintOnSave.description": "Whether to lint Python files when saved.", - "python.linting.lintOnSave.markdownDeprecationMessage": "This setting will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.lintOnSave.deprecationMessage": "This setting will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.maxNumberOfProblems.description": "Controls the maximum number of problems produced by the server.", - "python.linting.maxNumberOfProblems.markdownDeprecationMessage": "This setting will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.maxNumberOfProblems.deprecationMessage": "This setting will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.mypyArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.mypyArgs.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Mypy Type Checker extension](https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.mypyArgs.deprecationMessage": "This setting will soon be deprecated. Please use the Mypy Type Checker extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.mypyCategorySeverity.error.description": "Severity of Mypy message type 'Error'.", - "python.linting.mypyCategorySeverity.error.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Mypy Type Checker extension](https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.mypyCategorySeverity.error.deprecationMessage": "This setting will soon be deprecated. Please use the Mypy Type Checker extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.mypyCategorySeverity.note.description": "Severity of Mypy message type 'Note'.", - "python.linting.mypyCategorySeverity.note.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Mypy Type Checker extension](https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.mypyCategorySeverity.note.deprecationMessage": "This setting will soon be deprecated. Please use the Mypy Type Checker extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.mypyEnabled.description": "Whether to lint Python files using mypy.", - "python.linting.mypyEnabled.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Mypy Type Checker extension](https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.mypyEnabled.deprecationMessage": "This setting will soon be deprecated. Please use the Mypy Type Checker extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.mypyPath.description": "Path to mypy, you can use a custom version of mypy by modifying this setting to include the full path.", - "python.linting.mypyPath.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Mypy Type Checker extension](https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.mypyPath.deprecationMessage": "This setting will soon be deprecated. Please use the Mypy Type Checker extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.prospectorArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.prospectorArgs.markdownDeprecationMessage": "Prospector support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.prospectorArgs.deprecationMessage": "Prospector support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.prospectorEnabled.description": "Whether to lint Python files using prospector.", - "python.linting.prospectorEnabled.markdownDeprecationMessage": "Prospector support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.prospectorEnabled.deprecationMessage": "Prospector support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.prospectorPath.description": "Path to Prospector, you can use a custom version of prospector by modifying this setting to include the full path.", - "python.linting.prospectorPath.markdownDeprecationMessage": "Prospector support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.prospectorPath.deprecationMessage": "Prospector support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pycodestyleArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.pycodestyleArgs.markdownDeprecationMessage": "Pycodestyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pycodestyleArgs.deprecationMessage": "Pycodestyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pycodestyleCategorySeverity.E.description": "Severity of pycodestyle message type 'E'.", - "python.linting.pycodestyleCategorySeverity.E.markdownDeprecationMessage": "Pycodestyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pycodestyleCategorySeverity.E.deprecationMessage": "Pycodestyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pycodestyleCategorySeverity.W.description": "Severity of pycodestyle message type 'W'.", - "python.linting.pycodestyleCategorySeverity.W.markdownDeprecationMessage": "Pycodestyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pycodestyleCategorySeverity.W.deprecationMessage": "Pycodestyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pycodestyleEnabled.description": "Whether to lint Python files using pycodestyle.", - "python.linting.pycodestyleEnabled.markdownDeprecationMessage": "Pycodestyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pycodestyleEnabled.deprecationMessage": "Pycodestyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pycodestylePath.description": "Path to pycodestyle, you can use a custom version of pycodestyle by modifying this setting to include the full path.", - "python.linting.pycodestylePath.markdownDeprecationMessage": "Pycodestyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pycodestylePath.deprecationMessage": "Pycodestyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pydocstyleArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.pydocstyleArgs.markdownDeprecationMessage": "Pydocstyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pydocstyleArgs.deprecationMessage": "Pydocstyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pydocstyleEnabled.description": "Whether to lint Python files using pydocstyle.", - "python.linting.pydocstyleEnabled.markdownDeprecationMessage": "Pydocstyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pydocstyleEnabled.deprecationMessage": "Pydocstyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pydocstylePath.description": "Path to pydocstyle, you can use a custom version of pydocstyle by modifying this setting to include the full path.", - "python.linting.pydocstylePath.markdownDeprecationMessage": "Pydocstyle support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pydocstylePath.deprecationMessage": "Pydocstyle support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylamaArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.pylamaArgs.markdownDeprecationMessage": "Pylama support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylamaArgs.deprecationMessage": "Pylama support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylamaEnabled.description": "Whether to lint Python files using pylama.", - "python.linting.pylamaEnabled.markdownDeprecationMessage": "Pylama support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylamaEnabled.deprecationMessage": "Pylama support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylamaPath.description": "Path to pylama, you can use a custom version of pylama by modifying this setting to include the full path.", - "python.linting.pylamaPath.markdownDeprecationMessage": "Pylama support will soon be deprecated. Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylamaPath.deprecationMessage": "Pylama support will soon be deprecated. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintArgs.description": "Arguments passed in. Each argument is a separate item in the array.", - "python.linting.pylintArgs.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintArgs.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintCategorySeverity.convention.description": "Severity of Pylint message type 'Convention/C'.", - "python.linting.pylintCategorySeverity.convention.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintCategorySeverity.convention.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintCategorySeverity.error.description": "Severity of Pylint message type 'Error/E'.", - "python.linting.pylintCategorySeverity.error.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintCategorySeverity.error.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintCategorySeverity.fatal.description": "Severity of Pylint message type 'Error/F'.", - "python.linting.pylintCategorySeverity.fatal.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintCategorySeverity.fatal.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintCategorySeverity.refactor.description": "Severity of Pylint message type 'Refactor/R'.", - "python.linting.pylintCategorySeverity.refactor.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintCategorySeverity.refactor.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintCategorySeverity.warning.description": "Severity of Pylint message type 'Warning/W'.", - "python.linting.pylintCategorySeverity.warning.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintCategorySeverity.warning.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintEnabled.description": "Whether to lint Python files using pylint.", - "python.linting.pylintEnabled.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintEnabled.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", - "python.linting.pylintPath.description": "Path to Pylint, you can use a custom version of pylint by modifying this setting to include the full path.", - "python.linting.pylintPath.markdownDeprecationMessage": "This setting will soon be deprecated. Please use the [Pylint extension](https://marketplace.visualstudio.com/items?itemName=ms-python.pylint).
Learn more [here](https://aka.ms/AAlgvkb).", - "python.linting.pylintPath.deprecationMessage": "This setting will soon be deprecated. Please use the Pylint extension. Learn more here: https://aka.ms/AAlgvkb.", "python.logging.level.description": "The logging level the extension logs at, defaults to 'error'", "python.logging.level.deprecation": "This setting is deprecated. Please use command `Developer: Set Log Level...` to set logging level.", "python.missingPackage.severity.description": "Set severity of missing packages in requirements.txt or pyproject.toml", diff --git a/src/client/common/configSettings.ts b/src/client/common/configSettings.ts index cadc1515f7e6..f9c56d4992fe 100644 --- a/src/client/common/configSettings.ts +++ b/src/client/common/configSettings.ts @@ -6,7 +6,6 @@ import * as fs from 'fs'; import { ConfigurationChangeEvent, ConfigurationTarget, - DiagnosticSeverity, Disposable, Event, EventEmitter, @@ -29,7 +28,6 @@ import { IExperiments, IInterpreterPathService, IInterpreterSettings, - ILintingSettings, IPythonSettings, ITensorBoardSettings, ITerminalSettings, @@ -106,8 +104,6 @@ export class PythonSettings implements IPythonSettings { public devOptions: string[] = []; - public linting!: ILintingSettings; - public autoComplete!: IAutoCompleteSettings; public tensorBoard: ITensorBoardSettings | undefined; @@ -304,94 +300,8 @@ export class PythonSettings implements IPythonSettings { this.devOptions = systemVariables.resolveAny(pythonSettings.get('devOptions'))!; this.devOptions = Array.isArray(this.devOptions) ? this.devOptions : []; - const lintingSettings = systemVariables.resolveAny(pythonSettings.get('linting'))!; - if (this.linting) { - Object.assign(this.linting, lintingSettings); - } else { - this.linting = lintingSettings; - } - this.globalModuleInstallation = pythonSettings.get('globalModuleInstallation') === true; - // Support for travis. - this.linting = this.linting - ? this.linting - : { - enabled: false, - cwd: undefined, - ignorePatterns: [], - flake8Args: [], - flake8Enabled: false, - flake8Path: 'flake8', - lintOnSave: false, - maxNumberOfProblems: 100, - mypyArgs: [], - mypyEnabled: false, - mypyPath: 'mypy', - banditArgs: [], - banditEnabled: false, - banditPath: 'bandit', - pycodestyleArgs: [], - pycodestyleEnabled: false, - pycodestylePath: 'pycodestyle', - pylamaArgs: [], - pylamaEnabled: false, - pylamaPath: 'pylama', - prospectorArgs: [], - prospectorEnabled: false, - prospectorPath: 'prospector', - pydocstyleArgs: [], - pydocstyleEnabled: false, - pydocstylePath: 'pydocstyle', - pylintArgs: [], - pylintEnabled: false, - pylintPath: 'pylint', - pylintCategorySeverity: { - convention: DiagnosticSeverity.Hint, - error: DiagnosticSeverity.Error, - fatal: DiagnosticSeverity.Error, - refactor: DiagnosticSeverity.Hint, - warning: DiagnosticSeverity.Warning, - }, - pycodestyleCategorySeverity: { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning, - }, - flake8CategorySeverity: { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning, - // Per http://flake8.pycqa.org/en/latest/glossary.html#term-error-code - // 'F' does not mean 'fatal as in PyLint but rather 'pyflakes' such as - // unused imports, variables, etc. - F: DiagnosticSeverity.Warning, - }, - mypyCategorySeverity: { - error: DiagnosticSeverity.Error, - note: DiagnosticSeverity.Hint, - }, - }; - this.linting.pylintPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylintPath), workspaceRoot); - this.linting.flake8Path = getAbsolutePath(systemVariables.resolveAny(this.linting.flake8Path), workspaceRoot); - this.linting.pycodestylePath = getAbsolutePath( - systemVariables.resolveAny(this.linting.pycodestylePath), - workspaceRoot, - ); - this.linting.pylamaPath = getAbsolutePath(systemVariables.resolveAny(this.linting.pylamaPath), workspaceRoot); - this.linting.prospectorPath = getAbsolutePath( - systemVariables.resolveAny(this.linting.prospectorPath), - workspaceRoot, - ); - this.linting.pydocstylePath = getAbsolutePath( - systemVariables.resolveAny(this.linting.pydocstylePath), - workspaceRoot, - ); - this.linting.mypyPath = getAbsolutePath(systemVariables.resolveAny(this.linting.mypyPath), workspaceRoot); - this.linting.banditPath = getAbsolutePath(systemVariables.resolveAny(this.linting.banditPath), workspaceRoot); - - if (this.linting.cwd) { - this.linting.cwd = getAbsolutePath(systemVariables.resolveAny(this.linting.cwd), workspaceRoot); - } - const testSettings = systemVariables.resolveAny(pythonSettings.get('testing'))!; if (this.testing) { Object.assign(this.testing, testSettings); diff --git a/src/client/common/installer/moduleInstaller.ts b/src/client/common/installer/moduleInstaller.ts index 5a4f245900ea..4cc4cd0c6a2f 100644 --- a/src/client/common/installer/moduleInstaller.ts +++ b/src/client/common/installer/moduleInstaller.ts @@ -238,26 +238,10 @@ export abstract class ModuleInstaller implements IModuleInstaller { export function translateProductToModule(product: Product): string { switch (product) { - case Product.mypy: - return 'mypy'; - case Product.pylama: - return 'pylama'; - case Product.prospector: - return 'prospector'; - case Product.pylint: - return 'pylint'; case Product.pytest: return 'pytest'; - case Product.pycodestyle: - return 'pycodestyle'; - case Product.pydocstyle: - return 'pydocstyle'; - case Product.flake8: - return 'flake8'; case Product.unittest: return 'unittest'; - case Product.bandit: - return 'bandit'; case Product.tensorboard: return 'tensorboard'; case Product.torchProfilerInstallName: diff --git a/src/client/common/installer/productNames.ts b/src/client/common/installer/productNames.ts index 9b917d2f1d76..00b19ce77ac3 100644 --- a/src/client/common/installer/productNames.ts +++ b/src/client/common/installer/productNames.ts @@ -4,14 +4,6 @@ import { Product } from '../types'; export const ProductNames = new Map(); -ProductNames.set(Product.bandit, 'bandit'); -ProductNames.set(Product.flake8, 'flake8'); -ProductNames.set(Product.mypy, 'mypy'); -ProductNames.set(Product.pycodestyle, 'pycodestyle'); -ProductNames.set(Product.pylama, 'pylama'); -ProductNames.set(Product.prospector, 'prospector'); -ProductNames.set(Product.pydocstyle, 'pydocstyle'); -ProductNames.set(Product.pylint, 'pylint'); ProductNames.set(Product.pytest, 'pytest'); ProductNames.set(Product.tensorboard, 'tensorboard'); ProductNames.set(Product.torchProfilerInstallName, 'torch-tb-profiler'); diff --git a/src/client/common/installer/productPath.ts b/src/client/common/installer/productPath.ts index 3b3f1d7c1794..b06e4b7a48a9 100644 --- a/src/client/common/installer/productPath.ts +++ b/src/client/common/installer/productPath.ts @@ -7,7 +7,6 @@ import { inject, injectable } from 'inversify'; import * as path from 'path'; import { Uri } from 'vscode'; import { IServiceContainer } from '../../ioc/types'; -import { ILinterManager } from '../../linters/types'; import { ITestingService } from '../../testing/types'; import { IConfigurationService, IInstaller, Product } from '../types'; import { IProductPathService } from './types'; @@ -36,17 +35,6 @@ export abstract class BaseProductPathsService implements IProductPathService { } } -@injectable() -export class LinterProductPathService extends BaseProductPathsService { - constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { - super(serviceContainer); - } - public getExecutableNameFromSettings(product: Product, resource?: Uri): string { - const linterManager = this.serviceContainer.get(ILinterManager); - return linterManager.getLinterInfo(product).pathName(resource); - } -} - @injectable() export class TestFrameworkProductPathService extends BaseProductPathsService { constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { diff --git a/src/client/common/installer/productService.ts b/src/client/common/installer/productService.ts index af2192755fe8..bf5597cc5859 100644 --- a/src/client/common/installer/productService.ts +++ b/src/client/common/installer/productService.ts @@ -12,14 +12,6 @@ export class ProductService implements IProductService { private ProductTypes = new Map(); constructor() { - this.ProductTypes.set(Product.bandit, ProductType.Linter); - this.ProductTypes.set(Product.flake8, ProductType.Linter); - this.ProductTypes.set(Product.mypy, ProductType.Linter); - this.ProductTypes.set(Product.pycodestyle, ProductType.Linter); - this.ProductTypes.set(Product.prospector, ProductType.Linter); - this.ProductTypes.set(Product.pydocstyle, ProductType.Linter); - this.ProductTypes.set(Product.pylama, ProductType.Linter); - this.ProductTypes.set(Product.pylint, ProductType.Linter); this.ProductTypes.set(Product.pytest, ProductType.TestFramework); this.ProductTypes.set(Product.unittest, ProductType.TestFramework); this.ProductTypes.set(Product.tensorboard, ProductType.DataScience); diff --git a/src/client/common/installer/serviceRegistry.ts b/src/client/common/installer/serviceRegistry.ts index c4e7c1a089c6..d4d8a05c3a49 100644 --- a/src/client/common/installer/serviceRegistry.ts +++ b/src/client/common/installer/serviceRegistry.ts @@ -9,11 +9,7 @@ import { CondaInstaller } from './condaInstaller'; import { PipEnvInstaller } from './pipEnvInstaller'; import { PipInstaller } from './pipInstaller'; import { PoetryInstaller } from './poetryInstaller'; -import { - DataScienceProductPathService, - LinterProductPathService, - TestFrameworkProductPathService, -} from './productPath'; +import { DataScienceProductPathService, TestFrameworkProductPathService } from './productPath'; import { ProductService } from './productService'; import { IInstallationChannelManager, IModuleInstaller, IProductPathService, IProductService } from './types'; @@ -24,7 +20,6 @@ export function registerTypes(serviceManager: IServiceManager) { serviceManager.addSingleton(IModuleInstaller, PoetryInstaller); serviceManager.addSingleton(IInstallationChannelManager, InstallationChannelManager); serviceManager.addSingleton(IProductService, ProductService); - serviceManager.addSingleton(IProductPathService, LinterProductPathService, ProductType.Linter); serviceManager.addSingleton( IProductPathService, TestFrameworkProductPathService, diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 05a8a985a5ff..742948a49652 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -8,7 +8,6 @@ import { CancellationToken, ConfigurationChangeEvent, ConfigurationTarget, - DiagnosticSeverity, Disposable, DocumentSymbolProvider, Event, @@ -85,24 +84,14 @@ export enum ProductInstallStatus { } export enum ProductType { - Linter = 'Linter', TestFramework = 'TestFramework', - RefactoringLibrary = 'RefactoringLibrary', DataScience = 'DataScience', Python = 'Python', } export enum Product { pytest = 1, - pylint = 3, - flake8 = 4, - pycodestyle = 5, - pylama = 6, - prospector = 7, - pydocstyle = 8, - mypy = 11, unittest = 12, - bandit = 17, tensorboard = 24, torchProfilerInstallName = 25, torchProfilerImportName = 26, @@ -179,7 +168,6 @@ export interface IPythonSettings { readonly pipenvPath: string; readonly poetryPath: string; readonly devOptions: string[]; - readonly linting: ILintingSettings; readonly testing: ITestingSettings; readonly autoComplete: IAutoCompleteSettings; readonly terminal: ITerminalSettings; @@ -197,67 +185,10 @@ export interface ITensorBoardSettings { logDirectory: string | undefined; } -export interface IPylintCategorySeverity { - readonly convention: DiagnosticSeverity; - readonly refactor: DiagnosticSeverity; - readonly warning: DiagnosticSeverity; - readonly error: DiagnosticSeverity; - readonly fatal: DiagnosticSeverity; -} -export interface IPycodestyleCategorySeverity { - readonly W: DiagnosticSeverity; - readonly E: DiagnosticSeverity; -} - -export interface Flake8CategorySeverity { - readonly F: DiagnosticSeverity; - readonly E: DiagnosticSeverity; - readonly W: DiagnosticSeverity; -} -export interface IMypyCategorySeverity { - readonly error: DiagnosticSeverity; - readonly note: DiagnosticSeverity; -} export interface IInterpreterSettings { infoVisibility: 'never' | 'onPythonRelated' | 'always'; } -export interface ILintingSettings { - readonly enabled: boolean; - readonly ignorePatterns: string[]; - readonly prospectorEnabled: boolean; - readonly prospectorArgs: string[]; - readonly pylintEnabled: boolean; - readonly pylintArgs: string[]; - readonly pycodestyleEnabled: boolean; - readonly pycodestyleArgs: string[]; - readonly pylamaEnabled: boolean; - readonly pylamaArgs: string[]; - readonly flake8Enabled: boolean; - readonly flake8Args: string[]; - readonly pydocstyleEnabled: boolean; - readonly pydocstyleArgs: string[]; - readonly lintOnSave: boolean; - readonly maxNumberOfProblems: number; - readonly pylintCategorySeverity: IPylintCategorySeverity; - readonly pycodestyleCategorySeverity: IPycodestyleCategorySeverity; - readonly flake8CategorySeverity: Flake8CategorySeverity; - readonly mypyCategorySeverity: IMypyCategorySeverity; - cwd?: string; - prospectorPath: string; - pylintPath: string; - pycodestylePath: string; - pylamaPath: string; - flake8Path: string; - pydocstylePath: string; - mypyEnabled: boolean; - mypyArgs: string[]; - mypyPath: string; - banditEnabled: boolean; - banditArgs: string[]; - banditPath: string; -} - export interface ITerminalSettings { readonly executeInFileDir: boolean; readonly focusAfterLaunch: boolean; diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index bbb55a79ce40..b5d1721d14fa 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -506,14 +506,3 @@ export namespace CreateEnv { export const disableCheckWorkspace = l10n.t('Disable (Workspace)'); } } - -export namespace ToolsExtensions { - export const flake8PromptMessage = l10n.t( - 'Use the Flake8 extension to enable easier configuration and new features such as quick fixes.', - ); - export const pylintPromptMessage = l10n.t( - 'Use the Pylint extension to enable easier configuration and new features such as quick fixes.', - ); - export const installPylintExtension = l10n.t('Install Pylint extension'); - export const installFlake8Extension = l10n.t('Install Flake8 extension'); -} diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index 0d3b04d9bb8c..37ca1ad54afc 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -27,7 +27,6 @@ import { registerTypes as debugConfigurationRegisterTypes } from './debugger/ext import { IDebugConfigurationService, IDynamicDebugConfigurationService } from './debugger/extension/types'; import { IInterpreterService } from './interpreter/contracts'; import { getLanguageConfiguration } from './language/languageConfiguration'; -import { registerTypes as lintersRegisterTypes } from './linters/serviceRegistry'; import { ReplProvider } from './providers/replProvider'; import { registerTypes as providersRegisterTypes } from './providers/serviceRegistry'; import { TerminalProvider } from './providers/terminalProvider'; @@ -122,7 +121,6 @@ async function activateLegacy(ext: ExtensionState): Promise { serviceManager.addSingletonInstance(UseProposedApi, enableProposedApi); // Feature specific registrations. unitTestsRegisterTypes(serviceManager); - lintersRegisterTypes(serviceManager); installerRegisterTypes(serviceManager); commonRegisterTerminalTypes(serviceManager); debugConfigurationRegisterTypes(serviceManager); diff --git a/src/client/linters/bandit.ts b/src/client/linters/bandit.ts deleted file mode 100644 index bbc8836bfc6b..000000000000 --- a/src/client/linters/bandit.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage, LintMessageSeverity } from './types'; - -const severityMapping: Record = { - LOW: LintMessageSeverity.Information, - MEDIUM: LintMessageSeverity.Warning, - HIGH: LintMessageSeverity.Error, -}; - -export const BANDIT_REGEX = - '(?\\d+),(?(col)?(\\d+)?),(?\\w+),(?\\w+\\d+):(?.*)\\r?(\\n|$)'; - -export class Bandit extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.bandit, serviceContainer); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - // View all errors in bandit <= 1.5.1 (https://github.com/PyCQA/bandit/issues/371) - const messages = await this.run([document.uri.fsPath], document, cancellation, BANDIT_REGEX); - - messages.forEach((msg) => { - msg.severity = severityMapping[msg.type]; - }); - return messages; - } -} diff --git a/src/client/linters/baseLinter.ts b/src/client/linters/baseLinter.ts deleted file mode 100644 index bb24bee1637f..000000000000 --- a/src/client/linters/baseLinter.ts +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as path from 'path'; -import * as vscode from 'vscode'; -import { IWorkspaceService } from '../common/application/types'; -import { isTestExecution } from '../common/constants'; -import '../common/extensions'; -import { IPythonToolExecutionService } from '../common/process/types'; -import { splitLines } from '../common/stringUtils'; -import { - ExecutionInfo, - Flake8CategorySeverity, - IConfigurationService, - IMypyCategorySeverity, - IPycodestyleCategorySeverity, - IPylintCategorySeverity, - IPythonSettings, - Product, -} from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceError, traceLog } from '../logging'; -import { ErrorHandler } from './errorHandlers/errorHandler'; -import { ILinter, ILinterInfo, ILinterManager, ILintMessage, LinterId, LintMessageSeverity } from './types'; - -const namedRegexp = require('named-js-regexp'); -// Allow negative column numbers (https://github.com/PyCQA/pylint/issues/1822) -// Allow codes with more than one letter (i.e. ABC123) -const REGEX = '(?\\d+),(?-?\\d+),(?\\w+),(?\\w+\\d+):(?.*)\\r?(\\n|$)'; - -interface IRegexGroup { - line: number; - column: number; - code: string; - message: string; - type: string; -} - -function matchNamedRegEx(data: string, regex: string): IRegexGroup | undefined { - const compiledRegexp = namedRegexp(regex, 'g'); - const rawMatch = compiledRegexp.exec(data); - if (rawMatch !== null) { - return rawMatch.groups(); - } - - return undefined; -} - -export function parseLine(line: string, regex: string, linterID: LinterId, colOffset = 0): ILintMessage | undefined { - const match = matchNamedRegEx(line, regex)!; - if (!match) { - return undefined; - } - - match.line = Number(match.line); - - match.column = Number(match.column); - - return { - code: match.code, - message: match.message, - column: Number.isNaN(match.column) || match.column <= 0 ? 0 : match.column - colOffset, - line: match.line, - type: match.type, - provider: linterID, - }; -} - -export abstract class BaseLinter implements ILinter { - protected readonly configService: IConfigurationService; - - private errorHandler: ErrorHandler; - - private _pythonSettings!: IPythonSettings; - - private _info: ILinterInfo; - - private workspace: IWorkspaceService; - - protected get pythonSettings(): IPythonSettings { - return this._pythonSettings; - } - - constructor( - product: Product, - protected readonly serviceContainer: IServiceContainer, - protected readonly columnOffset = 0, - ) { - this._info = serviceContainer.get(ILinterManager).getLinterInfo(product); - this.errorHandler = new ErrorHandler(this.info.product, serviceContainer); - this.configService = serviceContainer.get(IConfigurationService); - this.workspace = serviceContainer.get(IWorkspaceService); - } - - public get info(): ILinterInfo { - return this._info; - } - - public async lint(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise { - this._pythonSettings = this.configService.getSettings(document.uri); - return this.runLinter(document, cancellation); - } - - protected getWorkspaceRootPath(document: vscode.TextDocument): string { - const workspaceFolder = this.workspace.getWorkspaceFolder(document.uri); - const workspaceRootPath = - workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string' ? workspaceFolder.uri.fsPath : undefined; - return typeof workspaceRootPath === 'string' ? workspaceRootPath : path.dirname(document.uri.fsPath); - } - - protected getWorkingDirectoryPath(document: vscode.TextDocument): string { - return this._pythonSettings.linting.cwd || this.getWorkspaceRootPath(document); - } - - protected abstract runLinter( - document: vscode.TextDocument, - cancellation: vscode.CancellationToken, - ): Promise; - - // eslint-disable-next-line class-methods-use-this - protected parseMessagesSeverity( - error: string, - categorySeverity: - | Flake8CategorySeverity - | IMypyCategorySeverity - | IPycodestyleCategorySeverity - | IPylintCategorySeverity, - ): LintMessageSeverity { - const severity = error as keyof typeof categorySeverity; - - if (categorySeverity[severity]) { - const severityName = categorySeverity[severity]; - switch (severityName) { - case 'Error': - return LintMessageSeverity.Error; - case 'Hint': - return LintMessageSeverity.Hint; - case 'Information': - return LintMessageSeverity.Information; - case 'Warning': - return LintMessageSeverity.Warning; - default: { - if (LintMessageSeverity[severityName]) { - return (LintMessageSeverity[severityName] as unknown) as LintMessageSeverity; - } - } - } - } - return LintMessageSeverity.Information; - } - - protected async run( - args: string[], - document: vscode.TextDocument, - cancellation: vscode.CancellationToken, - regEx: string = REGEX, - ): Promise { - if (!this.info.isEnabled(document.uri)) { - return []; - } - const executionInfo = this.info.getExecutionInfo(args, document.uri); - const cwd = this.getWorkingDirectoryPath(document); - const pythonToolsExecutionService = this.serviceContainer.get( - IPythonToolExecutionService, - ); - try { - const result = await pythonToolsExecutionService.execForLinter( - executionInfo, - { cwd, token: cancellation, mergeStdOutErr: false }, - document.uri, - ); - this.displayLinterResultHeader(result.stdout); - return await this.parseMessages(result.stdout, document, cancellation, regEx); - } catch (error) { - await this.handleError(error as Error, document.uri, executionInfo); - return []; - } - } - - protected async parseMessages( - output: string, - _document: vscode.TextDocument, - _token: vscode.CancellationToken, - regEx: string, - ): Promise { - const outputLines = splitLines(output, { removeEmptyEntries: false, trim: false }); - return this.parseLines(outputLines, regEx); - } - - protected async handleError(error: Error, resource: vscode.Uri, execInfo: ExecutionInfo): Promise { - if (isTestExecution()) { - this.errorHandler.handleError(error, resource, execInfo).ignoreErrors(); - } else { - this.errorHandler - .handleError(error, resource, execInfo) - .catch((ex) => traceError('Error in errorHandler.handleError', ex)) - .ignoreErrors(); - } - } - - private parseLine(line: string, regEx: string): ILintMessage | undefined { - return parseLine(line, regEx, this.info.id, this.columnOffset); - } - - private parseLines(outputLines: string[], regEx: string): ILintMessage[] { - const messages: ILintMessage[] = []; - for (const line of outputLines) { - try { - const msg = this.parseLine(line, regEx); - if (msg) { - messages.push(msg); - if (messages.length >= this.pythonSettings.linting.maxNumberOfProblems) { - break; - } - } - } catch (ex) { - traceError(`Linter '${this.info.id}' failed to parse the line '${line}.`, ex); - } - } - return messages; - } - - private displayLinterResultHeader(data: string) { - traceLog(`${'#'.repeat(10)}Linting Output - ${this.info.id}${'#'.repeat(10)}\n`); - traceLog(data); - } -} diff --git a/src/client/linters/constants.ts b/src/client/linters/constants.ts deleted file mode 100644 index 27b7c80db7f4..000000000000 --- a/src/client/linters/constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { Product } from '../common/types'; -import { LinterId } from './types'; - -// All supported linters must be in this map. -export const LINTERID_BY_PRODUCT = new Map([ - [Product.bandit, LinterId.Bandit], - [Product.flake8, LinterId.Flake8], - [Product.pylint, LinterId.PyLint], - [Product.mypy, LinterId.MyPy], - [Product.pycodestyle, LinterId.PyCodeStyle], - [Product.prospector, LinterId.Prospector], - [Product.pydocstyle, LinterId.PyDocStyle], - [Product.pylama, LinterId.PyLama], -]); diff --git a/src/client/linters/errorHandlers/baseErrorHandler.ts b/src/client/linters/errorHandlers/baseErrorHandler.ts deleted file mode 100644 index 16c5e93ae012..000000000000 --- a/src/client/linters/errorHandlers/baseErrorHandler.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { Uri } from 'vscode'; -import { ExecutionInfo, IInstaller, Product } from '../../common/types'; -import { IServiceContainer } from '../../ioc/types'; -import { IErrorHandler } from '../types'; - -export abstract class BaseErrorHandler implements IErrorHandler { - protected installer: IInstaller; - - private handler?: IErrorHandler; - - constructor(protected product: Product, protected serviceContainer: IServiceContainer) { - this.installer = this.serviceContainer.get(IInstaller); - } - - protected get nextHandler(): IErrorHandler | undefined { - return this.handler; - } - - public setNextHandler(handler: IErrorHandler): void { - this.handler = handler; - } - - public abstract handleError(error: Error, resource: Uri, execInfo: ExecutionInfo): Promise; -} diff --git a/src/client/linters/errorHandlers/errorHandler.ts b/src/client/linters/errorHandlers/errorHandler.ts deleted file mode 100644 index af28dd61c3a4..000000000000 --- a/src/client/linters/errorHandlers/errorHandler.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Uri } from 'vscode'; -import { ExecutionInfo, Product } from '../../common/types'; -import { IServiceContainer } from '../../ioc/types'; -import { IErrorHandler } from '../types'; -import { BaseErrorHandler } from './baseErrorHandler'; -import { StandardErrorHandler } from './standard'; - -export class ErrorHandler implements IErrorHandler { - private handler: BaseErrorHandler; - - constructor(product: Product, serviceContainer: IServiceContainer) { - this.handler = new StandardErrorHandler(product, serviceContainer); - } - - public handleError(error: Error, resource: Uri, execInfo: ExecutionInfo): Promise { - return this.handler.handleError(error, resource, execInfo); - } -} diff --git a/src/client/linters/errorHandlers/standard.ts b/src/client/linters/errorHandlers/standard.ts deleted file mode 100644 index 6367da7abe4a..000000000000 --- a/src/client/linters/errorHandlers/standard.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { l10n, Uri } from 'vscode'; -import { IApplicationShell } from '../../common/application/types'; -import { ExecutionInfo, ILogOutputChannel } from '../../common/types'; -import { traceError, traceLog } from '../../logging'; -import { ILinterManager, LinterId } from '../types'; -import { BaseErrorHandler } from './baseErrorHandler'; - -export class StandardErrorHandler extends BaseErrorHandler { - public async handleError(error: Error, resource: Uri, execInfo: ExecutionInfo): Promise { - if ( - typeof error === 'string' && - (error as string).includes("OSError: [Errno 2] No such file or directory: '/") - ) { - return this.nextHandler ? this.nextHandler.handleError(error, resource, execInfo) : Promise.resolve(false); - } - - const linterManager = this.serviceContainer.get(ILinterManager); - const info = linterManager.getLinterInfo(execInfo.product!); - - traceError(`There was an error in running the linter ${info.id}`, error); - if (info.id === LinterId.PyLint) { - traceError('Support for "pylint" is moved to ms-python.pylint extension.'); - traceError( - 'Please install the extension from: https://marketplace.visualstudio.com/items?itemName=ms-python.pylint', - ); - } else if (info.id === LinterId.Flake8) { - traceError('Support for "flake8" is moved to ms-python.flake8 extension.'); - traceError( - 'Please install the extension from: https://marketplace.visualstudio.com/items?itemName=ms-python.flake8', - ); - } else if (info.id === LinterId.MyPy) { - traceError('Support for "mypy" is moved to ms-python.mypy-type-checker extension.'); - traceError( - 'Please install the extension from: https://marketplace.visualstudio.com/items?itemName=ms-python.mypy-type-checker', - ); - } - traceError(`If the error is due to missing ${info.id}, please install ${info.id} using pip manually.`); - traceError('Learn more here: https://aka.ms/AAlgvkb'); - traceLog(`Linting with ${info.id} failed.`); - traceLog(error.toString()); - - this.displayLinterError(info.id).ignoreErrors(); - return true; - } - - private async displayLinterError(linterId: LinterId) { - const message = l10n.t("There was an error in running the linter '{0}'", linterId); - const appShell = this.serviceContainer.get(IApplicationShell); - const outputChannel = this.serviceContainer.get(ILogOutputChannel); - const action = await appShell.showErrorMessage(message, 'View Errors'); - if (action === 'View Errors') { - outputChannel.show(); - } - } -} diff --git a/src/client/linters/flake8.ts b/src/client/linters/flake8.ts deleted file mode 100644 index e79d09158741..000000000000 --- a/src/client/linters/flake8.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceLog } from '../logging'; -import { BaseLinter } from './baseLinter'; -import { isExtensionEnabled } from './prompts/common'; -import { FLAKE8_EXTENSION } from './prompts/flake8Prompt'; -import { IToolsExtensionPrompt } from './prompts/types'; -import { ILintMessage } from './types'; - -const COLUMN_OFF_SET = 1; - -export class Flake8 extends BaseLinter { - constructor(serviceContainer: IServiceContainer, private readonly prompt: IToolsExtensionPrompt) { - super(Product.flake8, serviceContainer, COLUMN_OFF_SET); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - await this.prompt.showPrompt(); - - if (isExtensionEnabled(this.serviceContainer, FLAKE8_EXTENSION)) { - traceLog( - 'LINTING: Skipping linting from Python extension, since Flake8 extension is installed and enabled.', - ); - return []; - } - - const messages = await this.run([document.uri.fsPath], document, cancellation); - messages.forEach((msg) => { - msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.flake8CategorySeverity); - // flake8 uses 0th line for some file-wide problems - // but diagnostics expects positive line numbers. - if (msg.line === 0) { - msg.line = 1; - } - }); - return messages; - } -} diff --git a/src/client/linters/linterInfo.ts b/src/client/linters/linterInfo.ts deleted file mode 100644 index 321f23b0f304..000000000000 --- a/src/client/linters/linterInfo.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import * as path from 'path'; -import { Uri } from 'vscode'; -import { linterScript } from '../common/process/internal/scripts'; -import { ExecutionInfo, IConfigurationService, ILintingSettings, Product } from '../common/types'; -import { ILinterInfo, LinterId } from './types'; - -export class LinterInfo implements ILinterInfo { - private _id: LinterId; - - private _product: Product; - - private _configFileNames: string[]; - - constructor( - product: Product, - id: LinterId, - protected configService: IConfigurationService, - configFileNames: string[] = [], - ) { - this._product = product; - this._id = id; - this._configFileNames = configFileNames; - } - - public get id(): LinterId { - return this._id; - } - - public get product(): Product { - return this._product; - } - - public get pathSettingName(): string { - return `${this.id}Path`; - } - - public get argsSettingName(): string { - return `${this.id}Args`; - } - - public get enabledSettingName(): string { - return `${this.id}Enabled`; - } - - public get configFileNames(): string[] { - return this._configFileNames; - } - - public async enableAsync(enabled: boolean, resource?: Uri): Promise { - return this.configService.updateSetting(`linting.${this.enabledSettingName}`, enabled, resource); - } - - public isEnabled(resource?: Uri): boolean { - const settings = this.configService.getSettings(resource); - const name = this.enabledSettingName as keyof ILintingSettings; - return settings.linting[name] as boolean; - } - - public pathName(resource?: Uri): string { - const settings = this.configService.getSettings(resource); - const name = this.pathSettingName as keyof ILintingSettings; - return settings.linting[name] as string; - } - - public linterArgs(resource?: Uri): string[] { - const settings = this.configService.getSettings(resource); - const name = this.argsSettingName as keyof ILintingSettings; - const args = settings.linting[name]; - return Array.isArray(args) ? (args as string[]) : []; - } - - public getExecutionInfo(customArgs: string[], resource?: Uri): ExecutionInfo { - const execPath = this.pathName(resource); - const args = this.linterArgs(resource).concat(customArgs); - const script = linterScript(); - if (path.basename(execPath) === execPath) { - return { - execPath: undefined, - args: [script, '-m', this.id, ...args], - product: this.product, - moduleName: execPath, - }; - } - return { - execPath, - moduleName: this.id, - args: [script, '-p', this.id, execPath, ...args], - product: this.product, - }; - } -} diff --git a/src/client/linters/linterManager.ts b/src/client/linters/linterManager.ts deleted file mode 100644 index 72c92aa1c77d..000000000000 --- a/src/client/linters/linterManager.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* eslint-disable max-classes-per-file */ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { CancellationToken, TextDocument, Uri } from 'vscode'; -import { IConfigurationService, Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceError } from '../logging'; -import { Bandit } from './bandit'; -import { Flake8 } from './flake8'; -import { LinterInfo } from './linterInfo'; -import { MyPy } from './mypy'; -import { getOrCreateFlake8Prompt } from './prompts/flake8Prompt'; -import { getOrCreatePylintPrompt } from './prompts/pylintPrompt'; -import { Prospector } from './prospector'; -import { Pycodestyle } from './pycodestyle'; -import { PyDocStyle } from './pydocstyle'; -import { PyLama } from './pylama'; -import { Pylint } from './pylint'; -import { ILinter, ILinterInfo, ILinterManager, ILintMessage, LinterId } from './types'; - -class DisabledLinter implements ILinter { - constructor(private configService: IConfigurationService) {} - - public get info() { - return new LinterInfo(Product.pylint, LinterId.PyLint, this.configService); - } - - // eslint-disable-next-line class-methods-use-this - public async lint(_document: TextDocument, _cancellation: CancellationToken): Promise { - return []; - } -} - -@injectable() -export class LinterManager implements ILinterManager { - protected linters: ILinterInfo[]; - - constructor(@inject(IConfigurationService) private configService: IConfigurationService) { - // Note that we use unit tests to ensure all the linters are here. - this.linters = [ - new LinterInfo(Product.bandit, LinterId.Bandit, this.configService), - new LinterInfo(Product.flake8, LinterId.Flake8, this.configService), - new LinterInfo(Product.pylint, LinterId.PyLint, this.configService, ['pylintrc', '.pylintrc']), - new LinterInfo(Product.mypy, LinterId.MyPy, this.configService), - new LinterInfo(Product.pycodestyle, LinterId.PyCodeStyle, this.configService), - new LinterInfo(Product.prospector, LinterId.Prospector, this.configService), - new LinterInfo(Product.pydocstyle, LinterId.PyDocStyle, this.configService), - new LinterInfo(Product.pylama, LinterId.PyLama, this.configService), - ]; - } - - public getAllLinterInfos(): ILinterInfo[] { - return this.linters; - } - - public getLinterInfo(product: Product): ILinterInfo { - const x = this.linters.findIndex((value, _index, _obj) => value.product === product); - if (x >= 0) { - return this.linters[x]; - } - throw new Error(`Invalid linter '${Product[product]}'`); - } - - public async isLintingEnabled(resource?: Uri): Promise { - const settings = this.configService.getSettings(resource); - const activeLintersPresent = await this.getActiveLinters(resource); - return settings.linting.enabled && activeLintersPresent.length > 0; - } - - public async enableLintingAsync(enable: boolean, resource?: Uri): Promise { - await this.configService.updateSetting('linting.enabled', enable, resource); - } - - public async getActiveLinters(resource?: Uri): Promise { - return this.linters.filter((x) => x.isEnabled(resource)); - } - - public async setActiveLintersAsync(products: Product[], resource?: Uri): Promise { - // ensure we only allow valid linters to be set, otherwise leave things alone. - // filter out any invalid products: - const validProducts = products.filter((product) => { - const foundIndex = this.linters.findIndex((validLinter) => validLinter.product === product); - return foundIndex !== -1; - }); - - // if we have valid linter product(s), enable only those - if (validProducts.length > 0) { - const active = await this.getActiveLinters(resource); - for (const x of active) { - await x.enableAsync(false, resource); - } - if (products.length > 0) { - const toActivate = this.linters.filter((x) => products.findIndex((p) => x.product === p) >= 0); - for (const x of toActivate) { - await x.enableAsync(true, resource); - } - await this.enableLintingAsync(true, resource); - } - } - } - - public async createLinter(product: Product, serviceContainer: IServiceContainer, resource?: Uri): Promise { - if (!(await this.isLintingEnabled(resource))) { - return new DisabledLinter(this.configService); - } - const error = 'Linter manager: Unknown linter'; - switch (product) { - case Product.bandit: - return new Bandit(serviceContainer); - case Product.flake8: - return new Flake8(serviceContainer, getOrCreateFlake8Prompt(serviceContainer)); - case Product.pylint: - return new Pylint(serviceContainer, getOrCreatePylintPrompt(serviceContainer)); - case Product.mypy: - return new MyPy(serviceContainer); - case Product.prospector: - return new Prospector(serviceContainer); - case Product.pylama: - return new PyLama(serviceContainer); - case Product.pydocstyle: - return new PyDocStyle(serviceContainer); - case Product.pycodestyle: - return new Pycodestyle(serviceContainer); - default: - traceError(error); - break; - } - throw new Error(error); - } -} diff --git a/src/client/linters/lintingEngine.ts b/src/client/linters/lintingEngine.ts deleted file mode 100644 index 2a4bf4e10848..000000000000 --- a/src/client/linters/lintingEngine.ts +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import { Minimatch } from 'minimatch'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import { ICommandManager, IDocumentManager, IWorkspaceService } from '../common/application/types'; -import { Commands } from '../common/constants'; -import { IFileSystem } from '../common/platform/types'; -import { IConfigurationService } from '../common/types'; -import { isNotebookCell, noop } from '../common/utils/misc'; -import { StopWatch } from '../common/utils/stopWatch'; -import { IInterpreterService } from '../interpreter/contracts'; -import { IServiceContainer } from '../ioc/types'; -import { sendTelemetryWhenDone } from '../telemetry'; -import { EventName } from '../telemetry/constants'; -import { LinterTrigger, LintingTelemetry } from '../telemetry/types'; -import { ILinterInfo, ILinterManager, ILintingEngine, ILintMessage, LintMessageSeverity } from './types'; - -const PYTHON: vscode.DocumentFilter = { language: 'python' }; - -const lintSeverityToVSSeverity = new Map(); -lintSeverityToVSSeverity.set(LintMessageSeverity.Error, vscode.DiagnosticSeverity.Error); -lintSeverityToVSSeverity.set(LintMessageSeverity.Hint, vscode.DiagnosticSeverity.Hint); -lintSeverityToVSSeverity.set(LintMessageSeverity.Information, vscode.DiagnosticSeverity.Information); -lintSeverityToVSSeverity.set(LintMessageSeverity.Warning, vscode.DiagnosticSeverity.Warning); - -@injectable() -export class LintingEngine implements ILintingEngine { - private workspace: IWorkspaceService; - - private documents: IDocumentManager; - - private configurationService: IConfigurationService; - - private linterManager: ILinterManager; - - private diagnosticCollection: vscode.DiagnosticCollection; - - private pendingLintings = new Map(); - - private fileSystem: IFileSystem; - - constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) { - this.documents = serviceContainer.get(IDocumentManager); - this.workspace = serviceContainer.get(IWorkspaceService); - this.configurationService = serviceContainer.get(IConfigurationService); - this.linterManager = serviceContainer.get(ILinterManager); - this.fileSystem = serviceContainer.get(IFileSystem); - this.diagnosticCollection = vscode.languages.createDiagnosticCollection('python'); - } - - public get diagnostics(): vscode.DiagnosticCollection { - return this.diagnosticCollection; - } - - public clearDiagnostics(document: vscode.TextDocument): void { - if (this.diagnosticCollection.has(document.uri)) { - this.diagnosticCollection.delete(document.uri); - } - } - - public async lintOpenPythonFiles(trigger: LinterTrigger = 'auto'): Promise { - this.diagnosticCollection.clear(); - const promises = this.documents.textDocuments.map(async (document) => this.lintDocument(document, trigger)); - await Promise.all(promises); - return this.diagnosticCollection; - } - - public async lintDocument(document: vscode.TextDocument, trigger: LinterTrigger): Promise { - if (isNotebookCell(document)) { - return; - } - this.diagnosticCollection.set(document.uri, []); - - // Check if we need to lint this document - if (!(await this.shouldLintDocument(document, trigger))) { - return; - } - - if (this.pendingLintings.has(document.uri.fsPath)) { - this.pendingLintings.get(document.uri.fsPath)!.cancel(); - this.pendingLintings.delete(document.uri.fsPath); - } - - const cancelToken = new vscode.CancellationTokenSource(); - cancelToken.token.onCancellationRequested(() => { - if (this.pendingLintings.has(document.uri.fsPath)) { - this.pendingLintings.delete(document.uri.fsPath); - } - }); - - this.pendingLintings.set(document.uri.fsPath, cancelToken); - - const activeLinters = await this.linterManager.getActiveLinters(document.uri); - const promises: Promise[] = activeLinters.map(async (info: ILinterInfo) => { - const stopWatch = new StopWatch(); - const linter = await this.linterManager.createLinter(info.product, this.serviceContainer, document.uri); - const promise = linter.lint(document, cancelToken.token); - this.sendLinterRunTelemetry(info, document.uri, promise, stopWatch, trigger); - return promise; - }); - - // linters will resolve asynchronously - keep a track of all - // diagnostics reported as them come in. - let diagnostics: vscode.Diagnostic[] = []; - const settings = this.configurationService.getSettings(document.uri); - - for (const p of promises) { - const msgs = await p; - if (cancelToken.token.isCancellationRequested) { - break; - } - - if (this.isDocumentOpen(document.uri)) { - // Build the message and suffix the message with the name of the linter used. - for (const m of msgs) { - diagnostics.push(this.createDiagnostics(m, document)); - } - // Limit the number of messages to the max value. - diagnostics = diagnostics.filter((_value, index) => index <= settings.linting.maxNumberOfProblems); - } - } - // Set all diagnostics found in this pass, as this method always clears existing diagnostics. - this.diagnosticCollection.set(document.uri, diagnostics); - } - - // eslint-disable-next-line class-methods-use-this - private sendLinterRunTelemetry( - info: ILinterInfo, - resource: vscode.Uri, - promise: Promise, - stopWatch: StopWatch, - trigger: LinterTrigger, - ): void { - const linterExecutablePathName = info.pathName(resource); - const properties: LintingTelemetry = { - tool: info.id, - hasCustomArgs: info.linterArgs(resource).length > 0, - trigger, - executableSpecified: linterExecutablePathName !== info.id, - }; - sendTelemetryWhenDone(EventName.LINTING, promise, stopWatch, properties); - } - - private isDocumentOpen(uri: vscode.Uri): boolean { - return this.documents.textDocuments.some((document) => document.uri.fsPath === uri.fsPath); - } - - // eslint-disable-next-line class-methods-use-this - private createDiagnostics(message: ILintMessage, _document: vscode.TextDocument): vscode.Diagnostic { - const position = new vscode.Position(message.line - 1, message.column); - let endPosition: vscode.Position = position; - if (message.endLine && message.endColumn) { - endPosition = new vscode.Position(message.endLine - 1, message.endColumn); - } - const range = new vscode.Range(position, endPosition); - - const severity = lintSeverityToVSSeverity.get(message.severity!)!; - const diagnostic = new vscode.Diagnostic(range, message.message, severity); - diagnostic.code = message.code; - diagnostic.source = message.provider; - return diagnostic; - } - - private async shouldLintDocument(document: vscode.TextDocument, trigger: LinterTrigger): Promise { - const interpreterService = this.serviceContainer.get(IInterpreterService); - const interpreter = await interpreterService.getActiveInterpreter(document.uri); - if (!interpreter && trigger === 'manual') { - this.serviceContainer - .get(ICommandManager) - .executeCommand(Commands.TriggerEnvironmentSelection, document.uri) - .then(noop, noop); - return false; - } - if (!(await this.linterManager.isLintingEnabled(document.uri))) { - this.diagnosticCollection.set(document.uri, []); - return false; - } - - if (document.languageId !== PYTHON.language) { - return false; - } - - const workspaceFolder = this.workspace.getWorkspaceFolder(document.uri); - const workspaceRootPath = - workspaceFolder && typeof workspaceFolder.uri.fsPath === 'string' ? workspaceFolder.uri.fsPath : undefined; - const relativeFileName = - typeof workspaceRootPath === 'string' - ? path.relative(workspaceRootPath, document.fileName) - : document.fileName; - - const settings = this.configurationService.getSettings(document.uri); - // { dot: true } is important so dirs like `.venv` will be matched by globs - const ignoreMinmatches = settings.linting.ignorePatterns.map( - (pattern) => new Minimatch(pattern, { dot: true }), - ); - if (ignoreMinmatches.some((matcher) => matcher.match(document.fileName) || matcher.match(relativeFileName))) { - return false; - } - if (document.uri.scheme !== 'file' || !document.uri.fsPath) { - return false; - } - return this.fileSystem.fileExists(document.uri.fsPath); - } -} diff --git a/src/client/linters/mypy.ts b/src/client/linters/mypy.ts deleted file mode 100644 index f39eef99b422..000000000000 --- a/src/client/linters/mypy.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { escapeRegExp } from 'lodash'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage } from './types'; - -export function getRegex(filepath: string): string { - return `${escapeRegExp(filepath)}:(?\\d+)(:(?\\d+))?: (?\\w+): (?.*)\\r?(\\n|$)`; -} -const COLUMN_OFF_SET = 1; - -export class MyPy extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.mypy, serviceContainer, COLUMN_OFF_SET); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const relativeFilePath = document.uri.fsPath.slice(this.getWorkspaceRootPath(document).length + 1); - const regex = getRegex(relativeFilePath); - const messages = await this.run([document.uri.fsPath], document, cancellation, regex); - messages.forEach((msg) => { - msg.severity = this.parseMessagesSeverity(msg.type, this.pythonSettings.linting.mypyCategorySeverity); - msg.code = msg.type; - }); - return messages; - } -} diff --git a/src/client/linters/prompts/common.ts b/src/client/linters/prompts/common.ts deleted file mode 100644 index ab88282db607..000000000000 --- a/src/client/linters/prompts/common.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { ShowToolsExtensionPrompt } from '../../common/experiments/groups'; -import { IExperimentService, IExtensions, IPersistentState, IPersistentStateFactory } from '../../common/types'; -import { IServiceContainer } from '../../ioc/types'; -import { traceLog } from '../../logging'; - -export function isExtensionDisabled(serviceContainer: IServiceContainer, extensionId: string): boolean { - const extensions: IExtensions = serviceContainer.get(IExtensions); - // When debugging the python extension this `extensionPath` below will point to your repo. - // If you are debugging this feature then set the `extensionPath` to right location after - // the next line. - const pythonExt = extensions.getExtension('ms-python.python'); - if (pythonExt) { - let found = false; - traceLog(`Extension search path: ${path.dirname(pythonExt.extensionPath)}`); - fs.readdirSync(path.dirname(pythonExt.extensionPath), { withFileTypes: false }).forEach((s) => { - if (s.toString().startsWith(extensionId)) { - found = true; - } - }); - return found; - } - return false; -} - -/** - * Detects if extension is installed and enabled. - */ -export function isExtensionEnabled(serviceContainer: IServiceContainer, extensionId: string): boolean { - const extensions: IExtensions = serviceContainer.get(IExtensions); - const extension = extensions.getExtension(extensionId); - return extension !== undefined; -} - -export function doNotShowPromptState( - serviceContainer: IServiceContainer, - promptKey: string, -): IPersistentState { - const persistFactory: IPersistentStateFactory = serviceContainer.get( - IPersistentStateFactory, - ); - return persistFactory.createWorkspacePersistentState(promptKey, false); -} - -export function inToolsExtensionsExperiment(serviceContainer: IServiceContainer): Promise { - const experiments: IExperimentService = serviceContainer.get(IExperimentService); - return experiments.inExperiment(ShowToolsExtensionPrompt.experiment); -} diff --git a/src/client/linters/prompts/flake8Prompt.ts b/src/client/linters/prompts/flake8Prompt.ts deleted file mode 100644 index fa1969df682a..000000000000 --- a/src/client/linters/prompts/flake8Prompt.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { IApplicationEnvironment } from '../../common/application/types'; -import { Common, ToolsExtensions } from '../../common/utils/localize'; -import { executeCommand } from '../../common/vscodeApis/commandApis'; -import { showInformationMessage } from '../../common/vscodeApis/windowApis'; -import { IServiceContainer } from '../../ioc/types'; -import { sendTelemetryEvent } from '../../telemetry'; -import { EventName } from '../../telemetry/constants'; -import { doNotShowPromptState, inToolsExtensionsExperiment, isExtensionDisabled, isExtensionEnabled } from './common'; -import { IToolsExtensionPrompt } from './types'; - -export const FLAKE8_EXTENSION = 'ms-python.flake8'; -const FLAKE8_PROMPT_DONOTSHOW_KEY = 'showFlake8ExtensionPrompt'; - -export class Flake8ExtensionPrompt implements IToolsExtensionPrompt { - private shownThisSession = false; - - public constructor(private readonly serviceContainer: IServiceContainer) {} - - public async showPrompt(): Promise { - const isEnabled = isExtensionEnabled(this.serviceContainer, FLAKE8_EXTENSION); - if (isEnabled || isExtensionDisabled(this.serviceContainer, FLAKE8_EXTENSION)) { - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_ALREADY_INSTALLED, undefined, { - extensionId: FLAKE8_EXTENSION, - isEnabled, - }); - return true; - } - - const doNotShow = doNotShowPromptState(this.serviceContainer, FLAKE8_PROMPT_DONOTSHOW_KEY); - if (this.shownThisSession || doNotShow.value) { - return false; - } - - if (!(await inToolsExtensionsExperiment(this.serviceContainer))) { - return false; - } - - this.shownThisSession = true; - const response = await showInformationMessage( - ToolsExtensions.flake8PromptMessage, - ToolsExtensions.installFlake8Extension, - Common.doNotShowAgain, - ); - - if (response === Common.doNotShowAgain) { - doNotShow.updateValue(true); - return false; - } - - if (response === ToolsExtensions.installFlake8Extension) { - const appEnv: IApplicationEnvironment = this.serviceContainer.get( - IApplicationEnvironment, - ); - await executeCommand('workbench.extensions.installExtension', FLAKE8_EXTENSION, { - installPreReleaseVersion: appEnv.extensionChannel === 'insiders', - }); - return true; - } - - return false; - } -} - -let _prompt: IToolsExtensionPrompt | undefined; -export function getOrCreateFlake8Prompt(serviceContainer: IServiceContainer): IToolsExtensionPrompt { - if (!_prompt) { - _prompt = new Flake8ExtensionPrompt(serviceContainer); - } - return _prompt; -} diff --git a/src/client/linters/prompts/pylintPrompt.ts b/src/client/linters/prompts/pylintPrompt.ts deleted file mode 100644 index 37e583243078..000000000000 --- a/src/client/linters/prompts/pylintPrompt.ts +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { IApplicationEnvironment } from '../../common/application/types'; -import { Common, ToolsExtensions } from '../../common/utils/localize'; -import { executeCommand } from '../../common/vscodeApis/commandApis'; -import { showInformationMessage } from '../../common/vscodeApis/windowApis'; -import { IServiceContainer } from '../../ioc/types'; -import { sendTelemetryEvent } from '../../telemetry'; -import { EventName } from '../../telemetry/constants'; -import { doNotShowPromptState, inToolsExtensionsExperiment, isExtensionDisabled, isExtensionEnabled } from './common'; -import { IToolsExtensionPrompt } from './types'; - -export const PYLINT_EXTENSION = 'ms-python.pylint'; -const PYLINT_PROMPT_DONOTSHOW_KEY = 'showPylintExtensionPrompt'; - -export class PylintExtensionPrompt implements IToolsExtensionPrompt { - private shownThisSession = false; - - public constructor(private readonly serviceContainer: IServiceContainer) {} - - public async showPrompt(): Promise { - const isEnabled = isExtensionEnabled(this.serviceContainer, PYLINT_EXTENSION); - if (isEnabled || isExtensionDisabled(this.serviceContainer, PYLINT_EXTENSION)) { - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_ALREADY_INSTALLED, undefined, { - extensionId: PYLINT_EXTENSION, - isEnabled, - }); - return true; - } - - const doNotShow = doNotShowPromptState(this.serviceContainer, PYLINT_PROMPT_DONOTSHOW_KEY); - if (this.shownThisSession || doNotShow.value) { - return false; - } - - if (!(await inToolsExtensionsExperiment(this.serviceContainer))) { - return false; - } - - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_PROMPT_SHOWN, undefined, { extensionId: PYLINT_EXTENSION }); - this.shownThisSession = true; - const response = await showInformationMessage( - ToolsExtensions.pylintPromptMessage, - ToolsExtensions.installPylintExtension, - Common.doNotShowAgain, - ); - - if (response === Common.doNotShowAgain) { - await doNotShow.updateValue(true); - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_PROMPT_DISMISSED, undefined, { - extensionId: PYLINT_EXTENSION, - dismissType: 'doNotShow', - }); - return false; - } - - if (response === ToolsExtensions.installPylintExtension) { - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_INSTALL_SELECTED, undefined, { - extensionId: PYLINT_EXTENSION, - }); - const appEnv: IApplicationEnvironment = this.serviceContainer.get( - IApplicationEnvironment, - ); - await executeCommand('workbench.extensions.installExtension', PYLINT_EXTENSION, { - installPreReleaseVersion: appEnv.extensionChannel === 'insiders', - }); - return true; - } - - sendTelemetryEvent(EventName.TOOLS_EXTENSIONS_PROMPT_DISMISSED, undefined, { - extensionId: PYLINT_EXTENSION, - dismissType: 'close', - }); - - return false; - } -} - -let _prompt: IToolsExtensionPrompt | undefined; -export function getOrCreatePylintPrompt(serviceContainer: IServiceContainer): IToolsExtensionPrompt { - if (!_prompt) { - _prompt = new PylintExtensionPrompt(serviceContainer); - } - return _prompt; -} diff --git a/src/client/linters/prompts/types.ts b/src/client/linters/prompts/types.ts deleted file mode 100644 index d7c884b3a00d..000000000000 --- a/src/client/linters/prompts/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -export interface IToolsExtensionPrompt { - showPrompt(): Promise; -} diff --git a/src/client/linters/prospector.ts b/src/client/linters/prospector.ts deleted file mode 100644 index fa4b3907255b..000000000000 --- a/src/client/linters/prospector.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as path from 'path'; -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceError, traceLog } from '../logging'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage } from './types'; - -interface IProspectorResponse { - messages: IProspectorMessage[]; -} -interface IProspectorMessage { - source: string; - message: string; - code: string; - location: IProspectorLocation; -} -interface IProspectorLocation { - function: string; - path: string; - line: number; - character: number; - module: 'beforeFormat'; -} - -export class Prospector extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.prospector, serviceContainer); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const cwd = this.getWorkingDirectoryPath(document); - const relativePath = path.relative(cwd, document.uri.fsPath); - return this.run([relativePath], document, cancellation); - } - - protected async parseMessages( - output: string, - _document: TextDocument, - _token: CancellationToken, - _regEx: string, - ): Promise { - let parsedData: IProspectorResponse; - try { - parsedData = JSON.parse(output); - } catch (ex) { - traceLog(`${'#'.repeat(10)}Linting Output - ${this.info.id}${'#'.repeat(10)}`); - traceLog(output); - traceError('Failed to parse Prospector output', ex); - return []; - } - return parsedData.messages - .filter((_value, index) => index <= this.pythonSettings.linting.maxNumberOfProblems) - .map((msg) => { - const lineNumber = - msg.location.line === null || Number.isNaN(msg.location.line) ? 1 : msg.location.line; - - return { - code: msg.code, - message: msg.message, - column: msg.location.character, - line: lineNumber, - type: msg.code, - provider: `${this.info.id} - ${msg.source}`, - }; - }); - } -} diff --git a/src/client/linters/pycodestyle.ts b/src/client/linters/pycodestyle.ts deleted file mode 100644 index 30517980e83c..000000000000 --- a/src/client/linters/pycodestyle.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage } from './types'; - -const COLUMN_OFF_SET = 1; - -export class Pycodestyle extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.pycodestyle, serviceContainer, COLUMN_OFF_SET); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const messages = await this.run([document.uri.fsPath], document, cancellation); - messages.forEach((msg) => { - msg.severity = this.parseMessagesSeverity( - msg.type, - this.pythonSettings.linting.pycodestyleCategorySeverity, - ); - }); - return messages; - } -} diff --git a/src/client/linters/pydocstyle.ts b/src/client/linters/pydocstyle.ts deleted file mode 100644 index 4851190a92ac..000000000000 --- a/src/client/linters/pydocstyle.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as path from 'path'; -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceError } from '../logging'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage, LintMessageSeverity } from './types'; -import { isWindows } from '../common/platform/platformService'; - -export class PyDocStyle extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.pydocstyle, serviceContainer); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const messages = await this.run([document.uri.fsPath], document, cancellation); - // All messages in pep8 are treated as warnings for now. - messages.forEach((msg) => { - msg.severity = LintMessageSeverity.Warning; - }); - - return messages; - } - - protected async parseMessages( - output: string, - document: TextDocument, - _token: CancellationToken, - _regEx: string, - ): Promise { - let outputLines = output.split(/\r?\n/g); - const baseFileName = path.basename(document.uri.fsPath); - - // Remember, the first line of the response contains the file name and line number, the next line contains the error message. - // So we have two lines per message, hence we need to take lines in pairs. - const maxLines = this.pythonSettings.linting.maxNumberOfProblems * 2; - // First line is almost always empty. - const oldOutputLines = outputLines.filter((line) => line.length > 0); - outputLines = []; - for (let counter = 0; counter < oldOutputLines.length / 2; counter += 1) { - outputLines.push(oldOutputLines[2 * counter] + oldOutputLines[2 * counter + 1]); - } - - return ( - outputLines - .filter((value, index) => index < maxLines && value.indexOf(':') >= 0) - .map((line) => { - // Windows will have a : after the drive letter (e.g. c:\). - if (isWindows()) { - return line.substring(line.indexOf(`${baseFileName}:`) + baseFileName.length + 1).trim(); - } - return line.substring(line.indexOf(':') + 1).trim(); - }) - // Iterate through the lines (skipping the messages). - // So, just iterate the response in pairs. - .map((line) => { - try { - if (line.trim().length === 0) { - return undefined; - } - const lineNumber = parseInt(line.substring(0, line.indexOf(' ')), 10); - const part = line.substring(line.indexOf(':') + 1).trim(); - const code = part.substring(0, part.indexOf(':')).trim(); - const message = part.substring(part.indexOf(':') + 1).trim(); - - const sourceLine = document.lineAt(lineNumber - 1).text; - const trimmedSourceLine = sourceLine.trim(); - const sourceStart = sourceLine.indexOf(trimmedSourceLine); - - return { - code, - message, - column: sourceStart, - line: lineNumber, - type: '', - provider: this.info.id, - } as ILintMessage; - } catch (ex) { - traceError(`Failed to parse pydocstyle line '${line}'`, ex); - } - - return undefined; - }) - .filter((item) => item !== undefined) - .map((item) => item!) - ); - } -} diff --git a/src/client/linters/pylama.ts b/src/client/linters/pylama.ts deleted file mode 100644 index d5930c839445..000000000000 --- a/src/client/linters/pylama.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { BaseLinter } from './baseLinter'; -import { ILintMessage, LintMessageSeverity } from './types'; - -/** - * Example messages to parse from PyLama - * 1. Linter: pycodestyle - recent version removed an extra colon (:) after line:col, hence made it optional in the regex (to be backward compatibile) - * `src/test_py.py:23:60 [E] E226 missing whitespace around arithmetic operator [pycodestyle]` - * 2. Linter: mypy - output is missing the error code, something like `E226` - hence made it optional in the regex - * `src/test_py.py:7:4 [E] Argument 1 to "fn" has incompatible type "str"; expected "int" [mypy]` - */ - -const REGEX = - '(?.py):(?\\d+):(?\\d+):? \\[(?\\w+)\\]( (?\\w\\d+)?:?)? (?.*)\\r?(\\n|$)'; -const COLUMN_OFF_SET = 1; - -export class PyLama extends BaseLinter { - constructor(serviceContainer: IServiceContainer) { - super(Product.pylama, serviceContainer, COLUMN_OFF_SET); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - const messages = await this.run([document.uri.fsPath], document, cancellation, REGEX); - // All messages in pylama are treated as warnings for now. - messages.forEach((msg) => { - msg.severity = LintMessageSeverity.Warning; - }); - - return messages; - } -} diff --git a/src/client/linters/pylint.ts b/src/client/linters/pylint.ts deleted file mode 100644 index 0b635417f906..000000000000 --- a/src/client/linters/pylint.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { CancellationToken, TextDocument } from 'vscode'; -import '../common/extensions'; -import { Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { traceError, traceLog } from '../logging'; -import { BaseLinter } from './baseLinter'; -import { isExtensionEnabled } from './prompts/common'; -import { PYLINT_EXTENSION } from './prompts/pylintPrompt'; -import { IToolsExtensionPrompt } from './prompts/types'; -import { ILintMessage } from './types'; - -interface IJsonMessage { - column: number | null; - line: number; - message: string; - symbol: string; - type: string; - endLine?: number | null; - endColumn?: number | null; -} - -export class Pylint extends BaseLinter { - constructor(serviceContainer: IServiceContainer, private readonly prompt: IToolsExtensionPrompt) { - super(Product.pylint, serviceContainer); - } - - protected async runLinter(document: TextDocument, cancellation: CancellationToken): Promise { - await this.prompt.showPrompt(); - - if (isExtensionEnabled(this.serviceContainer, PYLINT_EXTENSION)) { - traceLog( - 'LINTING: Skipping linting from Python extension, since Pylint extension is installed and enabled.', - ); - return []; - } - - const { uri } = document; - const settings = this.configService.getSettings(uri); - const args = [uri.fsPath]; - const messages = await this.run(args, document, cancellation); - messages.forEach((msg) => { - msg.severity = this.parseMessagesSeverity(msg.type, settings.linting.pylintCategorySeverity); - }); - return messages; - } - - private parseOutputMessage(outputMsg: IJsonMessage, colOffset = 0): ILintMessage | undefined { - // Both 'endLine' and 'endColumn' are only present on pylint 2.12.2+ - // If present, both can still be 'null' if AST node didn't have endLine and / or endColumn information. - // If 'endColumn' is 'null' or not preset, set it to 'undefined' to - // prevent the lintingEngine from inferring an error range. - if (outputMsg.endColumn) { - outputMsg.endColumn = outputMsg.endColumn <= 0 ? 0 : outputMsg.endColumn - colOffset; - } else { - outputMsg.endColumn = undefined; - } - - return { - code: outputMsg.symbol, - message: outputMsg.message, - column: outputMsg.column === null || outputMsg.column <= 0 ? 0 : outputMsg.column - colOffset, - line: outputMsg.line, - type: outputMsg.type, - provider: this.info.id, - endLine: outputMsg.endLine === null ? undefined : outputMsg.endLine, - endColumn: outputMsg.endColumn, - }; - } - - protected async parseMessages( - output: string, - _document: TextDocument, - _token: CancellationToken, - _: string, - ): Promise { - const messages: ILintMessage[] = []; - try { - const parsedOutput: IJsonMessage[] = JSON.parse(output); - for (const outputMsg of parsedOutput) { - const msg = this.parseOutputMessage(outputMsg, this.columnOffset); - if (msg) { - messages.push(msg); - if (messages.length >= this.pythonSettings.linting.maxNumberOfProblems) { - break; - } - } - } - } catch (ex) { - traceError(`Linter '${this.info.id}' failed to parse the output '${output}.`, ex); - } - return messages; - } -} diff --git a/src/client/linters/serviceRegistry.ts b/src/client/linters/serviceRegistry.ts deleted file mode 100644 index 26ada4d0cc8f..000000000000 --- a/src/client/linters/serviceRegistry.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { IExtensionActivationService } from '../activation/types'; -import { IServiceManager } from '../ioc/types'; -import { LinterProvider } from '../providers/linterProvider'; -import { LinterManager } from './linterManager'; -import { LintingEngine } from './lintingEngine'; -import { ILinterManager, ILintingEngine } from './types'; - -export function registerTypes(serviceManager: IServiceManager): void { - serviceManager.addSingleton(ILintingEngine, LintingEngine); - serviceManager.addSingleton(ILinterManager, LinterManager); - serviceManager.addSingleton(IExtensionActivationService, LinterProvider); -} diff --git a/src/client/linters/types.ts b/src/client/linters/types.ts deleted file mode 100644 index b24fe508ea1c..000000000000 --- a/src/client/linters/types.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as vscode from 'vscode'; -import { ExecutionInfo, Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { LinterTrigger } from '../telemetry/types'; - -export interface IErrorHandler { - handleError(error: Error, resource: vscode.Uri, execInfo: ExecutionInfo): Promise; -} - -export enum LinterId { - Flake8 = 'flake8', - MyPy = 'mypy', - PyCodeStyle = 'pycodestyle', - Prospector = 'prospector', - PyDocStyle = 'pydocstyle', - PyLama = 'pylama', - PyLint = 'pylint', - Bandit = 'bandit', -} - -export interface ILinterInfo { - readonly id: LinterId; - readonly product: Product; - readonly pathSettingName: string; - readonly argsSettingName: string; - readonly enabledSettingName: string; - readonly configFileNames: string[]; - enableAsync(enabled: boolean, resource?: vscode.Uri): Promise; - isEnabled(resource?: vscode.Uri): boolean; - pathName(resource?: vscode.Uri): string; - linterArgs(resource?: vscode.Uri): string[]; - getExecutionInfo(customArgs: string[], resource?: vscode.Uri): ExecutionInfo; -} - -export interface ILinter { - readonly info: ILinterInfo; - lint(document: vscode.TextDocument, cancellation: vscode.CancellationToken): Promise; -} - -export const ILinterManager = Symbol('ILinterManager'); -export interface ILinterManager { - getAllLinterInfos(): ILinterInfo[]; - getLinterInfo(product: Product): ILinterInfo; - getActiveLinters(resource?: vscode.Uri): Promise; - isLintingEnabled(resource?: vscode.Uri): Promise; - enableLintingAsync(enable: boolean, resource?: vscode.Uri): Promise; - setActiveLintersAsync(products: Product[], resource?: vscode.Uri): Promise; - createLinter(product: Product, serviceContainer: IServiceContainer, resource?: vscode.Uri): Promise; -} - -export interface ILintMessage { - line: number; - column: number; - endLine?: number; - endColumn?: number; - code: string | undefined; - message: string; - type: string; - severity?: LintMessageSeverity; - provider: string; -} -export enum LintMessageSeverity { - Hint, - Error, - Warning, - Information, -} - -export const ILintingEngine = Symbol('ILintingEngine'); -export interface ILintingEngine { - readonly diagnostics: vscode.DiagnosticCollection; - lintOpenPythonFiles(trigger?: LinterTrigger): Promise; - lintDocument(document: vscode.TextDocument, trigger: LinterTrigger): Promise; - clearDiagnostics(document: vscode.TextDocument): void; -} diff --git a/src/client/providers/linterProvider.ts b/src/client/providers/linterProvider.ts deleted file mode 100644 index 7821eaeccd53..000000000000 --- a/src/client/providers/linterProvider.ts +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { inject, injectable } from 'inversify'; -import * as path from 'path'; -import { ConfigurationChangeEvent, Disposable, TextDocument, Uri, workspace } from 'vscode'; -import { IExtensionActivationService } from '../activation/types'; -import { IDocumentManager, IWorkspaceService } from '../common/application/types'; -import { isTestExecution } from '../common/constants'; -import '../common/extensions'; -import { IFileSystem } from '../common/platform/types'; -import { IConfigurationService, IDisposable } from '../common/types'; -import { IInterpreterService } from '../interpreter/contracts'; -import { IServiceContainer } from '../ioc/types'; -import { ILinterManager, ILintingEngine } from '../linters/types'; - -@injectable() -export class LinterProvider implements IExtensionActivationService, Disposable { - public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false }; - - private interpreterService: IInterpreterService; - - private documents: IDocumentManager; - - private configuration: IConfigurationService; - - private linterManager: ILinterManager; - - private engine: ILintingEngine; - - private fs: IFileSystem; - - private readonly disposables: IDisposable[] = []; - - private workspaceService: IWorkspaceService; - - private activatedOnce = false; - - constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) { - this.serviceContainer = serviceContainer; - this.fs = this.serviceContainer.get(IFileSystem); - this.engine = this.serviceContainer.get(ILintingEngine); - this.linterManager = this.serviceContainer.get(ILinterManager); - this.interpreterService = this.serviceContainer.get(IInterpreterService); - this.documents = this.serviceContainer.get(IDocumentManager); - this.configuration = this.serviceContainer.get(IConfigurationService); - this.workspaceService = this.serviceContainer.get(IWorkspaceService); - } - - public async activate(): Promise { - if (this.activatedOnce) { - return; - } - this.activatedOnce = true; - this.disposables.push(this.interpreterService.onDidChangeInterpreter(() => this.engine.lintOpenPythonFiles())); - - this.documents.onDidOpenTextDocument((e) => this.onDocumentOpened(e), this.disposables); - this.documents.onDidCloseTextDocument((e) => this.onDocumentClosed(e), this.disposables); - this.documents.onDidSaveTextDocument((e) => this.onDocumentSaved(e), this.disposables); - - const disposable = this.workspaceService.onDidChangeConfiguration(this.lintSettingsChangedHandler.bind(this)); - this.disposables.push(disposable); - - // On workspace reopen we don't get `onDocumentOpened` since it is first opened - // and then the extension is activated. So schedule linting pass now. - if (!isTestExecution()) { - const timer = setTimeout(() => this.engine.lintOpenPythonFiles().ignoreErrors(), 1200); - this.disposables.push({ dispose: () => clearTimeout(timer) }); - } - } - - public dispose(): void { - this.disposables.forEach((d) => d.dispose()); - } - - private isDocumentOpen(uri: Uri): boolean { - return this.documents.textDocuments.some((document) => this.fs.arePathsSame(document.uri.fsPath, uri.fsPath)); - } - - private lintSettingsChangedHandler(e: ConfigurationChangeEvent) { - // Look for python files that belong to the specified workspace folder. - workspace.textDocuments.forEach((document) => { - if (e.affectsConfiguration('python.linting', document.uri)) { - this.engine.lintDocument(document, 'auto').ignoreErrors(); - } - }); - } - - private onDocumentOpened(document: TextDocument): void { - this.engine.lintDocument(document, 'auto').ignoreErrors(); - } - - private onDocumentSaved(document: TextDocument): void { - const settings = this.configuration.getSettings(document.uri); - if (document.languageId === 'python' && settings.linting.enabled && settings.linting.lintOnSave) { - this.engine.lintDocument(document, 'save').ignoreErrors(); - return; - } - - this.linterManager - .getActiveLinters(document.uri) - .then((linters) => { - const fileName = path.basename(document.uri.fsPath).toLowerCase(); - const watchers = linters.filter((info) => info.configFileNames.indexOf(fileName) >= 0); - if (watchers.length > 0) { - setTimeout(() => this.engine.lintOpenPythonFiles(), 1000); - } - }) - .ignoreErrors(); - } - - private onDocumentClosed(document: TextDocument) { - if (!document || !document.fileName || !document.uri) { - return; - } - // Check if this document is still open as a duplicate editor. - if (!this.isDocumentOpen(document.uri)) { - this.engine.clearDiagnostics(document); - } - } -} diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index 301502a0f6fa..de0980ada257 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -6,7 +6,6 @@ export enum EventName { FORMAT_ON_TYPE = 'FORMAT.FORMAT_ON_TYPE', EDITOR_LOAD = 'EDITOR.LOAD', - LINTING = 'LINTING', REPL = 'REPL', CREATE_NEW_FILE_COMMAND = 'CREATE_NEW_FILE_COMMAND', SELECT_INTERPRETER = 'SELECT_INTERPRETER', @@ -78,10 +77,8 @@ export enum EventName { DIAGNOSTICS_ACTION = 'DIAGNOSTICS.ACTION', DIAGNOSTICS_MESSAGE = 'DIAGNOSTICS.MESSAGE', - SELECT_LINTER = 'LINTING.SELECT', USE_REPORT_ISSUE_COMMAND = 'USE_REPORT_ISSUE_COMMAND', - LINTER_NOT_INSTALLED_PROMPT = 'LINTER_NOT_INSTALLED_PROMPT', HASHED_PACKAGE_NAME = 'HASHED_PACKAGE_NAME', JEDI_LANGUAGE_SERVER_ENABLED = 'JEDI_LANGUAGE_SERVER.ENABLED', @@ -115,11 +112,6 @@ export enum EventName { ENVIRONMENT_CHECK_TRIGGER = 'ENVIRONMENT.CHECK.TRIGGER', ENVIRONMENT_CHECK_RESULT = 'ENVIRONMENT.CHECK.RESULT', - - TOOLS_EXTENSIONS_ALREADY_INSTALLED = 'TOOLS_EXTENSIONS.ALREADY_INSTALLED', - TOOLS_EXTENSIONS_PROMPT_SHOWN = 'TOOLS_EXTENSIONS.PROMPT_SHOWN', - TOOLS_EXTENSIONS_INSTALL_SELECTED = 'TOOLS_EXTENSIONS.INSTALL_SELECTED', - TOOLS_EXTENSIONS_PROMPT_DISMISSED = 'TOOLS_EXTENSIONS.PROMPT_DISMISSED', } export enum PlatformErrors { diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index ba65c4d1913f..cc600d2d59a4 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -13,7 +13,6 @@ import { StopWatch } from '../common/utils/stopWatch'; import { isPromise } from '../common/utils/async'; import { DebugConfigurationType } from '../debugger/extension/types'; import { ConsoleType, TriggerType } from '../debugger/types'; -import { LinterId } from '../linters/types'; import { EnvironmentType, PythonEnvironment } from '../pythonEnvironments/info'; import { TensorBoardPromptSelection, @@ -22,7 +21,7 @@ import { TensorBoardEntrypoint, } from '../tensorBoard/constants'; import { EventName } from './constants'; -import type { LinterTrigger, TestTool } from './types'; +import type { TestTool } from './types'; /** * Checks whether telemetry is supported. @@ -894,33 +893,6 @@ export interface IEventNamePropertyMapping { hashedName: string; }; - /** - * Telemetry event sent with details of selection in prompt - * `Prompt message` :- 'Linter ${productName} is not installed' - */ - /* __GDPR__ - "linter_not_installed_prompt" : { - "tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "action": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" } - } - */ - [EventName.LINTER_NOT_INSTALLED_PROMPT]: { - /** - * Name of the linter - * - * @type {LinterId} - */ - tool?: LinterId; - /** - * `select` When 'Select linter' option is selected - * `disablePrompt` When "Don't show again" option is selected - * `install` When 'Install' option is selected - * - * @type {('select' | 'disablePrompt' | 'install')} - */ - action: 'select' | 'disablePrompt' | 'install'; - }; - /** * Telemetry event sent when installing modules */ @@ -961,44 +933,6 @@ export interface IEventNamePropertyMapping { */ version?: string; }; - /** - * Telemetry sent with details immediately after linting a document completes - */ - /* __GDPR__ - "linting" : { - "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "karthiknadig" }, - "tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "hascustomargs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "trigger" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "executablespecified" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" } - } - */ - [EventName.LINTING]: { - /** - * Name of the linter being used - * - * @type {LinterId} - */ - tool: LinterId; - /** - * If custom arguments for linter is provided in settings.json - * - * @type {boolean} - */ - hasCustomArgs: boolean; - /** - * Carries the source which triggered configuration of tests - * - * @type {LinterTrigger} - */ - trigger: LinterTrigger; - /** - * Carries `true` if linter executable is specified, `false` otherwise - * - * @type {boolean} - */ - executableSpecified: boolean; - }; /** * Telemetry event sent when an environment without contain a python binary is selected. */ @@ -1545,25 +1479,6 @@ export interface IEventNamePropertyMapping { } */ [EventName.REPL]: never | undefined; - /** - * Telemetry event sent with details of linter selected in quickpick of linter list. - */ - /* __GDPR__ - "linting.select" : { - "tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" }, - "enabled" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "karthiknadig" } - } - */ - [EventName.SELECT_LINTER]: { - /** - * The name of the linter - */ - tool?: LinterId; - /** - * Carries `true` if linter is enabled, `false` otherwise - */ - enabled: boolean; - }; /** * Telemetry event sent if and when user configure tests command. This command can be trigerred from multiple places in the extension. (Command palette, prompt etc.) */ @@ -2135,53 +2050,6 @@ export interface IEventNamePropertyMapping { [EventName.ENVIRONMENT_CHECK_RESULT]: { result: 'criteria-met' | 'criteria-not-met' | 'already-ran' | 'turned-off' | 'no-uri'; }; - /** - * Telemetry event sent when a linter or formatter extension is already installed. - */ - /* __GDPR__ - "tools_extensions.already_installed" : { - "extensionId" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "karthiknadig" } - } - */ - [EventName.TOOLS_EXTENSIONS_ALREADY_INSTALLED]: { - extensionId: 'ms-python.pylint' | 'ms-python.flake8'; - isEnabled: boolean; - }; - /** - * Telemetry event sent when install linter or formatter extension prompt is shown. - */ - /* __GDPR__ - "tools_extensions.prompt_shown" : { - "extensionId" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "karthiknadig" } - } - */ - [EventName.TOOLS_EXTENSIONS_PROMPT_SHOWN]: { - extensionId: 'ms-python.pylint' | 'ms-python.flake8'; - }; - /** - * Telemetry event sent when clicking to install linter or formatter extension from the suggestion prompt. - */ - /* __GDPR__ - "tools_extensions.install_selected" : { - "extensionId" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "karthiknadig" } - } - */ - [EventName.TOOLS_EXTENSIONS_INSTALL_SELECTED]: { - extensionId: 'ms-python.pylint' | 'ms-python.flake8'; - }; - /** - * Telemetry event sent when dismissing prompt suggesting to install the linter or formatter extension. - */ - /* __GDPR__ - "tools_extensions.prompt_dismissed" : { - "extensionId" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "karthiknadig" }, - "dismissType" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "owner": "karthiknadig" } - } - */ - [EventName.TOOLS_EXTENSIONS_PROMPT_DISMISSED]: { - extensionId: 'ms-python.pylint' | 'ms-python.flake8'; - dismissType: 'close' | 'doNotShow'; - }; /* __GDPR__ "query-expfeature" : { "owner": "luabud", diff --git a/src/client/telemetry/types.ts b/src/client/telemetry/types.ts index ae98707d94a8..865dca278bf0 100644 --- a/src/client/telemetry/types.ts +++ b/src/client/telemetry/types.ts @@ -8,10 +8,6 @@ import { EventName } from './constants'; export type EditorLoadTelemetry = IEventNamePropertyMapping[EventName.EDITOR_LOAD]; -export type LinterTrigger = 'auto' | 'save' | 'manual'; - -export type LintingTelemetry = IEventNamePropertyMapping[EventName.LINTING]; - export type PythonInterpreterTelemetry = IEventNamePropertyMapping[EventName.PYTHON_INTERPRETER]; export type DebuggerTelemetry = IEventNamePropertyMapping[EventName.DEBUGGER]; export type TestTool = 'pytest' | 'unittest'; diff --git a/src/test/common.ts b/src/test/common.ts index 4cc985c795b6..2ef366a3a472 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -40,23 +40,12 @@ export enum OSType { export type PythonSettingKeys = | 'defaultInterpreterPath' | 'languageServer' - | 'linting.lintOnSave' - | 'linting.enabled' - | 'linting.pylintEnabled' - | 'linting.flake8Enabled' - | 'linting.pycodestyleEnabled' - | 'linting.pylamaEnabled' - | 'linting.prospectorEnabled' - | 'linting.pydocstyleEnabled' - | 'linting.mypyEnabled' - | 'linting.banditEnabled' | 'testing.pytestArgs' | 'testing.unittestArgs' | 'formatting.provider' | 'testing.pytestEnabled' | 'testing.unittestEnabled' | 'envFile' - | 'linting.ignorePatterns' | 'terminal.activateEnvironment'; async function disposePythonSettings() { diff --git a/src/test/common/configSettings/configSettings.unit.test.ts b/src/test/common/configSettings/configSettings.unit.test.ts index e43ac7b7fbd8..83b5b4a3d524 100644 --- a/src/test/common/configSettings/configSettings.unit.test.ts +++ b/src/test/common/configSettings/configSettings.unit.test.ts @@ -20,7 +20,6 @@ import { IAutoCompleteSettings, IExperiments, IInterpreterSettings, - ILintingSettings, ITerminalSettings, } from '../../../client/common/types'; import { noop } from '../../../client/common/utils/misc'; @@ -115,7 +114,6 @@ suite('Python Settings', async () => { // complex settings config.setup((c) => c.get('interpreter')).returns(() => sourceSettings.interpreter); - config.setup((c) => c.get('linting')).returns(() => sourceSettings.linting); config.setup((c) => c.get('autoComplete')).returns(() => sourceSettings.autoComplete); config.setup((c) => c.get('testing')).returns(() => sourceSettings.testing); config.setup((c) => c.get('terminal')).returns(() => sourceSettings.terminal); diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts deleted file mode 100644 index 9523572ccfe2..000000000000 --- a/src/test/common/installer.test.ts +++ /dev/null @@ -1,331 +0,0 @@ -import * as path from 'path'; -import { instance, mock } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { ConfigurationTarget, Uri } from 'vscode'; -import { IExtensionSingleActivationService } from '../../client/activation/types'; -import { ActiveResourceService } from '../../client/common/application/activeResource'; -import { ApplicationEnvironment } from '../../client/common/application/applicationEnvironment'; -import { ClipboardService } from '../../client/common/application/clipboard'; -import { ReloadVSCodeCommandHandler } from '../../client/common/application/commands/reloadCommand'; -import { ReportIssueCommandHandler } from '../../client/common/application/commands/reportIssueCommand'; -import { DebugService } from '../../client/common/application/debugService'; -import { DebugSessionTelemetry } from '../../client/common/application/debugSessionTelemetry'; -import { DocumentManager } from '../../client/common/application/documentManager'; -import { Extensions } from '../../client/common/application/extensions'; -import { - IActiveResourceService, - IApplicationEnvironment, - IApplicationShell, - IClipboard, - ICommandManager, - IDebugService, - IDocumentManager, - IWorkspaceService, -} from '../../client/common/application/types'; -import { WorkspaceService } from '../../client/common/application/workspace'; -import { ConfigurationService } from '../../client/common/configuration/service'; -import { ExperimentService } from '../../client/common/experiments/service'; -import { InstallationChannelManager } from '../../client/common/installer/channelManager'; -import { ProductInstaller } from '../../client/common/installer/productInstaller'; -import { LinterProductPathService, TestFrameworkProductPathService } from '../../client/common/installer/productPath'; -import { ProductService } from '../../client/common/installer/productService'; -import { - IInstallationChannelManager, - IModuleInstaller, - IProductPathService, - IProductService, -} from '../../client/common/installer/types'; -import { InterpreterPathService } from '../../client/common/interpreterPathService'; -import { BrowserService } from '../../client/common/net/browser'; -import { PersistentStateFactory } from '../../client/common/persistentState'; -import { PathUtils } from '../../client/common/platform/pathUtils'; -import { CurrentProcess } from '../../client/common/process/currentProcess'; -import { ProcessLogger } from '../../client/common/process/logger'; -import { IProcessLogger, IProcessServiceFactory } from '../../client/common/process/types'; -import { TerminalActivator } from '../../client/common/terminal/activator'; -import { PowershellTerminalActivationFailedHandler } from '../../client/common/terminal/activator/powershellFailedHandler'; -import { Bash } from '../../client/common/terminal/environmentActivationProviders/bash'; -import { CommandPromptAndPowerShell } from '../../client/common/terminal/environmentActivationProviders/commandPrompt'; -import { Nushell } from '../../client/common/terminal/environmentActivationProviders/nushell'; -import { CondaActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/condaActivationProvider'; -import { PipEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pipEnvActivationProvider'; -import { PyEnvActivationCommandProvider } from '../../client/common/terminal/environmentActivationProviders/pyenvActivationProvider'; -import { TerminalServiceFactory } from '../../client/common/terminal/factory'; -import { TerminalHelper } from '../../client/common/terminal/helper'; -import { SettingsShellDetector } from '../../client/common/terminal/shellDetectors/settingsShellDetector'; -import { TerminalNameShellDetector } from '../../client/common/terminal/shellDetectors/terminalNameShellDetector'; -import { UserEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/userEnvironmentShellDetector'; -import { VSCEnvironmentShellDetector } from '../../client/common/terminal/shellDetectors/vscEnvironmentShellDetector'; -import { - IShellDetector, - ITerminalActivationCommandProvider, - ITerminalActivationHandler, - ITerminalActivator, - ITerminalHelper, - ITerminalServiceFactory, - TerminalActivationProviders, -} from '../../client/common/terminal/types'; -import { - IBrowserService, - IConfigurationService, - ICurrentProcess, - IExperimentService, - IExtensions, - IInstaller, - IInterpreterPathService, - IPathUtils, - IPersistentStateFactory, - IRandom, - IsWindows, - Product, - ProductType, -} from '../../client/common/types'; -import { createDeferred } from '../../client/common/utils/async'; -import { IMultiStepInputFactory, MultiStepInputFactory } from '../../client/common/utils/multiStepInput'; -import { Random } from '../../client/common/utils/random'; -import { ImportTracker } from '../../client/telemetry/importTracker'; -import { IImportTracker } from '../../client/telemetry/types'; -import { rootWorkspaceUri, updateSetting } from '../common'; -import { MockModuleInstaller } from '../mocks/moduleInstaller'; -import { MockProcessService } from '../mocks/proc'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; -import { closeActiveWindows, initializeTest, IS_MULTI_ROOT_TEST, TEST_TIMEOUT } from '../initialize'; -import { IActivatedEnvironmentLaunch } from '../../client/interpreter/contracts'; -import { ActivatedEnvironmentLaunch } from '../../client/interpreter/virtualEnvs/activatedEnvLaunch'; -import { - IPythonPathUpdaterServiceFactory, - IPythonPathUpdaterServiceManager, -} from '../../client/interpreter/configuration/types'; -import { PythonPathUpdaterService } from '../../client/interpreter/configuration/pythonPathUpdaterService'; -import { PythonPathUpdaterServiceFactory } from '../../client/interpreter/configuration/pythonPathUpdaterServiceFactory'; -import { getProductsForInstallerTests } from './productsToTest'; - -suite('Installer', () => { - let ioc: UnitTestIocContainer; - const workspaceUri = Uri.file(path.join(__dirname, '..', '..', '..', 'src', 'test')); - const resource = IS_MULTI_ROOT_TEST ? workspaceUri : undefined; - suiteSetup(initializeTest); - setup(async () => { - await initializeTest(); - await resetSettings(); - await initializeDI(); - }); - suiteTeardown(async () => { - await closeActiveWindows(); - await resetSettings(); - }); - teardown(async () => { - await ioc.dispose(); - await closeActiveWindows(); - }); - - async function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerUnitTestTypes(); - ioc.registerFileSystemTypes(); - ioc.registerVariableTypes(); - ioc.registerLinterTypes(); - ioc.registerInterpreterStorageTypes(); - - ioc.serviceManager.addSingleton(IPersistentStateFactory, PersistentStateFactory); - ioc.serviceManager.addSingleton(IInstaller, ProductInstaller); - ioc.serviceManager.addSingleton(IPathUtils, PathUtils); - ioc.serviceManager.addSingleton(IProcessLogger, ProcessLogger); - ioc.serviceManager.addSingleton(ICurrentProcess, CurrentProcess); - ioc.serviceManager.addSingleton( - IInstallationChannelManager, - InstallationChannelManager, - ); - ioc.serviceManager.addSingletonInstance( - ICommandManager, - TypeMoq.Mock.ofType().object, - ); - - ioc.serviceManager.addSingletonInstance( - IApplicationShell, - TypeMoq.Mock.ofType().object, - ); - ioc.serviceManager.addSingleton(IConfigurationService, ConfigurationService); - ioc.serviceManager.addSingleton(IWorkspaceService, WorkspaceService); - - await ioc.registerMockInterpreterTypes(); - ioc.registerMockProcessTypes(); - ioc.serviceManager.addSingletonInstance(IsWindows, false); - ioc.serviceManager.addSingletonInstance(IProductService, new ProductService()); - ioc.serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter, - ); - ioc.serviceManager.addSingleton( - IProductPathService, - TestFrameworkProductPathService, - ProductType.TestFramework, - ); - ioc.serviceManager.addSingleton( - IActivatedEnvironmentLaunch, - ActivatedEnvironmentLaunch, - ); - ioc.serviceManager.addSingleton( - IPythonPathUpdaterServiceManager, - PythonPathUpdaterService, - ); - ioc.serviceManager.addSingleton( - IPythonPathUpdaterServiceFactory, - PythonPathUpdaterServiceFactory, - ); - ioc.serviceManager.addSingleton(IActiveResourceService, ActiveResourceService); - ioc.serviceManager.addSingleton(IInterpreterPathService, InterpreterPathService); - ioc.serviceManager.addSingleton(IExtensions, Extensions); - ioc.serviceManager.addSingleton(IRandom, Random); - ioc.serviceManager.addSingleton(ITerminalServiceFactory, TerminalServiceFactory); - ioc.serviceManager.addSingleton(IClipboard, ClipboardService); - ioc.serviceManager.addSingleton(IDocumentManager, DocumentManager); - ioc.serviceManager.addSingleton(IDebugService, DebugService); - ioc.serviceManager.addSingleton(IApplicationEnvironment, ApplicationEnvironment); - ioc.serviceManager.addSingleton(IBrowserService, BrowserService); - ioc.serviceManager.addSingleton(ITerminalActivator, TerminalActivator); - ioc.serviceManager.addSingleton( - ITerminalActivationHandler, - PowershellTerminalActivationFailedHandler, - ); - ioc.serviceManager.addSingleton(IExperimentService, ExperimentService); - - ioc.serviceManager.addSingleton(ITerminalHelper, TerminalHelper); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - Bash, - TerminalActivationProviders.bashCShellFish, - ); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - CommandPromptAndPowerShell, - TerminalActivationProviders.commandPromptAndPowerShell, - ); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - Nushell, - TerminalActivationProviders.nushell, - ); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - PyEnvActivationCommandProvider, - TerminalActivationProviders.pyenv, - ); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - CondaActivationCommandProvider, - TerminalActivationProviders.conda, - ); - ioc.serviceManager.addSingleton( - ITerminalActivationCommandProvider, - PipEnvActivationCommandProvider, - TerminalActivationProviders.pipenv, - ); - ioc.serviceManager.addSingleton(IMultiStepInputFactory, MultiStepInputFactory); - ioc.serviceManager.addSingleton(IImportTracker, ImportTracker); - ioc.serviceManager.addBinding(IImportTracker, IExtensionSingleActivationService); - ioc.serviceManager.addSingleton(IShellDetector, TerminalNameShellDetector); - ioc.serviceManager.addSingleton(IShellDetector, SettingsShellDetector); - ioc.serviceManager.addSingleton(IShellDetector, UserEnvironmentShellDetector); - ioc.serviceManager.addSingleton(IShellDetector, VSCEnvironmentShellDetector); - ioc.serviceManager.addSingleton( - IExtensionSingleActivationService, - ReloadVSCodeCommandHandler, - ); - ioc.serviceManager.addSingleton( - IExtensionSingleActivationService, - ReportIssueCommandHandler, - ); - - ioc.serviceManager.addSingleton( - IExtensionSingleActivationService, - DebugSessionTelemetry, - ); - } - async function resetSettings() { - await updateSetting('linting.pylintEnabled', true, rootWorkspaceUri, ConfigurationTarget.Workspace); - } - - async function testCheckingIfProductIsInstalled(product: Product) { - const installer = ioc.serviceContainer.get(IInstaller); - const processService = (await ioc.serviceContainer - .get(IProcessServiceFactory) - .create()) as MockProcessService; - const checkInstalledDef = createDeferred(); - processService.onExec((_file, args, _options, callback) => { - const moduleName = installer.translateProductToModuleName(product); - if (args.length > 1 && args[0] === '-c' && args[1] === `import ${moduleName}`) { - checkInstalledDef.resolve(true); - } - callback({ stdout: '' }); - }); - await installer.isInstalled(product, resource); - await checkInstalledDef.promise; - } - - getProductsForInstallerTests().forEach((prod) => { - test(`Ensure isInstalled for Product: '${prod.name}' executes the right command`, async function () { - if ( - new ProductService().getProductType(prod.value) === ProductType.DataScience || - new ProductService().getProductType(prod.value) === ProductType.Python - ) { - return this.skip(); - } - ioc.serviceManager.addSingletonInstance( - IModuleInstaller, - new MockModuleInstaller('one', false), - ); - ioc.serviceManager.addSingletonInstance( - IModuleInstaller, - new MockModuleInstaller('two', true), - ); - ioc.serviceManager.addSingletonInstance(ITerminalHelper, instance(mock(TerminalHelper))); - if (prod.value === Product.unittest) { - return undefined; - } - await testCheckingIfProductIsInstalled(prod.value); - - return undefined; - }).timeout(TEST_TIMEOUT * 3); - }); - - async function testInstallingProduct(product: Product) { - const installer = ioc.serviceContainer.get(IInstaller); - const checkInstalledDef = createDeferred(); - const moduleInstallers = ioc.serviceContainer.getAll(IModuleInstaller); - const moduleInstallerOne = moduleInstallers.find((item) => item.displayName === 'two')!; - - moduleInstallerOne.on('installModule', (name: Product | string) => { - if (product === name) { - checkInstalledDef.resolve(); - } - }); - await installer.install(product); - await checkInstalledDef.promise; - } - - getProductsForInstallerTests().forEach((prod) => { - test(`Ensure install for Product: '${prod.name}' executes the right command in IModuleInstaller`, async function () { - const productType = new ProductService().getProductType(prod.value); - if (productType === ProductType.DataScience || productType === ProductType.Python) { - return this.skip(); - } - ioc.serviceManager.addSingletonInstance( - IModuleInstaller, - new MockModuleInstaller('one', false), - ); - ioc.serviceManager.addSingletonInstance( - IModuleInstaller, - new MockModuleInstaller('two', true), - ); - ioc.serviceManager.addSingletonInstance(ITerminalHelper, instance(mock(TerminalHelper))); - if (prod.value === Product.unittest) { - return undefined; - } - await testInstallingProduct(prod.value); - - return undefined; - }).timeout(TEST_TIMEOUT * 3); - }); -}); diff --git a/src/test/common/installer/installer.invalidPath.unit.test.ts b/src/test/common/installer/installer.invalidPath.unit.test.ts deleted file mode 100644 index b6738759f0d7..000000000000 --- a/src/test/common/installer/installer.invalidPath.unit.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect, use } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as path from 'path'; -import * as TypeMoq from 'typemoq'; -import { Uri } from 'vscode'; -import { IApplicationShell, IWorkspaceService } from '../../../client/common/application/types'; -import '../../../client/common/extensions'; -import { ProductInstaller } from '../../../client/common/installer/productInstaller'; -import { ProductService } from '../../../client/common/installer/productService'; -import { IProductPathService, IProductService } from '../../../client/common/installer/types'; -import { IPersistentState, IPersistentStateFactory, Product, ProductType } from '../../../client/common/types'; -import { IInterpreterService } from '../../../client/interpreter/contracts'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { PythonEnvironment } from '../../../client/pythonEnvironments/info'; -import { getProductsForInstallerTests } from '../productsToTest'; - -use(chaiAsPromised); - -suite('Module Installer - Invalid Paths', () => { - [undefined, Uri.file('resource')].forEach((resource) => { - ['moduleName', path.join('users', 'dev', 'tool', 'executable')].forEach((pathToExecutable) => { - const isExecutableAModule = path.basename(pathToExecutable) === pathToExecutable; - - getProductsForInstallerTests().forEach((product) => { - let installer: ProductInstaller; - let serviceContainer: TypeMoq.IMock; - let app: TypeMoq.IMock; - let workspaceService: TypeMoq.IMock; - let productPathService: TypeMoq.IMock; - let persistentState: TypeMoq.IMock; - - setup(function () { - if (new ProductService().getProductType(product.value) === ProductType.DataScience) { - return this.skip(); - } - serviceContainer = TypeMoq.Mock.ofType(); - - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) - .returns(() => new ProductService()); - app = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) - .returns(() => app.object); - workspaceService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) - .returns(() => workspaceService.object); - - productPathService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) - .returns(() => productPathService.object); - - const interpreterService = TypeMoq.Mock.ofType(); - - const pythonInterpreter = TypeMoq.Mock.ofType(); - - pythonInterpreter.setup((i) => (i as any).then).returns(() => undefined); - interpreterService - .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonInterpreter.object)); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) - .returns(() => interpreterService.object); - - persistentState = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) - .returns(() => persistentState.object); - - installer = new ProductInstaller(serviceContainer.object); - }); - - switch (product.value) { - case Product.unittest: { - return; - } - default: { - test(`Ensure invalid path message is ${isExecutableAModule ? 'not displayed' : 'displayed'} ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - // If the path to executable is a module, then we won't display error message indicating path is invalid. - - productPathService - .setup((p) => - p.getExecutableNameFromSettings(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource)), - ) - .returns(() => pathToExecutable) - .verifiable(TypeMoq.Times.atLeast(isExecutableAModule ? 0 : 1)); - productPathService - .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) - .returns(() => isExecutableAModule) - .verifiable(TypeMoq.Times.atLeastOnce()); - const anyParams = [0, 1, 2, 3, 4, 5].map(() => TypeMoq.It.isAny()); - app.setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), ...anyParams)) - .callback((message) => { - if (!isExecutableAModule) { - expect(message).contains(pathToExecutable); - } - }) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.exactly(1)); - const persistValue = TypeMoq.Mock.ofType>(); - persistValue.setup((pv) => pv.value).returns(() => false); - persistValue.setup((pv) => pv.updateValue(TypeMoq.It.isValue(true))); - persistentState - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistValue.object); - await installer.promptToInstall(product.value, resource); - productPathService.verifyAll(); - }); - } - } - }); - }); - }); -}); diff --git a/src/test/common/installer/installer.unit.test.ts b/src/test/common/installer/installer.unit.test.ts deleted file mode 100644 index 69a5f3678f69..000000000000 --- a/src/test/common/installer/installer.unit.test.ts +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/* eslint-disable max-classes-per-file */ - -import { assert, expect, use } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as sinon from 'sinon'; -import * as TypeMoq from 'typemoq'; -import { Disposable, Uri, WorkspaceFolder } from 'vscode'; -import { IApplicationShell, IWorkspaceService } from '../../../client/common/application/types'; -import '../../../client/common/extensions'; -import { ProductInstaller } from '../../../client/common/installer/productInstaller'; -import { ProductService } from '../../../client/common/installer/productService'; -import { - IInstallationChannelManager, - IModuleInstaller, - IProductPathService, - IProductService, -} from '../../../client/common/installer/types'; -import { - ExecutionResult, - IProcessService, - IProcessServiceFactory, - IPythonExecutionFactory, - IPythonExecutionService, -} from '../../../client/common/process/types'; -import { - IDisposableRegistry, - InstallerResponse, - IPersistentState, - IPersistentStateFactory, - Product, - ProductType, -} from '../../../client/common/types'; -import { createDeferred, Deferred } from '../../../client/common/utils/async'; -import { IInterpreterService } from '../../../client/interpreter/contracts'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { PythonEnvironment } from '../../../client/pythonEnvironments/info'; -import { sleep } from '../../common'; -import { getProductsForInstallerTests } from '../productsToTest'; - -use(chaiAsPromised); - -suite('Module Installer only', () => { - [undefined, Uri.file('resource')].forEach((resource) => { - getProductsForInstallerTests() - .concat([{ name: 'Unknown product', value: 404 }]) - - .forEach((product) => { - let disposables: Disposable[] = []; - let installer: ProductInstaller; - let installationChannel: TypeMoq.IMock; - let moduleInstaller: TypeMoq.IMock; - let serviceContainer: TypeMoq.IMock; - let app: TypeMoq.IMock; - let promptDeferred: Deferred | undefined; - let workspaceService: TypeMoq.IMock; - let persistentStore: TypeMoq.IMock; - - let productPathService: TypeMoq.IMock; - let interpreterService: TypeMoq.IMock; - const productService = new ProductService(); - - setup(function () { - if (new ProductService().getProductType(product.value) === ProductType.DataScience) { - return this.skip(); - } - promptDeferred = createDeferred(); - serviceContainer = TypeMoq.Mock.ofType(); - - disposables = []; - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) - .returns(() => disposables); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) - .returns(() => productService); - installationChannel = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny())) - .returns(() => installationChannel.object); - app = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) - .returns(() => app.object); - workspaceService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) - .returns(() => workspaceService.object); - persistentStore = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPersistentStateFactory), TypeMoq.It.isAny())) - .returns(() => persistentStore.object); - - moduleInstaller = TypeMoq.Mock.ofType(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - moduleInstaller.setup((x: any) => x.then).returns(() => undefined); - installationChannel - .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve(moduleInstaller.object)); - installationChannel - .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(moduleInstaller.object)); - - productPathService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IProductPathService), TypeMoq.It.isAny())) - .returns(() => productPathService.object); - productPathService - .setup((p) => p.getExecutableNameFromSettings(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) - .returns(() => 'xyz'); - productPathService - .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) - .returns(() => true); - interpreterService = TypeMoq.Mock.ofType(); - const pythonInterpreter = TypeMoq.Mock.ofType(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pythonInterpreter.setup((i) => (i as any).then).returns(() => undefined); - interpreterService - .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonInterpreter.object)); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) - .returns(() => interpreterService.object); - installer = new ProductInstaller(serviceContainer.object); - - return undefined; - }); - - teardown(() => { - if (new ProductService().getProductType(product.value) === ProductType.DataScience) { - sinon.restore(); - return; - } - // This must be resolved, else all subsequent tests will fail (as this same promise will be used for other tests). - if (promptDeferred) { - promptDeferred.resolve(); - } - disposables.forEach((disposable) => { - if (disposable) { - disposable.dispose(); - } - }); - sinon.restore(); - }); - - switch (product.value) { - case 404 as Product: { - test(`If product type is not recognized, throw error (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - app.setup((a) => - a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - ).verifiable(TypeMoq.Times.never()); - const getProductType = sinon.stub(ProductService.prototype, 'getProductType'); - - getProductType.returns('random' as ProductType); - const promise = installer.promptToInstall(product.value, resource); - await expect(promise).to.eventually.be.rejectedWith(`Unknown product ${product.value}`); - app.verifyAll(); - assert.ok(getProductType.calledOnce); - }); - return; - } - case Product.unittest: { - test(`Ensure resource info is passed into the module installer ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const response = await installer.install(product.value, resource); - expect(response).to.be.equal(InstallerResponse.Installed); - }); - test(`Ensure resource info is passed into the module installer (created using ProductInstaller) ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - const response = await installer.install(product.value, resource); - expect(response).to.be.equal(InstallerResponse.Installed); - }); - break; - } - - default: - test(`Ensure the prompt is displayed only once, until the prompt is closed, ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) - .returns(() => TypeMoq.Mock.ofType().object) - .verifiable(TypeMoq.Times.exactly(resource ? 5 : 0)); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - ), - ) - .returns(() => promptDeferred!.promise) - .verifiable(TypeMoq.Times.once()); - const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup((p) => p.value).returns(() => false); - persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); - persistentStore - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistVal.object); - - // Display first prompt. - installer.promptToInstall(product.value, resource).ignoreErrors(); - await sleep(1); - - // Display a few more prompts. - installer.promptToInstall(product.value, resource).ignoreErrors(); - await sleep(1); - installer.promptToInstall(product.value, resource).ignoreErrors(); - await sleep(1); - installer.promptToInstall(product.value, resource).ignoreErrors(); - await sleep(1); - installer.promptToInstall(product.value, resource).ignoreErrors(); - await sleep(1); - - app.verifyAll(); - workspaceService.verifyAll(); - }); - test(`Ensure the prompt is displayed again when previous prompt has been closed, ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) - .returns(() => TypeMoq.Mock.ofType().object) - .verifiable(TypeMoq.Times.exactly(resource ? 3 : 0)); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - ), - ) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.exactly(3)); - const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup((p) => p.value).returns(() => false); - persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); - persistentStore - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistVal.object); - - await installer.promptToInstall(product.value, resource); - await installer.promptToInstall(product.value, resource); - await installer.promptToInstall(product.value, resource); - - app.verifyAll(); - workspaceService.verifyAll(); - }); - - if (product.value === Product.pylint) { - test(`Ensure the install prompt is not displayed when the user requests it not be shown again, ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) - .returns(() => TypeMoq.Mock.ofType().object) - .verifiable(TypeMoq.Times.exactly(resource ? 2 : 0)); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue('Install'), - TypeMoq.It.isValue('Select Linter'), - TypeMoq.It.isValue("Don't show again"), - ), - ) - .returns(async () => "Don't show again") - .verifiable(TypeMoq.Times.once()); - const persistVal = TypeMoq.Mock.ofType>(); - let mockPersistVal = false; - persistVal.setup((p) => p.value).returns(() => mockPersistVal); - persistVal - .setup((p) => p.updateValue(TypeMoq.It.isValue(true))) - .returns(() => { - mockPersistVal = true; - return Promise.resolve(); - }) - .verifiable(TypeMoq.Times.once()); - persistentStore - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistVal.object) - .verifiable(TypeMoq.Times.exactly(3)); - - // Display first prompt. - const initialResponse = await installer.promptToInstall(product.value, resource); - - // Display a second prompt. - const secondResponse = await installer.promptToInstall(product.value, resource); - - expect(initialResponse).to.be.equal(InstallerResponse.Ignore); - expect(secondResponse).to.be.equal(InstallerResponse.Ignore); - - app.verifyAll(); - workspaceService.verifyAll(); - persistentStore.verifyAll(); - persistVal.verifyAll(); - }); - } else if (productService.getProductType(product.value) === ProductType.Linter) { - test(`Ensure the 'do not show again' prompt isn't shown for non-pylint linters, ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) - .returns(() => TypeMoq.Mock.ofType().object); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue('Install'), - TypeMoq.It.isValue('Select Linter'), - ), - ) - .returns(async () => undefined) - .verifiable(TypeMoq.Times.once()); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue('Install'), - TypeMoq.It.isValue('Select Linter'), - TypeMoq.It.isValue("Don't show again"), - ), - ) - .returns(async () => undefined) - .verifiable(TypeMoq.Times.never()); - const persistVal = TypeMoq.Mock.ofType>(); - let mockPersistVal = false; - persistVal.setup((p) => p.value).returns(() => mockPersistVal); - persistVal - .setup((p) => p.updateValue(TypeMoq.It.isValue(true))) - .returns(() => { - mockPersistVal = true; - return Promise.resolve(); - }); - persistentStore - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistVal.object); - - // Display the prompt. - await installer.promptToInstall(product.value, resource); - - // we're just ensuring the 'disable pylint' prompt never appears... - app.verifyAll(); - }); - } - - test(`Ensure resource info is passed into the module installer ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - moduleInstaller - .setup((m) => - m.installModule( - TypeMoq.It.isValue(product.value), - TypeMoq.It.isValue(resource), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => Promise.reject(new Error('UnitTesting'))); - - try { - await installer.install(product.value, resource); - } catch (ex) { - moduleInstaller.verify( - (m) => - m.installModule( - TypeMoq.It.isValue(product.value), - TypeMoq.It.isValue(resource), - TypeMoq.It.isValue(undefined), - ), - TypeMoq.Times.once(), - ); - } - }); - - test(`Return InstallerResponse.Ignore for the module installer ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - }) if installation channel is not defined`, async () => { - moduleInstaller - .setup((m) => - m.installModule( - TypeMoq.It.isValue(product.value), - TypeMoq.It.isValue(resource), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => Promise.reject(new Error('UnitTesting'))); - installationChannel.reset(); - installationChannel - .setup((i) => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)); - try { - const response = await installer.install(product.value, resource); - expect(response).to.equal(InstallerResponse.Ignore); - } catch (ex) { - assert(false, `Should not throw errors, ${ex}`); - } - }); - test(`Ensure resource info is passed into the module installer (created using ProductInstaller) ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - moduleInstaller - .setup((m) => - m.installModule( - TypeMoq.It.isValue(product.value), - TypeMoq.It.isValue(resource), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => Promise.reject(new Error('UnitTesting'))); - - try { - await installer.install(product.value, resource); - } catch (ex) { - moduleInstaller.verify( - (m) => - m.installModule( - TypeMoq.It.isValue(product.value), - TypeMoq.It.isValue(resource), - TypeMoq.It.isValue(undefined), - ), - TypeMoq.Times.once(), - ); - } - }); - } - // Test isInstalled() - if (product.value === Product.unittest) { - test(`Method isInstalled() returns true for module installer ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const result = await installer.isInstalled(product.value, resource); - expect(result).to.equal(true, 'Should be true'); - }); - } else { - test(`Method isInstalled() returns true if module is installed for the module installer ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - const pythonExecutionFactory = TypeMoq.Mock.ofType(); - const pythonExecutionService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) - .returns(() => pythonExecutionFactory.object); - pythonExecutionFactory - .setup((p) => p.createActivatedEnvironment(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonExecutionService.object)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pythonExecutionService.setup((p) => (p as any).then).returns(() => undefined); - pythonExecutionService - .setup((p) => p.isModuleInstalled(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(true)) - .verifiable(TypeMoq.Times.once()); - - const response = await installer.isInstalled(product.value, resource); - expect(response).to.equal(true, 'Should be true'); - pythonExecutionService.verifyAll(); - }); - test(`Method isInstalled() returns false if module is not installed for the module installer ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - const pythonExecutionFactory = TypeMoq.Mock.ofType(); - const pythonExecutionService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory))) - .returns(() => pythonExecutionFactory.object); - pythonExecutionFactory - .setup((p) => p.createActivatedEnvironment(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(pythonExecutionService.object)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pythonExecutionService.setup((p) => (p as any).then).returns(() => undefined); - pythonExecutionService - .setup((p) => p.isModuleInstalled(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(false)) - .verifiable(TypeMoq.Times.once()); - - const response = await installer.isInstalled(product.value, resource); - expect(response).to.equal(false, 'Should be false'); - - pythonExecutionService.verifyAll(); - }); - test(`Method isInstalled() returns true if running 'path/to/module_executable --version' succeeds for the module installer ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - const processServiceFactory = TypeMoq.Mock.ofType(); - const processService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(IProcessServiceFactory)) - .returns(() => processServiceFactory.object); - processServiceFactory - .setup((p) => p.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(processService.object)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - processService.setup((p) => (p as any).then).returns(() => undefined); - const executionResult: ExecutionResult = { - stdout: 'output', - }; - processService - .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve(executionResult)) - .verifiable(TypeMoq.Times.once()); - - productPathService.reset(); - productPathService - .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) - .returns(() => false); - - const response = await installer.isInstalled(product.value, resource); - expect(response).to.equal(true, 'Should be true'); - - processService.verifyAll(); - }); - test(`Method isInstalled() returns false if running 'path/to/module_executable --version' fails for the module installer ${ - product.name - } (${resource ? 'With a resource' : 'without a resource'})`, async () => { - const processServiceFactory = TypeMoq.Mock.ofType(); - const processService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(IProcessServiceFactory)) - .returns(() => processServiceFactory.object); - processServiceFactory - .setup((p) => p.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(processService.object)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - processService.setup((p) => (p as any).then).returns(() => undefined); - processService - .setup((p) => p.exec(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.reject(new Error('Kaboom'))) - .verifiable(TypeMoq.Times.once()); - - productPathService.reset(); - productPathService - .setup((p) => p.isExecutableAModule(TypeMoq.It.isAny(), TypeMoq.It.isValue(resource))) - .returns(() => false); - - const response = await installer.isInstalled(product.value, resource); - expect(response).to.equal(false, 'Should be false'); - - processService.verifyAll(); - }); - } - - // Test promptToInstall() when no interpreter is selected - test(`If no interpreter is selected, promptToInstall() doesn't prompt for product ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isValue(resource!))) - .returns(() => TypeMoq.Mock.ofType().object) - .verifiable(TypeMoq.Times.never()); - app.setup((a) => - a.showErrorMessage( - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - TypeMoq.It.isAny(), - ), - ) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.never()); - const persistVal = TypeMoq.Mock.ofType>(); - persistVal.setup((p) => p.value).returns(() => false); - persistVal.setup((p) => p.updateValue(TypeMoq.It.isValue(true))); - persistentStore - .setup((ps) => - ps.createGlobalPersistentState( - TypeMoq.It.isAnyString(), - TypeMoq.It.isValue(undefined), - ), - ) - .returns(() => persistVal.object); - - interpreterService.reset(); - interpreterService - .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)) - .verifiable(TypeMoq.Times.once()); - await installer.promptToInstall(product.value, resource); - - app.verifyAll(); - interpreterService.verifyAll(); - workspaceService.verifyAll(); - }); - }); - }); -}); diff --git a/src/test/common/installer/moduleInstaller.unit.test.ts b/src/test/common/installer/moduleInstaller.unit.test.ts index 01ac0e315555..3df64ceb2dec 100644 --- a/src/test/common/installer/moduleInstaller.unit.test.ts +++ b/src/test/common/installer/moduleInstaller.unit.test.ts @@ -322,110 +322,6 @@ suite('Module Installer', () => { terminalService.verifyAll(); } - if (product.value === Product.pylint) { - generatePythonInterpreterVersions().forEach((interpreterInfo) => { - const majorVersion = interpreterInfo.version - ? interpreterInfo.version.major - : 0; - if (majorVersion === 2) { - const testTitle = `Ensure install arg is \'pylint<2.0.0\' in ${ - interpreterInfo.version ? interpreterInfo.version.raw : '' - }`; - if (InstallerClass === PipInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const proxyArgs = - proxyServer.length === 0 ? [] : ['--proxy', proxyServer]; - const expectedArgs = [ - '-m', - 'pip', - ...proxyArgs, - 'install', - '-U', - '"pylint<2.0.0"', - ]; - await installModuleAndVerifyCommand(pythonPath, expectedArgs); - }); - } - if (InstallerClass === PipEnvInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const expectedArgs = ['install', '"pylint<2.0.0"', '--dev']; - await installModuleAndVerifyCommand(pipenvName, expectedArgs); - }); - } - if (InstallerClass === CondaInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const expectedArgs = ['install']; - if (condaEnvInfo && condaEnvInfo.name) { - expectedArgs.push('--name'); - expectedArgs.push( - condaEnvInfo.name.toCommandArgumentForPythonExt(), - ); - } else if (condaEnvInfo && condaEnvInfo.path) { - expectedArgs.push('--prefix'); - expectedArgs.push( - condaEnvInfo.path.fileToCommandArgumentForPythonExt(), - ); - } - expectedArgs.push('"pylint<2.0.0"'); - expectedArgs.push('-y'); - await installModuleAndVerifyCommand(condaExecutable, expectedArgs); - }); - } - } else { - const testTitle = `Ensure install arg is \'pylint\' in ${ - interpreterInfo.version ? interpreterInfo.version.raw : '' - }`; - if (InstallerClass === PipInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const proxyArgs = - proxyServer.length === 0 ? [] : ['--proxy', proxyServer]; - const expectedArgs = [ - '-m', - 'pip', - ...proxyArgs, - 'install', - '-U', - 'pylint', - ]; - await installModuleAndVerifyCommand(pythonPath, expectedArgs); - }); - } - if (InstallerClass === PipEnvInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const expectedArgs = ['install', 'pylint', '--dev']; - await installModuleAndVerifyCommand(pipenvName, expectedArgs); - }); - } - if (InstallerClass === CondaInstaller) { - test(testTitle, async () => { - setActiveInterpreter(interpreterInfo); - const expectedArgs = ['install']; - if (condaEnvInfo && condaEnvInfo.name) { - expectedArgs.push('--name'); - expectedArgs.push( - condaEnvInfo.name.toCommandArgumentForPythonExt(), - ); - } else if (condaEnvInfo && condaEnvInfo.path) { - expectedArgs.push('--prefix'); - expectedArgs.push( - condaEnvInfo.path.fileToCommandArgumentForPythonExt(), - ); - } - expectedArgs.push('pylint'); - expectedArgs.push('-y'); - await installModuleAndVerifyCommand(condaExecutable, expectedArgs); - }); - } - } - }); - return; - } - if (InstallerClass === TestModuleInstaller) { suite(`If interpreter type is Unknown (${product.name})`, async () => { test(`If 'python.globalModuleInstallation' is set to true and pythonPath directory is read only, do an elevated install`, async () => { @@ -692,21 +588,6 @@ suite('Module Installer', () => { }); }); -function generatePythonInterpreterVersions() { - const versions: SemVer[] = ['2.7.0-final', '3.4.0-final', '3.5.0-final', '3.6.0-final', '3.7.0-final'].map( - (ver) => new SemVer(ver), - ); - return versions.map((version) => { - const info = TypeMoq.Mock.ofType(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - info.setup((t: any) => t.then).returns(() => undefined); - info.setup((t) => t.envType).returns(() => EnvironmentType.VirtualEnv); - info.setup((t) => t.version).returns(() => version); - info.setup((t) => t.path).returns(() => pythonPath); - return info.object; - }); -} - function getModuleNamesForTesting(): { name: string; value: Product; moduleName: string }[] { return getNamesAndValues(Product) .map((product) => { diff --git a/src/test/common/installer/productPath.unit.test.ts b/src/test/common/installer/productPath.unit.test.ts deleted file mode 100644 index 8e65f3a5caed..000000000000 --- a/src/test/common/installer/productPath.unit.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { fail } from 'assert'; -import { expect, use } from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as TypeMoq from 'typemoq'; -import { Uri } from 'vscode'; -import '../../../client/common/extensions'; -import { ProductInstaller } from '../../../client/common/installer/productInstaller'; -import { - BaseProductPathsService, - LinterProductPathService, - TestFrameworkProductPathService, -} from '../../../client/common/installer/productPath'; -import { ProductService } from '../../../client/common/installer/productService'; -import { IProductService } from '../../../client/common/installer/types'; -import { IConfigurationService, IInstaller, IPythonSettings, Product, ProductType } from '../../../client/common/types'; -import { IServiceContainer } from '../../../client/ioc/types'; -import { ILinterInfo, ILinterManager } from '../../../client/linters/types'; -import { ITestsHelper } from '../../../client/testing/common/types'; -import { ITestingSettings } from '../../../client/testing/configuration/types'; -import { getProductsForInstallerTests } from '../productsToTest'; - -use(chaiAsPromised); - -suite('Product Path', () => { - [undefined, Uri.file('resource')].forEach((resource) => { - getProductsForInstallerTests().forEach((product) => { - class TestBaseProductPathsService extends BaseProductPathsService { - public getExecutableNameFromSettings(_: Product, _resource?: Uri): string { - return ''; - } - } - let serviceContainer: TypeMoq.IMock; - let unitTestSettings: TypeMoq.IMock; - let configService: TypeMoq.IMock; - let productInstaller: ProductInstaller; - setup(function () { - if (new ProductService().getProductType(product.value) === ProductType.DataScience) { - return this.skip(); - } - serviceContainer = TypeMoq.Mock.ofType(); - configService = TypeMoq.Mock.ofType(); - unitTestSettings = TypeMoq.Mock.ofType(); - - productInstaller = new ProductInstaller(serviceContainer.object); - const pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup((p) => p.testing).returns(() => unitTestSettings.object); - configService - .setup((s) => s.getSettings(TypeMoq.It.isValue(resource))) - .returns(() => pythonSettings.object); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) - .returns(() => configService.object); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) - .returns(() => productInstaller); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IProductService), TypeMoq.It.isAny())) - .returns(() => new ProductService()); - }); - - suite('Method isExecutableAModule()', () => { - test('Returns true if User has customized the executable name', () => { - productInstaller.translateProductToModuleName = () => 'moduleName'; - const productPathService = new TestBaseProductPathsService(serviceContainer.object); - productPathService.getExecutableNameFromSettings = () => 'executableName'; - expect(productPathService.isExecutableAModule(product.value)).to.equal(true, 'Should be true'); - }); - test('Returns false if User has customized the full path to executable', () => { - productInstaller.translateProductToModuleName = () => 'moduleName'; - const productPathService = new TestBaseProductPathsService(serviceContainer.object); - productPathService.getExecutableNameFromSettings = () => 'path/to/executable'; - expect(productPathService.isExecutableAModule(product.value)).to.equal(false, 'Should be false'); - }); - test('Returns false if translating product to module name fails with error', () => { - productInstaller.translateProductToModuleName = () => { - return new Error('Kaboom') as any; - }; - const productPathService = new TestBaseProductPathsService(serviceContainer.object); - productPathService.getExecutableNameFromSettings = () => 'executableName'; - expect(productPathService.isExecutableAModule(product.value)).to.equal(false, 'Should be false'); - }); - }); - const productType = new ProductService().getProductType(product.value); - switch (productType) { - case ProductType.Linter: { - test(`Ensure path is returned for ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const productPathService = new LinterProductPathService(serviceContainer.object); - const linterManager = TypeMoq.Mock.ofType(); - const linterInfo = TypeMoq.Mock.ofType(); - const expectedPath = 'Some Path'; - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) - .returns(() => linterManager.object); - linterInfo - .setup((l) => l.pathName(TypeMoq.It.isValue(resource))) - .returns(() => expectedPath) - .verifiable(TypeMoq.Times.once()); - linterManager - .setup((l) => l.getLinterInfo(TypeMoq.It.isValue(product.value))) - .returns(() => linterInfo.object) - .verifiable(TypeMoq.Times.once()); - - const value = productPathService.getExecutableNameFromSettings(product.value, resource); - expect(value).to.be.equal(expectedPath); - linterInfo.verifyAll(); - linterManager.verifyAll(); - }); - break; - } - case ProductType.TestFramework: { - test(`Ensure path is returned for ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const productPathService = new TestFrameworkProductPathService(serviceContainer.object); - const testHelper = TypeMoq.Mock.ofType(); - const expectedPath = 'Some Path'; - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) - .returns(() => testHelper.object); - testHelper - .setup((t) => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) - .returns(() => { - return { - argsName: 'autoTestDiscoverOnSaveEnabled', - enabledName: 'autoTestDiscoverOnSaveEnabled', - pathName: 'pytestPath', - }; - }) - .verifiable(TypeMoq.Times.once()); - unitTestSettings - .setup((u) => u.pytestPath) - .returns(() => expectedPath) - .verifiable(TypeMoq.Times.atLeastOnce()); - - const value = productPathService.getExecutableNameFromSettings(product.value, resource); - expect(value).to.be.equal(expectedPath); - testHelper.verifyAll(); - unitTestSettings.verifyAll(); - }); - test(`Ensure module name is returned for ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const productPathService = new TestFrameworkProductPathService(serviceContainer.object); - const testHelper = TypeMoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(ITestsHelper), TypeMoq.It.isAny())) - .returns(() => testHelper.object); - testHelper - .setup((t) => t.getSettingsPropertyNames(TypeMoq.It.isValue(product.value))) - .returns(() => { - return { - argsName: 'autoTestDiscoverOnSaveEnabled', - enabledName: 'autoTestDiscoverOnSaveEnabled', - pathName: undefined, - }; - }) - .verifiable(TypeMoq.Times.once()); - - const value = productPathService.getExecutableNameFromSettings(product.value, resource); - const moduleName = productInstaller.translateProductToModuleName(product.value); - expect(value).to.be.equal(moduleName); - testHelper.verifyAll(); - }); - break; - } - default: { - test(`No tests for Product Path of this Product Type ${product.name}`, () => { - fail('No tests for Product Path of this Product Type'); - }); - } - } - }); - }); -}); diff --git a/src/test/common/installer/serviceRegistry.unit.test.ts b/src/test/common/installer/serviceRegistry.unit.test.ts index 5b971790fa9a..8a811ad7ac4d 100644 --- a/src/test/common/installer/serviceRegistry.unit.test.ts +++ b/src/test/common/installer/serviceRegistry.unit.test.ts @@ -9,10 +9,7 @@ import { CondaInstaller } from '../../../client/common/installer/condaInstaller' import { PipEnvInstaller } from '../../../client/common/installer/pipEnvInstaller'; import { PipInstaller } from '../../../client/common/installer/pipInstaller'; import { PoetryInstaller } from '../../../client/common/installer/poetryInstaller'; -import { - LinterProductPathService, - TestFrameworkProductPathService, -} from '../../../client/common/installer/productPath'; +import { TestFrameworkProductPathService } from '../../../client/common/installer/productPath'; import { ProductService } from '../../../client/common/installer/productService'; import { registerTypes } from '../../../client/common/installer/serviceRegistry'; import { @@ -45,13 +42,6 @@ suite('Common installer Service Registry', () => { ), ).once(); verify(serviceManager.addSingleton(IProductService, ProductService)).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter, - ), - ).once(); verify( serviceManager.addSingleton( IProductPathService, diff --git a/src/test/common/moduleInstaller.test.ts b/src/test/common/moduleInstaller.test.ts index d91c32fc7350..6d1d153aba94 100644 --- a/src/test/common/moduleInstaller.test.ts +++ b/src/test/common/moduleInstaller.test.ts @@ -3,7 +3,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import { SemVer } from 'semver'; import { instance, mock, when } from 'ts-mockito'; import * as TypeMoq from 'typemoq'; -import { ConfigurationTarget, Uri } from 'vscode'; +import { Uri } from 'vscode'; import { IExtensionSingleActivationService } from '../../client/activation/types'; import { ActiveResourceService } from '../../client/common/application/activeResource'; import { ApplicationEnvironment } from '../../client/common/application/applicationEnvironment'; @@ -96,7 +96,7 @@ import { JupyterExtensionDependencyManager } from '../../client/jupyter/jupyterE import { EnvironmentType, PythonEnvironment } from '../../client/pythonEnvironments/info'; import { ImportTracker } from '../../client/telemetry/importTracker'; import { IImportTracker } from '../../client/telemetry/types'; -import { PYTHON_PATH, rootWorkspaceUri } from '../common'; +import { PYTHON_PATH } from '../common'; import { MockModuleInstaller } from '../mocks/moduleInstaller'; import { MockProcessService } from '../mocks/proc'; import { UnitTestIocContainer } from '../testing/serviceRegistry'; @@ -130,7 +130,6 @@ suite('Module Installer', () => { chaiShould(); await initializeDI(); await initializeTest(); - await resetSettings(); }); suiteTeardown(async () => { await closeActiveWindows(); @@ -144,7 +143,6 @@ suite('Module Installer', () => { ioc = new UnitTestIocContainer(); ioc.registerUnitTestTypes(); ioc.registerVariableTypes(); - ioc.registerLinterTypes(); ioc.registerInterpreterStorageTypes(); ioc.serviceManager.addSingleton(IPersistentStateFactory, PersistentStateFactory); @@ -263,15 +261,6 @@ suite('Module Installer', () => { DebugSessionTelemetry, ); } - async function resetSettings(): Promise { - const configService = ioc.serviceManager.get(IConfigurationService); - await configService.updateSetting( - 'linting.pylintEnabled', - true, - rootWorkspaceUri, - ConfigurationTarget.Workspace, - ); - } test('Ensure pip is supported and conda is not', async () => { ioc.serviceManager.addSingletonInstance( IModuleInstaller, diff --git a/src/test/common/productsToTest.ts b/src/test/common/productsToTest.ts deleted file mode 100644 index e82d12bbd9eb..000000000000 --- a/src/test/common/productsToTest.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { Product } from '../../client/common/types'; -import { getNamesAndValues } from '../../client/common/utils/enum'; - -export function getProductsForInstallerTests(): { name: string; value: Product }[] { - return getNamesAndValues(Product).filter( - (p) => - !['pylint', 'flake8', 'pycodestyle', 'pylama', 'prospector', 'pydocstyle', 'mypy', 'bandit'].includes( - p.name, - ), - ); -} diff --git a/src/test/install/channelManager.channels.test.ts b/src/test/install/channelManager.channels.test.ts index 5e102a0a5182..0d8190f046a3 100644 --- a/src/test/install/channelManager.channels.test.ts +++ b/src/test/install/channelManager.channels.test.ts @@ -89,7 +89,7 @@ suite('Installation - installation channels', () => { installer2.setup((x) => x.displayName).returns(() => 'Name 2'); const cm = new InstallationChannelManager(serviceContainer); - await cm.getInstallationChannel(Product.pylint); + await cm.getInstallationChannel(Product.pytest); assert.notStrictEqual(items, undefined, 'showQuickPick not called'); assert.strictEqual(items!.length, 2, 'Incorrect number of installer shown'); diff --git a/src/test/install/channelManager.messages.test.ts b/src/test/install/channelManager.messages.test.ts index c21612e8f56c..326ba1ad4bfd 100644 --- a/src/test/install/channelManager.messages.test.ts +++ b/src/test/install/channelManager.messages.test.ts @@ -185,7 +185,7 @@ suite('Installation - channel messages', () => { if (methodType === 'showNoInstallersMessage') { await channels.showNoInstallersMessage(); } else { - await channels.getInstallationChannel(Product.pylint); + await channels.getInstallationChannel(Product.pytest); } await verify(message, url); } diff --git a/src/test/linters/bandit.unit.test.ts b/src/test/linters/bandit.unit.test.ts deleted file mode 100644 index 6a44158034bd..000000000000 --- a/src/test/linters/bandit.unit.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { parseLine } from '../../client/linters/baseLinter'; -import { BANDIT_REGEX } from '../../client/linters/bandit'; - -import { ILintMessage, LinterId } from '../../client/linters/types'; - -suite('Linting - Bandit', () => { - test('parsing new bandit with col', () => { - const newOutput = `\ -1,0,LOW,B404:Consider possible security implications associated with subprocess module. -19,4,HIGH,B602:subprocess call with shell=True identified, security issue. -`; - - const lines = newOutput.split('\n'); - const tests: [string, ILintMessage | undefined][] = [ - [ - lines[0], - { - code: 'B404', - message: 'Consider possible security implications associated with subprocess module.', - column: 0, - line: 1, - type: 'LOW', - provider: 'bandit', - }, - ], - [ - lines[1], - { - code: 'B602', - message: 'subprocess call with shell=True identified, security issue.', - column: 3, - line: 19, - type: 'HIGH', - provider: 'bandit', - }, - ], - ]; - for (const [line, expected] of tests) { - const msg = parseLine(line, BANDIT_REGEX, LinterId.Bandit, 1); - - expect(msg).to.deep.equal(expected); - } - }); - test('parsing old bandit with no col', () => { - const newOutput = `\ -1,col,LOW,B404:Consider possible security implications associated with subprocess module. -19,col,HIGH,B602:subprocess call with shell=True identified, security issue. -`; - - const lines = newOutput.split('\n'); - const tests: [string, ILintMessage | undefined][] = [ - [ - lines[0], - { - code: 'B404', - message: 'Consider possible security implications associated with subprocess module.', - column: 0, - line: 1, - type: 'LOW', - provider: 'bandit', - }, - ], - [ - lines[1], - { - code: 'B602', - message: 'subprocess call with shell=True identified, security issue.', - column: 0, - line: 19, - type: 'HIGH', - provider: 'bandit', - }, - ], - ]; - for (const [line, expected] of tests) { - const msg = parseLine(line, BANDIT_REGEX, LinterId.Bandit, 1); - - expect(msg).to.deep.equal(expected); - } - }); -}); diff --git a/src/test/linters/common.ts b/src/test/linters/common.ts deleted file mode 100644 index 3c8f72a8d710..000000000000 --- a/src/test/linters/common.ts +++ /dev/null @@ -1,405 +0,0 @@ -/* eslint-disable max-classes-per-file */ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as os from 'os'; -import * as TypeMoq from 'typemoq'; -import { DiagnosticSeverity, TextDocument, Uri, WorkspaceFolder } from 'vscode'; -import { LanguageServerType } from '../../client/activation/types'; -import { IApplicationShell, IWorkspaceService } from '../../client/common/application/types'; -import { Product } from '../../client/common/installer/productInstaller'; -import { ProductNames } from '../../client/common/installer/productNames'; -import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { IPythonExecutionFactory, IPythonToolExecutionService } from '../../client/common/process/types'; -import { - Flake8CategorySeverity, - IConfigurationService, - IInstaller, - IMypyCategorySeverity, - ILogOutputChannel, - IPycodestyleCategorySeverity, - IPylintCategorySeverity, - IPythonSettings, -} from '../../client/common/types'; -import { IServiceContainer } from '../../client/ioc/types'; -import { LINTERID_BY_PRODUCT } from '../../client/linters/constants'; -import { LinterManager } from '../../client/linters/linterManager'; -import { ILinter, ILinterManager, ILintMessage, LinterId } from '../../client/linters/types'; - -export function newMockDocument(filename: string): TypeMoq.IMock { - const uri = Uri.file(filename); - const doc = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - doc.setup((s) => s.uri).returns(() => uri); - return doc; -} - -export function linterMessageAsLine(msg: ILintMessage): string { - switch (msg.provider) { - case 'pydocstyle': { - return `:${msg.line} spam:${os.EOL}\t${msg.code}: ${msg.message}`; - } - default: { - return `${msg.line},${msg.column},${msg.type},${msg.code}:${msg.message}`; - } - } -} - -function pylintMessageAsString(msg: ILintMessage, trailingComma = true): string { - return ` { - "type": "${msg.type}", - "line": ${msg.line}, - "column": ${msg.column}, - "symbol": "${msg.code}", - "message": "${msg.message}", - "endLine": ${msg.endLine ?? null}, - "endColumn": ${msg.endColumn ?? null} - }${trailingComma ? ',' : ''}`; -} - -export function pylintLinterMessagesAsOutput(messages: ILintMessage[]): string { - const lines: string[] = ['[']; - if (messages) { - const pylintMessages = messages.slice(0, -1).map((msg) => pylintMessageAsString(msg, true)); - const lastMessage = pylintMessageAsString(messages[messages.length - 1], false); - - lines.push(...pylintMessages, lastMessage); - } - lines.push(']'); - return lines.join(os.EOL); -} - -export function getLinterID(product: Product): LinterId { - const linterID = LINTERID_BY_PRODUCT.get(product); - if (!linterID) { - throwUnknownProduct(product); - } - return linterID!; -} - -export function getProductName(product: Product, capitalize = true): string { - let prodName = ProductNames.get(product); - if (!prodName) { - prodName = Product[product]; - } - if (capitalize) { - return prodName.charAt(0).toUpperCase() + prodName.slice(1); - } - return prodName; -} - -export function throwUnknownProduct(product: Product): void { - throw Error(`unsupported product ${Product[product]} (${product})`); -} - -export class LintingSettings { - public enabled: boolean; - - public cwd?: string; - - public ignorePatterns: string[]; - - public prospectorEnabled: boolean; - - public prospectorArgs: string[]; - - public pylintEnabled: boolean; - - public pylintArgs: string[]; - - public pycodestyleEnabled: boolean; - - public pycodestyleArgs: string[]; - - public pylamaEnabled: boolean; - - public pylamaArgs: string[]; - - public flake8Enabled: boolean; - - public flake8Args: string[]; - - public pydocstyleEnabled: boolean; - - public pydocstyleArgs: string[]; - - public lintOnSave: boolean; - - public maxNumberOfProblems: number; - - public pylintCategorySeverity: IPylintCategorySeverity; - - public pycodestyleCategorySeverity: IPycodestyleCategorySeverity; - - public flake8CategorySeverity: Flake8CategorySeverity; - - public mypyCategorySeverity: IMypyCategorySeverity; - - public prospectorPath: string; - - public pylintPath: string; - - public pycodestylePath: string; - - public pylamaPath: string; - - public flake8Path: string; - - public pydocstylePath: string; - - public mypyEnabled: boolean; - - public mypyArgs: string[]; - - public mypyPath: string; - - public banditEnabled: boolean; - - public banditArgs: string[]; - - public banditPath: string; - - constructor() { - // mostly from configSettings.ts - - this.enabled = true; - this.cwd = undefined; - this.ignorePatterns = []; - this.lintOnSave = false; - this.maxNumberOfProblems = 100; - - this.flake8Enabled = false; - this.flake8Path = 'flake8'; - this.flake8Args = []; - this.flake8CategorySeverity = { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning, - F: DiagnosticSeverity.Warning, - }; - - this.mypyEnabled = false; - this.mypyPath = 'mypy'; - this.mypyArgs = []; - this.mypyCategorySeverity = { - error: DiagnosticSeverity.Error, - note: DiagnosticSeverity.Hint, - }; - - this.banditEnabled = false; - this.banditPath = 'bandit'; - this.banditArgs = []; - - this.pycodestyleEnabled = false; - this.pycodestylePath = 'pycodestyle'; - this.pycodestyleArgs = []; - this.pycodestyleCategorySeverity = { - E: DiagnosticSeverity.Error, - W: DiagnosticSeverity.Warning, - }; - - this.pylamaEnabled = false; - this.pylamaPath = 'pylama'; - this.pylamaArgs = []; - - this.prospectorEnabled = false; - this.prospectorPath = 'prospector'; - this.prospectorArgs = []; - - this.pydocstyleEnabled = false; - this.pydocstylePath = 'pydocstyle'; - this.pydocstyleArgs = []; - - this.pylintEnabled = false; - this.pylintPath = 'pylint'; - this.pylintArgs = []; - this.pylintCategorySeverity = { - convention: DiagnosticSeverity.Hint, - error: DiagnosticSeverity.Error, - fatal: DiagnosticSeverity.Error, - refactor: DiagnosticSeverity.Hint, - warning: DiagnosticSeverity.Warning, - }; - } -} - -export class BaseTestFixture { - public serviceContainer: TypeMoq.IMock; - - public linterManager: LinterManager; - - // services - public workspaceService: TypeMoq.IMock; - - public installer: TypeMoq.IMock; - - public appShell: TypeMoq.IMock; - - // config - public configService: TypeMoq.IMock; - - public pythonSettings: TypeMoq.IMock; - - public lintingSettings: LintingSettings; - - // data - public outputChannel: TypeMoq.IMock; - - // artifacts - public output: string; - - public logged: string[]; - - constructor( - platformService: IPlatformService, - filesystem: IFileSystem, - pythonToolExecService: IPythonToolExecutionService, - pythonExecFactory: IPythonExecutionFactory, - configService?: TypeMoq.IMock, - serviceContainer?: TypeMoq.IMock, - ignoreConfigUpdates = false, - public readonly workspaceDir = '.', - protected readonly printLogs = false, - ) { - this.serviceContainer = - serviceContainer || TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - - // services - - this.workspaceService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - this.installer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - this.appShell = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) - .returns(() => filesystem); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) - .returns(() => this.workspaceService.object); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IInstaller), TypeMoq.It.isAny())) - .returns(() => this.installer.object); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService), TypeMoq.It.isAny())) - .returns(() => platformService); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPythonToolExecutionService), TypeMoq.It.isAny())) - .returns(() => pythonToolExecService); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPythonExecutionFactory), TypeMoq.It.isAny())) - .returns(() => pythonExecFactory); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny())) - .returns(() => this.appShell.object); - this.initServices(); - - // config - - this.configService = - configService || TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - this.pythonSettings = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - this.lintingSettings = new LintingSettings(); - - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) - .returns(() => this.configService.object); - this.configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => this.pythonSettings.object); - this.pythonSettings.setup((s) => s.linting).returns(() => this.lintingSettings); - this.initConfig(ignoreConfigUpdates); - - // data - - this.outputChannel = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(ILogOutputChannel))) - .returns(() => this.outputChannel.object); - this.initData(); - - // artifacts - - this.output = ''; - this.logged = []; - - // linting - - this.linterManager = new LinterManager(this.configService.object); - this.serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) - .returns(() => this.linterManager); - } - - public async getLinter(product: Product, enabled = true): Promise { - const info = this.linterManager.getLinterInfo(product); - - // @ts-ignore We only do this during testing. - this.lintingSettings[info.enabledSettingName] = enabled; - - await this.linterManager.setActiveLintersAsync([product]); - await this.linterManager.enableLintingAsync(enabled); - return this.linterManager.createLinter(product, this.serviceContainer.object); - } - - public async getEnabledLinter(product: Product): Promise { - return this.getLinter(product, true); - } - - public async getDisabledLinter(product: Product): Promise { - return this.getLinter(product, false); - } - - // eslint-disable-next-line class-methods-use-this - protected newMockDocument(filename: string): TypeMoq.IMock { - return newMockDocument(filename); - } - - private initServices(): void { - const workspaceFolder = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - workspaceFolder.setup((f) => f.uri).returns(() => Uri.file(this.workspaceDir)); - this.workspaceService - .setup((s) => s.getWorkspaceFolder(TypeMoq.It.isAny())) - .returns(() => workspaceFolder.object); - - this.appShell - .setup((a) => a.showErrorMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)); - } - - private initConfig(ignoreUpdates = false): void { - this.configService - .setup((c) => - c.updateSetting(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - ) - .callback((setting, value) => { - if (ignoreUpdates) { - return; - } - const prefix = 'linting.'; - if (setting.startsWith(prefix)) { - // @ts-ignore We only do this during testing. - this.lintingSettings[setting.substr(prefix.length)] = value; - } - }) - .returns(() => Promise.resolve(undefined)); - - this.pythonSettings.setup((s) => s.languageServer).returns(() => LanguageServerType.Jedi); - } - - private initData(): void { - this.outputChannel - .setup((o) => o.appendLine(TypeMoq.It.isAny())) - .callback((line) => { - if (this.output === '') { - this.output = line; - } else { - this.output = `${this.output}${os.EOL}${line}`; - } - }); - this.outputChannel - .setup((o) => o.append(TypeMoq.It.isAny())) - .callback((data) => { - this.output += data; - }); - this.outputChannel.setup((o) => o.show()); - } -} diff --git a/src/test/linters/lint.args.test.ts b/src/test/linters/lint.args.test.ts deleted file mode 100644 index 2c32a73052bf..000000000000 --- a/src/test/linters/lint.args.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { Container } from 'inversify'; -import * as path from 'path'; -import * as TypeMoq from 'typemoq'; -import { CancellationTokenSource, TextDocument, Uri, WorkspaceFolder } from 'vscode'; -import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; -import '../../client/common/extensions'; -import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { - IConfigurationService, - IExtensions, - IInstaller, - ILintingSettings, - IPythonSettings, -} from '../../client/common/types'; -import { - IInterpreterAutoSelectionService, - IInterpreterAutoSelectionProxyService, -} from '../../client/interpreter/autoSelection/types'; -import { IInterpreterService } from '../../client/interpreter/contracts'; -import { ServiceContainer } from '../../client/ioc/container'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { Bandit } from '../../client/linters/bandit'; -import { BaseLinter } from '../../client/linters/baseLinter'; -import { Flake8 } from '../../client/linters/flake8'; -import { LinterManager } from '../../client/linters/linterManager'; -import { MyPy } from '../../client/linters/mypy'; -import { Prospector } from '../../client/linters/prospector'; -import { Pycodestyle } from '../../client/linters/pycodestyle'; -import { PyDocStyle } from '../../client/linters/pydocstyle'; -import { PyLama } from '../../client/linters/pylama'; -import { Pylint } from '../../client/linters/pylint'; -import { ILinterManager, ILintingEngine } from '../../client/linters/types'; -import { initialize } from '../initialize'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; - -suite('Linting - Arguments', () => { - [undefined, path.join('users', 'dev_user')].forEach((workspaceUri) => { - [ - Uri.file(path.join('users', 'dev_user', 'development path to', 'one.py')), - Uri.file(path.join('users', 'dev_user', 'development', 'one.py')), - ].forEach((fileUri) => { - suite( - `File path ${fileUri.fsPath.indexOf(' ') > 0 ? 'with' : 'without'} spaces and ${ - workspaceUri ? 'without' : 'with' - } a workspace`, - () => { - let interpreterService: TypeMoq.IMock; - let engine: TypeMoq.IMock; - let configService: TypeMoq.IMock; - let docManager: TypeMoq.IMock; - let settings: TypeMoq.IMock; - let lm: ILinterManager; - let serviceContainer: ServiceContainer; - let document: TypeMoq.IMock; - let workspaceService: TypeMoq.IMock; - let extensionsService: TypeMoq.IMock; - const cancellationToken = new CancellationTokenSource().token; - suiteSetup(initialize); - setup(async () => { - const cont = new Container(); - const serviceManager = new ServiceManager(cont); - - serviceContainer = new ServiceContainer(cont); - - const fs = TypeMoq.Mock.ofType(); - fs.setup((x) => x.fileExists(TypeMoq.It.isAny())).returns( - () => new Promise((resolve) => resolve(true)), - ); - fs.setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns( - () => true, - ); - serviceManager.addSingletonInstance(IFileSystem, fs.object); - - interpreterService = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance( - IInterpreterService, - interpreterService.object, - ); - serviceManager.addSingleton( - IInterpreterAutoSelectionService, - MockAutoSelectionService, - ); - serviceManager.addSingleton( - IInterpreterAutoSelectionProxyService, - MockAutoSelectionService, - ); - engine = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(ILintingEngine, engine.object); - - docManager = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IDocumentManager, docManager.object); - - const lintSettings = TypeMoq.Mock.ofType(); - lintSettings.setup((x) => x.enabled).returns(() => true); - lintSettings.setup((x) => x.lintOnSave).returns(() => true); - lintSettings.setup((x) => x.cwd).returns(() => undefined); - - settings = TypeMoq.Mock.ofType(); - settings.setup((x) => x.linting).returns(() => lintSettings.object); - - configService = TypeMoq.Mock.ofType(); - configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - serviceManager.addSingletonInstance( - IConfigurationService, - configService.object, - ); - - const workspaceFolder: WorkspaceFolder | undefined = workspaceUri - ? { uri: Uri.file(workspaceUri), index: 0, name: '' } - : undefined; - workspaceService = TypeMoq.Mock.ofType(); - workspaceService - .setup((w) => w.getWorkspaceFolder(TypeMoq.It.isAny())) - .returns(() => workspaceFolder); - serviceManager.addSingletonInstance( - IWorkspaceService, - workspaceService.object, - ); - - const installer = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IInstaller, installer.object); - - const platformService = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IPlatformService, platformService.object); - - extensionsService = TypeMoq.Mock.ofType(); - extensionsService.setup((e) => e.getExtension(TypeMoq.It.isAny())).returns(() => undefined); - serviceManager.addSingletonInstance(IExtensions, extensionsService.object); - - lm = new LinterManager(configService.object); - serviceManager.addSingletonInstance(ILinterManager, lm); - document = TypeMoq.Mock.ofType(); - }); - - async function testLinter(linter: BaseLinter, expectedArgs: string[]) { - document.setup((d) => d.uri).returns(() => fileUri); - - let invoked = false; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (linter as any).run = (args: string[]) => { - expect(args).to.deep.equal(expectedArgs); - invoked = true; - return Promise.resolve([]); - }; - await linter.lint(document.object, cancellationToken); - expect(invoked).to.be.equal(true, 'method not invoked'); - } - test('Flake8', async () => { - const linter = new Flake8(serviceContainer, { showPrompt: () => Promise.resolve(false) }); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('Pycodestyle', async () => { - const linter = new Pycodestyle(serviceContainer); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('Prospector', async () => { - const linter = new Prospector(serviceContainer); - const expectedPath = workspaceUri - ? fileUri.fsPath.substring(workspaceUri.length + 2) - : path.basename(fileUri.fsPath); - const expectedArgs = [expectedPath]; - await testLinter(linter, expectedArgs); - }); - test('Pylama', async () => { - const linter = new PyLama(serviceContainer); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('MyPy', async () => { - const linter = new MyPy(serviceContainer); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('Pydocstyle', async () => { - const linter = new PyDocStyle(serviceContainer); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('Pylint', async () => { - const linter = new Pylint(serviceContainer, { showPrompt: () => Promise.resolve(false) }); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - test('Bandit', async () => { - const linter = new Bandit(serviceContainer); - const expectedArgs = [fileUri.fsPath]; - await testLinter(linter, expectedArgs); - }); - }, - ); - }); - }); -}); diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts deleted file mode 100644 index 9887cbc5605a..000000000000 --- a/src/test/linters/lint.functional.test.ts +++ /dev/null @@ -1,889 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as assert from 'assert'; -import * as fs from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import * as sinon from 'sinon'; -import { anything, instance, mock, when } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import { CancellationTokenSource, TextDocument, TextLine, Uri } from 'vscode'; -import { Product } from '../../client/common/installer/productInstaller'; -import { FileSystem } from '../../client/common/platform/fileSystem'; -import { PlatformService } from '../../client/common/platform/platformService'; -import { IFileSystem } from '../../client/common/platform/types'; -import { ProcessServiceFactory } from '../../client/common/process/processFactory'; -import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; -import { PythonToolExecutionService } from '../../client/common/process/pythonToolService'; -import { - IProcessLogger, - IPythonExecutionFactory, - IPythonToolExecutionService, -} from '../../client/common/process/types'; -import { - IConfigurationService, - IDisposableRegistry, - IInterpreterPathService, - IPersistentState, -} from '../../client/common/types'; -import { IEnvironmentVariablesProvider } from '../../client/common/variables/types'; -import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; -import { - IActivatedEnvironmentLaunch, - IComponentAdapter, - IInterpreterService, -} from '../../client/interpreter/contracts'; -import { IServiceContainer } from '../../client/ioc/types'; -import { LINTERID_BY_PRODUCT } from '../../client/linters/constants'; -import { ILintMessage, LinterId, LintMessageSeverity } from '../../client/linters/types'; -import { deleteFile, PYTHON_PATH } from '../common'; -import { BaseTestFixture, getLinterID, getProductName, newMockDocument, throwUnknownProduct } from './common'; -import { IInterpreterAutoSelectionService } from '../../client/interpreter/autoSelection/types'; -import { Conda } from '../../client/pythonEnvironments/common/environmentManagers/conda'; -import * as promptApis from '../../client/linters/prompts/common'; - -const workspaceDir = path.join(__dirname, '..', '..', '..', 'src', 'test'); -const workspaceUri = Uri.file(workspaceDir); -const pythonFilesDir = path.join(workspaceDir, 'pythonFiles', 'linting'); -const fileToLint = path.join(pythonFilesDir, 'file.py'); - -const linterConfigDirs = new Map([ - [LinterId.Flake8, path.join(pythonFilesDir, 'flake8config')], - [LinterId.PyCodeStyle, path.join(pythonFilesDir, 'pycodestyleconfig')], - [LinterId.PyDocStyle, path.join(pythonFilesDir, 'pydocstyleconfig27')], - [LinterId.PyLint, path.join(pythonFilesDir, 'pylintconfig')], -]); -const linterConfigRCFiles = new Map([ - [LinterId.PyLint, '.pylintrc'], - [LinterId.PyDocStyle, '.pydocstyle'], -]); - -const pylintMessagesToBeReturned: ILintMessage[] = [ - { - line: 24, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 30, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 34, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 40, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 44, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 55, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 59, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 62, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling undefined-variable (E0602)', - provider: '', - type: 'warning', - }, - { - line: 70, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 84, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 87, - column: 0, - severity: LintMessageSeverity.Hint, - code: 'C0304', - message: 'Final newline missing', - provider: '', - type: 'warning', - }, - { - line: 11, - column: 20, - severity: LintMessageSeverity.Warning, - code: 'W0613', - message: "Unused argument 'arg'", - provider: '', - type: 'warning', - }, - { - line: 26, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blop' member", - provider: '', - type: 'warning', - }, - { - line: 36, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 46, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 61, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 72, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 75, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 77, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 83, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, -]; -const flake8MessagesToBeReturned: ILintMessage[] = [ - { - line: 5, - column: 1, - severity: LintMessageSeverity.Error, - code: 'E302', - message: 'expected 2 blank lines, found 1', - provider: '', - type: 'E', - }, - { - line: 19, - column: 15, - severity: LintMessageSeverity.Error, - code: 'E127', - message: 'continuation line over-indented for visual indent', - provider: '', - type: 'E', - }, - { - line: 24, - column: 23, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 62, - column: 30, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 70, - column: 22, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 80, - column: 5, - severity: LintMessageSeverity.Error, - code: 'E303', - message: 'too many blank lines (2)', - provider: '', - type: 'E', - }, - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: 'E', - }, -]; -const pycodestyleMessagesToBeReturned: ILintMessage[] = [ - { - line: 5, - column: 1, - severity: LintMessageSeverity.Error, - code: 'E302', - message: 'expected 2 blank lines, found 1', - provider: '', - type: 'E', - }, - { - line: 19, - column: 15, - severity: LintMessageSeverity.Error, - code: 'E127', - message: 'continuation line over-indented for visual indent', - provider: '', - type: 'E', - }, - { - line: 24, - column: 23, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 62, - column: 30, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 70, - column: 22, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 80, - column: 5, - severity: LintMessageSeverity.Error, - code: 'E303', - message: 'too many blank lines (2)', - provider: '', - type: 'E', - }, - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: 'E', - }, -]; -const pydocstyleMessagesToBeReturned: ILintMessage[] = [ - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'e')", - column: 0, - line: 1, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 't')", - column: 0, - line: 5, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D102', - severity: LintMessageSeverity.Information, - message: 'Missing docstring in public method', - column: 4, - line: 8, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D401', - severity: LintMessageSeverity.Information, - message: "First line should be in imperative mood ('thi', not 'this')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('This', not 'this')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'e')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('And', not 'and')", - column: 4, - line: 15, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 't')", - column: 4, - line: 15, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 21, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 21, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 28, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 28, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 38, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 38, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 53, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 53, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 68, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 68, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 80, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 80, - type: '', - provider: 'pydocstyle', - }, -]; - -const filteredFlake8MessagesToBeReturned: ILintMessage[] = [ - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: '', - }, -]; -const filteredPycodestyleMessagesToBeReturned: ILintMessage[] = [ - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: '', - }, -]; - -function getMessages(product: Product): ILintMessage[] { - switch (product) { - case Product.pylint: { - return pylintMessagesToBeReturned; - } - case Product.flake8: { - return flake8MessagesToBeReturned; - } - case Product.pycodestyle: { - return pycodestyleMessagesToBeReturned; - } - case Product.pydocstyle: { - return pydocstyleMessagesToBeReturned; - } - default: { - throwUnknownProduct(product); - return []; - } - } -} - -async function getInfoForConfig(product: Product) { - const prodID = getLinterID(product); - const dirname = linterConfigDirs.get(prodID); - assert.notStrictEqual(dirname, undefined, `tests not set up for ${Product[product]}`); - - const filename = path.join(dirname!, product === Product.pylint ? 'file2.py' : 'file.py'); - let messagesToBeReceived: ILintMessage[] = []; - switch (product) { - case Product.flake8: { - messagesToBeReceived = filteredFlake8MessagesToBeReturned; - break; - } - case Product.pycodestyle: { - messagesToBeReceived = filteredPycodestyleMessagesToBeReturned; - break; - } - default: { - break; - } - } - const basename = linterConfigRCFiles.get(prodID); - return { - filename, - messagesToBeReceived, - origRCFile: basename ? path.join(dirname!, basename) : '', - }; -} - -class TestFixture extends BaseTestFixture { - constructor(printLogs = false) { - const serviceContainer = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - const configService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - const processLogger = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - const componentAdapter = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - componentAdapter - .setup((c) => c.getCondaEnvironment(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)); - - const filesystem = new FileSystem(); - processLogger - .setup((p) => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => { - /** No body */ - }); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IProcessLogger), TypeMoq.It.isAny())) - .returns(() => processLogger.object); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) - .returns(() => filesystem); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IComponentAdapter), TypeMoq.It.isAny())) - .returns(() => componentAdapter.object); - const activatedEnvironmentLaunch = TypeMoq.Mock.ofType(); - activatedEnvironmentLaunch - .setup((a) => a.selectIfLaunchedViaActivatedEnv()) - .returns(() => Promise.resolve(undefined)); - serviceContainer - .setup((s) => s.get(TypeMoq.It.isValue(IActivatedEnvironmentLaunch), TypeMoq.It.isAny())) - .returns(() => activatedEnvironmentLaunch.object); - const platformService = new PlatformService(); - - super( - platformService, - filesystem, - TestFixture.newPythonToolExecService(serviceContainer.object), - TestFixture.newPythonExecFactory(serviceContainer, configService.object), - configService, - serviceContainer, - false, - workspaceDir, - printLogs, - ); - - this.pythonSettings.setup((s) => s.pythonPath).returns(() => PYTHON_PATH); - } - - private static newPythonToolExecService(serviceContainer: IServiceContainer): IPythonToolExecutionService { - // We do not worry about the IProcessServiceFactory possibly - // needed by PythonToolExecutionService. - return new PythonToolExecutionService(serviceContainer); - } - - private static newPythonExecFactory( - serviceContainer: TypeMoq.IMock, - configService: IConfigurationService, - ): IPythonExecutionFactory { - const envVarsService = TypeMoq.Mock.ofType( - undefined, - TypeMoq.MockBehavior.Strict, - ); - envVarsService - .setup((e) => e.getEnvironmentVariables(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(process.env)); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny())) - .returns(() => envVarsService.object); - const disposableRegistry: IDisposableRegistry = []; - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())) - .returns(() => disposableRegistry); - - const envActivationService = TypeMoq.Mock.ofType( - undefined, - TypeMoq.MockBehavior.Strict, - ); - - const interpreterService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - interpreterService.setup((i) => i.hasInterpreters()).returns(() => Promise.resolve(true)); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IInterpreterService), TypeMoq.It.isAny())) - .returns(() => interpreterService.object); - - sinon.stub(Conda, 'getConda').resolves(new Conda('conda')); - sinon.stub(Conda.prototype, 'getCondaVersion').resolves(undefined); - - const processLogger = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - processLogger - .setup((p) => p.logProcess(TypeMoq.It.isAnyString(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => { - /** No body */ - }); - const procServiceFactory = new ProcessServiceFactory( - envVarsService.object, - processLogger.object, - disposableRegistry, - ); - const pyenvs: IComponentAdapter = mock(); - - const autoSelection = mock(); - const interpreterPathExpHelper = mock(); - when(interpreterPathExpHelper.get(anything())).thenReturn('selected interpreter path'); - - return new PythonExecutionFactory( - serviceContainer.object, - envActivationService.object, - procServiceFactory, - configService, - instance(pyenvs), - instance(autoSelection), - instance(interpreterPathExpHelper), - ); - } - - // eslint-disable-next-line class-methods-use-this - public makeDocument(filename: string): TextDocument { - const doc = newMockDocument(filename); - - doc.setup((d) => d.lineAt(TypeMoq.It.isAny())).returns((lno) => { - const lines = fs.readFileSync(filename).toString().split(os.EOL); - const textline = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - textline.setup((t) => t.text).returns(() => lines[lno]); - return textline.object; - }); - - return doc.object; - } -} - -suite('Linting Functional Tests', () => { - let isExtensionEnabledStub: sinon.SinonStub; - let isExtensionDisabledStub: sinon.SinonStub; - let doNotShowPromptStateStub: sinon.SinonStub; - let persistentState: TypeMoq.IMock>; - setup(() => { - isExtensionEnabledStub = sinon.stub(promptApis, 'isExtensionEnabled'); - isExtensionDisabledStub = sinon.stub(promptApis, 'isExtensionDisabled'); - // For these tests we assume that linter extensions are not installed. - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - persistentState = TypeMoq.Mock.ofType>(); - persistentState.setup((p) => p.value).returns(() => true); - doNotShowPromptStateStub = sinon.stub(promptApis, 'doNotShowPromptState'); - doNotShowPromptStateStub.returns(persistentState.object); - }); - teardown(() => { - sinon.restore(); - }); - // These are integration tests that mock out everything except - // the filesystem and process execution. - - async function testLinterMessages( - fixture: TestFixture, - product: Product, - pythonFile: string, - messagesToBeReceived: ILintMessage[], - ) { - const doc = fixture.makeDocument(pythonFile); - await fixture.linterManager.setActiveLintersAsync([product], doc.uri); - const linter = await fixture.linterManager.createLinter(product, fixture.serviceContainer.object); - - const messages = await linter.lint(doc, new CancellationTokenSource().token); - - if (messagesToBeReceived.length === 0) { - assert.strictEqual(messages.length, 0, `No errors in linter, Output - ${fixture.output}`); - } else if (fixture.output.indexOf('ENOENT') === -1) { - // Pylint for Python Version 2.7 could return 80 linter messages, where as in 3.5 it might only return 1. - // Looks like pylint stops linting as soon as it comes across any ERRORS. - assert.notStrictEqual(messages.length, 0, `No errors in linter, Output - ${fixture.output}`); - } - } - for (const product of LINTERID_BY_PRODUCT.keys()) { - test(getProductName(product), async function () { - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { - return this.skip(); - } - - const fixture = new TestFixture(); - const messagesToBeReturned = getMessages(product); - await testLinterMessages(fixture, product, fileToLint, messagesToBeReturned); - - return undefined; - }); - } - for (const product of LINTERID_BY_PRODUCT.keys()) { - test(`${getProductName(product)} with config in root`, async function () { - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { - return this.skip(); - } - - const fixture = new TestFixture(); - const { filename, messagesToBeReceived, origRCFile } = await getInfoForConfig(product); - let rcfile = ''; - async function cleanUp() { - if (rcfile !== '') { - await deleteFile(rcfile); - } - } - if (origRCFile !== '') { - rcfile = path.join(workspaceUri.fsPath, path.basename(origRCFile)); - await fs.copy(origRCFile, rcfile); - } - - try { - await testLinterMessages(fixture, product, filename, messagesToBeReceived); - } finally { - await cleanUp(); - } - - return undefined; - }); - } - - async function testLinterMessageCount( - fixture: TestFixture, - product: Product, - pythonFile: string, - messageCountToBeReceived: number, - ) { - const doc = fixture.makeDocument(pythonFile); - await fixture.linterManager.setActiveLintersAsync([product], doc.uri); - const linter = await fixture.linterManager.createLinter(product, fixture.serviceContainer.object); - - const messages = await linter.lint(doc, new CancellationTokenSource().token); - - assert.strictEqual( - messages.length, - messageCountToBeReceived, - 'Expected number of lint errors does not match lint error count', - ); - } - test('Three line output counted as one message', async () => { - const maxErrors = 5; - const fixture = new TestFixture(); - fixture.lintingSettings.maxNumberOfProblems = maxErrors; - await testLinterMessageCount( - fixture, - Product.pylint, - path.join(pythonFilesDir, 'threeLineLints.py'), - maxErrors, - ); - }); - - test('Linters use config in cwd directory', async () => { - const maxErrors = 0; - const fixture = new TestFixture(); - fixture.lintingSettings.cwd = path.join(pythonFilesDir, 'pylintcwd'); - - await testLinterMessageCount( - fixture, - Product.pylint, - path.join(pythonFilesDir, 'threeLineLints.py'), - maxErrors, - ); - }); -}); diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts deleted file mode 100644 index 5c1cae31d158..000000000000 --- a/src/test/linters/lint.multiroot.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as assert from 'assert'; -import * as path from 'path'; -import { CancellationTokenSource, ConfigurationTarget, Uri, workspace } from 'vscode'; -import { LanguageServerType } from '../../client/activation/types'; -import { PythonSettings } from '../../client/common/configSettings'; -import { LinterProductPathService, TestFrameworkProductPathService } from '../../client/common/installer/productPath'; -import { ProductService } from '../../client/common/installer/productService'; -import { IProductPathService, IProductService } from '../../client/common/installer/types'; -import { IConfigurationService, Product, ProductType } from '../../client/common/types'; -import { OSType } from '../../client/common/utils/platform'; -import { PythonPathUpdaterService } from '../../client/interpreter/configuration/pythonPathUpdaterService'; -import { PythonPathUpdaterServiceFactory } from '../../client/interpreter/configuration/pythonPathUpdaterServiceFactory'; -import { - IPythonPathUpdaterServiceManager, - IPythonPathUpdaterServiceFactory, -} from '../../client/interpreter/configuration/types'; -import { IActivatedEnvironmentLaunch } from '../../client/interpreter/contracts'; -import { ActivatedEnvironmentLaunch } from '../../client/interpreter/virtualEnvs/activatedEnvLaunch'; -import { ILinter, ILinterManager } from '../../client/linters/types'; -import { isOs } from '../common'; -import { TEST_TIMEOUT } from '../constants'; -import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; - -const multirootPath = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRootWkspc'); - -suite('Multiroot Linting', () => { - const pylintSetting = 'linting.pylintEnabled'; - const flake8Setting = 'linting.flake8Enabled'; - - let ioc: UnitTestIocContainer; - suiteSetup(async function () { - if (!IS_MULTI_ROOT_TEST) { - this.skip(); - } - await initialize(); - await initializeDI(); - await initializeTest(); - }); - suiteTeardown(async () => { - await ioc?.dispose(); - await closeActiveWindows(); - PythonSettings.dispose(); - }); - teardown(async () => { - await closeActiveWindows(); - }); - - async function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerCommonTypes(false); - ioc.registerProcessTypes(); - ioc.registerLinterTypes(); - ioc.registerVariableTypes(); - ioc.registerFileSystemTypes(); - await ioc.registerMockInterpreterTypes(); - ioc.serviceManager.addSingleton( - IActivatedEnvironmentLaunch, - ActivatedEnvironmentLaunch, - ); - ioc.serviceManager.addSingleton( - IPythonPathUpdaterServiceManager, - PythonPathUpdaterService, - ); - ioc.serviceManager.addSingleton( - IPythonPathUpdaterServiceFactory, - PythonPathUpdaterServiceFactory, - ); - ioc.registerInterpreterStorageTypes(); - ioc.serviceManager.addSingletonInstance(IProductService, new ProductService()); - ioc.serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter, - ); - ioc.serviceManager.addSingleton( - IProductPathService, - TestFrameworkProductPathService, - ProductType.TestFramework, - ); - } - - async function createLinter(product: Product): Promise { - const lm = ioc.serviceContainer.get(ILinterManager); - return lm.createLinter(product, ioc.serviceContainer); - } - async function testLinterInWorkspaceFolder( - product: Product, - workspaceFolderRelativePath: string, - mustHaveErrors: boolean, - ): Promise { - const fileToLint = path.join(multirootPath, workspaceFolderRelativePath, 'file.py'); - const cancelToken = new CancellationTokenSource(); - const document = await workspace.openTextDocument(fileToLint); - - const linter = await createLinter(product); - const messages = await linter.lint(document, cancelToken.token); - - const errorMessage = mustHaveErrors ? 'No errors returned by linter' : 'Errors returned by linter'; - assert.strictEqual(messages.length > 0, mustHaveErrors, errorMessage); - } - - test('Enabling Pylint in root and also in Workspace, should return errors', async function () { - // Timing out on Windows, tracked by #18337. - if (isOs(OSType.Windows)) { - return this.skip(); - } - - await runTest(Product.pylint, true, true, pylintSetting); - - return undefined; - }).timeout(TEST_TIMEOUT * 2); - test('Enabling Pylint in root and disabling in Workspace, should not return errors', async () => { - await runTest(Product.pylint, true, false, pylintSetting); - }).timeout(TEST_TIMEOUT * 2); - test('Disabling Pylint in root and enabling in Workspace, should return errors', async function () { - // Timing out on Windows, tracked by #18337. - if (isOs(OSType.Windows)) { - return this.skip(); - } - - await runTest(Product.pylint, false, true, pylintSetting); - - return undefined; - }).timeout(TEST_TIMEOUT * 2); - - test('Enabling Flake8 in root and also in Workspace, should return errors', async function () { - // Timing out on Windows, tracked by #18337. - if (isOs(OSType.Windows)) { - return this.skip(); - } - - await runTest(Product.flake8, true, true, flake8Setting); - - return undefined; - }).timeout(TEST_TIMEOUT * 2); - test('Enabling Flake8 in root and disabling in Workspace, should not return errors', async () => { - await runTest(Product.flake8, true, false, flake8Setting); - }).timeout(TEST_TIMEOUT * 2); - test('Disabling Flake8 in root and enabling in Workspace, should return errors', async function () { - // Timing out on Windows, tracked by #18337. - if (isOs(OSType.Windows)) { - return this.skip(); - } - - await runTest(Product.flake8, false, true, flake8Setting); - - return undefined; - }).timeout(TEST_TIMEOUT * 2); - - async function runTest(product: Product, global: boolean, wks: boolean, setting: string): Promise { - const config = ioc.serviceContainer.get(IConfigurationService); - await config.updateSetting( - 'languageServer', - LanguageServerType.Jedi, - Uri.file(multirootPath), - ConfigurationTarget.Global, - ); - await Promise.all([ - config.updateSetting(setting, global, Uri.file(multirootPath), ConfigurationTarget.Global), - config.updateSetting(setting, wks, Uri.file(multirootPath), ConfigurationTarget.Workspace), - ]); - await testLinterInWorkspaceFolder(product, 'workspace1', wks); - await Promise.all( - [ConfigurationTarget.Global, ConfigurationTarget.Workspace].map((configTarget) => - config.updateSetting(setting, undefined, Uri.file(multirootPath), configTarget), - ), - ); - } -}); diff --git a/src/test/linters/lint.provider.test.ts b/src/test/linters/lint.provider.test.ts deleted file mode 100644 index 760c2282ba05..000000000000 --- a/src/test/linters/lint.provider.test.ts +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { Container } from 'inversify'; -import * as TypeMoq from 'typemoq'; -import * as vscode from 'vscode'; -import { LanguageServerType } from '../../client/activation/types'; -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - IWorkspaceService, -} from '../../client/common/application/types'; -import { PersistentStateFactory } from '../../client/common/persistentState'; -import { IFileSystem } from '../../client/common/platform/types'; -import { - GLOBAL_MEMENTO, - IConfigurationService, - IInstaller, - ILintingSettings, - IMemento, - IPersistentStateFactory, - IPythonSettings, - Product, - Resource, - WORKSPACE_MEMENTO, -} from '../../client/common/types'; -import { createDeferred } from '../../client/common/utils/async'; -import { - IInterpreterAutoSelectionService, - IInterpreterAutoSelectionProxyService, -} from '../../client/interpreter/autoSelection/types'; -import { IInterpreterService } from '../../client/interpreter/contracts'; -import { ServiceContainer } from '../../client/ioc/container'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { LinterManager } from '../../client/linters/linterManager'; -import { ILinterManager, ILintingEngine } from '../../client/linters/types'; -import { LinterProvider } from '../../client/providers/linterProvider'; -import { initialize } from '../initialize'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; -import { MockMemento } from '../mocks/mementos'; - -suite('Linting - Provider', () => { - let interpreterService: TypeMoq.IMock; - let engine: TypeMoq.IMock; - let configService: TypeMoq.IMock; - let docManager: TypeMoq.IMock; - let settings: TypeMoq.IMock; - let lm: ILinterManager; - let serviceContainer: ServiceContainer; - let emitter: vscode.EventEmitter; - let document: TypeMoq.IMock; - let fs: TypeMoq.IMock; - let appShell: TypeMoq.IMock; - let linterInstaller: TypeMoq.IMock; - let workspaceService: TypeMoq.IMock; - let workspaceConfig: TypeMoq.IMock; - - suiteSetup(initialize); - setup(async () => { - const cont = new Container(); - const serviceManager = new ServiceManager(cont); - - serviceContainer = new ServiceContainer(cont); - - fs = TypeMoq.Mock.ofType(); - fs.setup((x) => x.fileExists(TypeMoq.It.isAny())).returns( - () => new Promise((resolve) => resolve(true)), - ); - fs.setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => true); - serviceManager.addSingletonInstance(IFileSystem, fs.object); - - interpreterService = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IInterpreterService, interpreterService.object); - - engine = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(ILintingEngine, engine.object); - - docManager = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IDocumentManager, docManager.object); - - const lintSettings = TypeMoq.Mock.ofType(); - lintSettings.setup((x) => x.enabled).returns(() => true); - lintSettings.setup((x) => x.lintOnSave).returns(() => true); - - settings = TypeMoq.Mock.ofType(); - settings.setup((x) => x.linting).returns(() => lintSettings.object); - settings.setup((p) => p.languageServer).returns(() => LanguageServerType.Jedi); - - configService = TypeMoq.Mock.ofType(); - configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - serviceManager.addSingletonInstance(IConfigurationService, configService.object); - - appShell = TypeMoq.Mock.ofType(); - linterInstaller = TypeMoq.Mock.ofType(); - - workspaceService = TypeMoq.Mock.ofType(); - workspaceConfig = TypeMoq.Mock.ofType(); - workspaceService - .setup((w) => w.getConfiguration('python', TypeMoq.It.isAny())) - .returns(() => workspaceConfig.object); - workspaceService.setup((w) => w.getConfiguration('python')).returns(() => workspaceConfig.object); - - serviceManager.addSingletonInstance(IApplicationShell, appShell.object); - serviceManager.addSingletonInstance(IInstaller, linterInstaller.object); - serviceManager.addSingletonInstance(IWorkspaceService, workspaceService.object); - serviceManager.addSingleton( - IInterpreterAutoSelectionService, - MockAutoSelectionService, - ); - serviceManager.addSingleton( - IInterpreterAutoSelectionProxyService, - MockAutoSelectionService, - ); - serviceManager.addSingleton(IPersistentStateFactory, PersistentStateFactory); - serviceManager.addSingleton(IMemento, MockMemento, GLOBAL_MEMENTO); - serviceManager.addSingleton(IMemento, MockMemento, WORKSPACE_MEMENTO); - serviceManager.addSingletonInstance( - ICommandManager, - TypeMoq.Mock.ofType().object, - ); - lm = new LinterManager(configService.object); - serviceManager.addSingletonInstance(ILinterManager, lm); - emitter = new vscode.EventEmitter(); - document = TypeMoq.Mock.ofType(); - }); - - test('Lint on open file', async () => { - docManager.setup((x) => x.onDidOpenTextDocument).returns(() => emitter.event); - document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.py')); - document.setup((x) => x.languageId).returns(() => 'python'); - - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - emitter.fire(document.object); - engine.verify((x) => x.lintDocument(document.object, 'auto'), TypeMoq.Times.once()); - }); - - test('Lint on save file', async () => { - docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.py')); - document.setup((x) => x.languageId).returns(() => 'python'); - - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - emitter.fire(document.object); - engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.once()); - }); - - test('No lint on open other files', async () => { - docManager.setup((x) => x.onDidOpenTextDocument).returns(() => emitter.event); - document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.cs')); - document.setup((x) => x.languageId).returns(() => 'csharp'); - - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - emitter.fire(document.object); - engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); - }); - - test('No lint on save other files', async () => { - docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup((x) => x.uri).returns(() => vscode.Uri.file('test.cs')); - document.setup((x) => x.languageId).returns(() => 'csharp'); - - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - emitter.fire(document.object); - engine.verify((x) => x.lintDocument(document.object, 'save'), TypeMoq.Times.never()); - }); - - test('Lint on change interpreters', async () => { - const e = new vscode.EventEmitter(); - interpreterService.setup((x) => x.onDidChangeInterpreter).returns(() => e.event); - - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - e.fire(undefined); - engine.verify((x) => x.lintOpenPythonFiles(), TypeMoq.Times.once()); - }); - - test('Lint on save pylintrc', async () => { - docManager.setup((x) => x.onDidSaveTextDocument).returns(() => emitter.event); - document.setup((x) => x.uri).returns(() => vscode.Uri.file('.pylintrc')); - - await lm.setActiveLintersAsync([Product.pylint]); - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - emitter.fire(document.object); - - const deferred = createDeferred(); - setTimeout(() => deferred.resolve(), 2000); - await deferred.promise; - engine.verify((x) => x.lintOpenPythonFiles(), TypeMoq.Times.once()); - }); - - test('Diagnostic cleared on file close', async () => testClearDiagnosticsOnClose(true)); - test('Diagnostic not cleared on file opened in another tab', async () => testClearDiagnosticsOnClose(false)); - - async function testClearDiagnosticsOnClose(closed: boolean) { - docManager.setup((x) => x.onDidCloseTextDocument).returns(() => emitter.event); - - const uri = vscode.Uri.file('test.py'); - document.setup((x) => x.uri).returns(() => uri); - document.setup((x) => x.isClosed).returns(() => closed); - - docManager.setup((x) => x.textDocuments).returns(() => (closed ? [] : [document.object])); - const linterProvider = new LinterProvider(serviceContainer); - await linterProvider.activate(); - - emitter.fire(document.object); - const timesExpected = closed ? TypeMoq.Times.once() : TypeMoq.Times.never(); - engine.verify((x) => x.clearDiagnostics(TypeMoq.It.isAny()), timesExpected); - } -}); diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts deleted file mode 100644 index 837830f0c499..000000000000 --- a/src/test/linters/lint.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as assert from 'assert'; -import { ConfigurationTarget } from 'vscode'; -import { Product } from '../../client/common/installer/productInstaller'; -import { LinterProductPathService, TestFrameworkProductPathService } from '../../client/common/installer/productPath'; -import { ProductService } from '../../client/common/installer/productService'; -import { IProductPathService, IProductService } from '../../client/common/installer/types'; -import { IConfigurationService, ILintingSettings, ProductType } from '../../client/common/types'; -import { LINTERID_BY_PRODUCT } from '../../client/linters/constants'; -import { LinterManager } from '../../client/linters/linterManager'; -import { ILinterManager } from '../../client/linters/types'; -import { rootWorkspaceUri } from '../common'; -import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST } from '../initialize'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; - -suite('Linting Settings', () => { - let ioc: UnitTestIocContainer; - let linterManager: ILinterManager; - let configService: IConfigurationService; - - suiteSetup(async () => { - await initialize(); - }); - setup(async () => { - await initializeDI(); - await initializeTest(); - }); - suiteTeardown(closeActiveWindows); - teardown(async () => { - await closeActiveWindows(); - await resetSettings(); - await ioc.dispose(); - }); - - async function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerCommonTypes(false); - ioc.registerProcessTypes(); - ioc.registerLinterTypes(); - ioc.registerVariableTypes(); - ioc.registerPlatformTypes(); - configService = ioc.serviceContainer.get(IConfigurationService); - linterManager = new LinterManager(configService); - ioc.serviceManager.addSingletonInstance(IProductService, new ProductService()); - ioc.serviceManager.addSingleton( - IProductPathService, - LinterProductPathService, - ProductType.Linter, - ); - ioc.serviceManager.addSingleton( - IProductPathService, - TestFrameworkProductPathService, - ProductType.TestFramework, - ); - } - - async function resetSettings(lintingEnabled = true) { - // Don't run these updates in parallel, as they are updating the same file. - const target = IS_MULTI_ROOT_TEST ? ConfigurationTarget.WorkspaceFolder : ConfigurationTarget.Workspace; - - await configService.updateSetting('linting.enabled', lintingEnabled, rootWorkspaceUri, target); - await configService.updateSetting('linting.lintOnSave', false, rootWorkspaceUri, target); - - linterManager.getAllLinterInfos().forEach(async (x) => { - const settingKey = `linting.${x.enabledSettingName}`; - await configService.updateSetting(settingKey, false, rootWorkspaceUri, target); - }); - } - - test('enable through manager (global)', async () => { - const settings = configService.getSettings(); - await resetSettings(false); - - await linterManager.enableLintingAsync(false); - assert.strictEqual(settings.linting.enabled, false, 'mismatch'); - - await linterManager.enableLintingAsync(true); - assert.strictEqual(settings.linting.enabled, true, 'mismatch'); - }); - - LINTERID_BY_PRODUCT.forEach((_, key) => { - const product = Product[key]; - - test(`enable through manager (${product})`, async () => { - const settings = configService.getSettings(); - await resetSettings(); - - const name = `${product}Enabled` as keyof ILintingSettings; - - assert.strictEqual(settings.linting[name], false, 'mismatch'); - - await linterManager.setActiveLintersAsync([key]); - - assert.strictEqual(settings.linting[name], true, 'mismatch'); - linterManager.getAllLinterInfos().forEach(async (x) => { - if (x.product !== key) { - assert.strictEqual( - settings.linting[x.enabledSettingName as keyof ILintingSettings], - false, - 'mismatch', - ); - } - }); - }); - }); -}); diff --git a/src/test/linters/lint.unit.test.ts b/src/test/linters/lint.unit.test.ts deleted file mode 100644 index 02bdd4c82c79..000000000000 --- a/src/test/linters/lint.unit.test.ts +++ /dev/null @@ -1,854 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as assert from 'assert'; -import * as os from 'os'; -import * as sinon from 'sinon'; -import * as TypeMoq from 'typemoq'; -import { CancellationTokenSource, TextDocument, TextLine } from 'vscode'; -import { Product } from '../../client/common/installer/productInstaller'; -import { ProductNames } from '../../client/common/installer/productNames'; -import { ProductService } from '../../client/common/installer/productService'; -import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { - IPythonExecutionFactory, - IPythonExecutionService, - IPythonToolExecutionService, -} from '../../client/common/process/types'; -import { IPersistentState, ProductType } from '../../client/common/types'; -import { LINTERID_BY_PRODUCT } from '../../client/linters/constants'; -import * as promptApis from '../../client/linters/prompts/common'; -import { ILintMessage, LintMessageSeverity } from '../../client/linters/types'; -import { - BaseTestFixture, - getLinterID, - getProductName, - linterMessageAsLine, - pylintLinterMessagesAsOutput, - throwUnknownProduct, -} from './common'; - -const pylintMessagesToBeReturned: ILintMessage[] = [ - { - line: 24, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 30, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 34, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 40, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 44, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 55, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 59, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0012', - message: 'Locally enabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 62, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling undefined-variable (E0602)', - provider: '', - type: 'warning', - }, - { - line: 70, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 84, - column: 0, - severity: LintMessageSeverity.Information, - code: 'I0011', - message: 'Locally disabling no-member (E1101)', - provider: '', - type: 'warning', - }, - { - line: 87, - column: 0, - severity: LintMessageSeverity.Hint, - code: 'C0304', - message: 'Final newline missing', - provider: '', - type: 'warning', - }, - { - line: 11, - column: 20, - severity: LintMessageSeverity.Warning, - code: 'W0613', - message: "Unused argument 'arg'", - provider: '', - type: 'warning', - }, - { - line: 26, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blop' member", - provider: '', - type: 'warning', - }, - { - line: 36, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - }, - { - line: 46, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: undefined, - endColumn: undefined, - }, - { - line: 61, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: 61, - endColumn: undefined, - }, - { - line: 72, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: 72, - endColumn: 28, - }, - { - line: 75, - column: 18, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: 75, - endColumn: 28, - }, - { - line: 77, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: 77, - endColumn: 24, - }, - { - line: 83, - column: 14, - severity: LintMessageSeverity.Error, - code: 'E1101', - message: "Instance of 'Foo' has no 'blip' member", - provider: '', - type: 'warning', - endLine: 83, - endColumn: 24, - }, -]; -const flake8MessagesToBeReturned: ILintMessage[] = [ - { - line: 5, - column: 1, - severity: LintMessageSeverity.Error, - code: 'E302', - message: 'expected 2 blank lines, found 1', - provider: '', - type: 'E', - }, - { - line: 19, - column: 15, - severity: LintMessageSeverity.Error, - code: 'E127', - message: 'continuation line over-indented for visual indent', - provider: '', - type: 'E', - }, - { - line: 24, - column: 23, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 62, - column: 30, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 70, - column: 22, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 80, - column: 5, - severity: LintMessageSeverity.Error, - code: 'E303', - message: 'too many blank lines (2)', - provider: '', - type: 'E', - }, - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: 'E', - }, -]; -const pycodestyleMessagesToBeReturned: ILintMessage[] = [ - { - line: 5, - column: 1, - severity: LintMessageSeverity.Error, - code: 'E302', - message: 'expected 2 blank lines, found 1', - provider: '', - type: 'E', - }, - { - line: 19, - column: 15, - severity: LintMessageSeverity.Error, - code: 'E127', - message: 'continuation line over-indented for visual indent', - provider: '', - type: 'E', - }, - { - line: 24, - column: 23, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 62, - column: 30, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 70, - column: 22, - severity: LintMessageSeverity.Error, - code: 'E261', - message: 'at least two spaces before inline comment', - provider: '', - type: 'E', - }, - { - line: 80, - column: 5, - severity: LintMessageSeverity.Error, - code: 'E303', - message: 'too many blank lines (2)', - provider: '', - type: 'E', - }, - { - line: 87, - column: 24, - severity: LintMessageSeverity.Warning, - code: 'W292', - message: 'no newline at end of file', - provider: '', - type: 'E', - }, -]; -const pydocstyleMessagesToBeReturned: ILintMessage[] = [ - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'e')", - column: 0, - line: 1, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 't')", - column: 0, - line: 5, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D102', - severity: LintMessageSeverity.Information, - message: 'Missing docstring in public method', - column: 4, - line: 8, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D401', - severity: LintMessageSeverity.Information, - message: "First line should be in imperative mood ('thi', not 'this')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('This', not 'this')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'e')", - column: 4, - line: 11, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('And', not 'and')", - column: 4, - line: 15, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 't')", - column: 4, - line: 15, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 21, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 21, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 28, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 28, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 38, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 38, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 53, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 53, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 68, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 68, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D403', - severity: LintMessageSeverity.Information, - message: "First word of the first line should be properly capitalized ('Test', not 'test')", - column: 4, - line: 80, - type: '', - provider: 'pydocstyle', - }, - { - code: 'D400', - severity: LintMessageSeverity.Information, - message: "First line should end with a period (not 'g')", - column: 4, - line: 80, - type: '', - provider: 'pydocstyle', - }, -]; - -class TestFixture extends BaseTestFixture { - public platformService: TypeMoq.IMock; - - public filesystem: TypeMoq.IMock; - - public pythonToolExecService: TypeMoq.IMock; - - public pythonExecService: TypeMoq.IMock; - - public pythonExecFactory: TypeMoq.IMock; - - constructor(workspaceDir = '.', printLogs = false) { - const platformService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - const filesystem = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - const pythonToolExecService = TypeMoq.Mock.ofType( - undefined, - TypeMoq.MockBehavior.Strict, - ); - const pythonExecFactory = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - super( - platformService.object, - filesystem.object, - pythonToolExecService.object, - pythonExecFactory.object, - undefined, - undefined, - true, - workspaceDir, - printLogs, - ); - - this.platformService = platformService; - this.filesystem = filesystem; - this.pythonToolExecService = pythonToolExecService; - this.pythonExecService = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - this.pythonExecFactory = pythonExecFactory; - - this.filesystem.setup((f) => f.fileExists(TypeMoq.It.isAny())).returns(() => Promise.resolve(true)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.pythonExecService.setup((s: any) => s.then).returns(() => undefined); - this.pythonExecService - .setup((s) => s.isModuleInstalled(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(true)); - - this.pythonExecFactory - .setup((f) => f.create(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(this.pythonExecService.object)); - } - - public makeDocument(product: Product, filename: string): TextDocument { - const doc = this.newMockDocument(filename); - if (product === Product.pydocstyle) { - const dummyLine = TypeMoq.Mock.ofType(undefined, TypeMoq.MockBehavior.Strict); - dummyLine.setup((d) => d.text).returns(() => ' ...'); - doc.setup((s) => s.lineAt(TypeMoq.It.isAny())).returns(() => dummyLine.object); - } - return doc.object; - } - - public setDefaultMessages(product: Product): ILintMessage[] { - let messages: ILintMessage[]; - switch (product) { - case Product.pylint: { - messages = pylintMessagesToBeReturned; - break; - } - case Product.flake8: { - messages = flake8MessagesToBeReturned; - break; - } - case Product.pycodestyle: { - messages = pycodestyleMessagesToBeReturned; - break; - } - case Product.pydocstyle: { - messages = pydocstyleMessagesToBeReturned; - break; - } - default: { - throwUnknownProduct(product); - return []; - } - } - this.setMessages(messages, product); - return messages; - } - - public setMessages(messages: ILintMessage[], product?: Product) { - if (messages.length === 0) { - this.setStdout(''); - return; - } - - if (product && getLinterID(product) === 'pylint') { - this.setStdout(pylintLinterMessagesAsOutput(messages)); - return; - } - const lines: string[] = []; - for (const msg of messages) { - if (msg.provider === '' && product) { - msg.provider = getLinterID(product); - } - const line = linterMessageAsLine(msg); - lines.push(line); - } - this.setStdout(lines.join(os.EOL) + os.EOL); - } - - public setStdout(stdout: string) { - this.pythonToolExecService - .setup((s) => s.execForLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve({ stdout })); - } -} - -suite('Linting Scenarios', () => { - // Note that these aren't actually unit tests. Instead they are - // integration tests with heavy usage of mocks. - - let isExtensionEnabledStub: sinon.SinonStub; - let isExtensionDisabledStub: sinon.SinonStub; - let doNotShowPromptStateStub: sinon.SinonStub; - let persistentState: TypeMoq.IMock>; - setup(() => { - isExtensionEnabledStub = sinon.stub(promptApis, 'isExtensionEnabled'); - isExtensionDisabledStub = sinon.stub(promptApis, 'isExtensionDisabled'); - // For these tests we assume that linter extensions are not installed. - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - persistentState = TypeMoq.Mock.ofType>(); - persistentState.setup((p) => p.value).returns(() => true); - doNotShowPromptStateStub = sinon.stub(promptApis, 'doNotShowPromptState'); - doNotShowPromptStateStub.returns(persistentState.object); - }); - - teardown(() => { - sinon.restore(); - }); - - test('No linting with PyLint (enabled) when disabled at top-level', async () => { - const product = Product.pylint; - const fixture = new TestFixture(); - fixture.lintingSettings.enabled = false; - fixture.setDefaultMessages(product); - const linter = await fixture.getEnabledLinter(product); - - const messages = await linter.lint( - fixture.makeDocument(product, 'spam.py'), - new CancellationTokenSource().token, - ); - - assert.strictEqual( - messages.length, - 0, - `Unexpected linter errors when linting is disabled, Output - ${fixture.output}`, - ); - }); - - test('No linting with Pylint disabled (and Flake8 enabled)', async () => { - const product = Product.pylint; - const fixture = new TestFixture(); - fixture.lintingSettings.enabled = true; - fixture.lintingSettings.flake8Enabled = true; - fixture.setDefaultMessages(Product.pylint); - const linter = await fixture.getDisabledLinter(product); - - const messages = await linter.lint( - fixture.makeDocument(product, 'spam.py'), - new CancellationTokenSource().token, - ); - - assert.strictEqual( - messages.length, - 0, - `Unexpected linter errors when linting is disabled, Output - ${fixture.output}`, - ); - }); - - async function testEnablingDisablingOfLinter(fixture: TestFixture, product: Product, enabled: boolean) { - fixture.lintingSettings.enabled = true; - fixture.setDefaultMessages(product); - if (enabled) { - fixture.setDefaultMessages(product); - } - const linter = await fixture.getLinter(product, enabled); - - const messages = await linter.lint( - fixture.makeDocument(product, 'spam.py'), - new CancellationTokenSource().token, - ); - - if (enabled) { - assert.notStrictEqual( - messages.length, - 0, - `Expected linter errors when linter is enabled, Output - ${fixture.output}`, - ); - } else { - assert.strictEqual( - messages.length, - 0, - `Unexpected linter errors when linter is disabled, Output - ${fixture.output}`, - ); - } - } - for (const product of LINTERID_BY_PRODUCT.keys()) { - for (const enabled of [false, true]) { - test(`${enabled ? 'Enable' : 'Disable'} ${getProductName(product)} and run linter`, async function () { - // TODO: Add coverage for these linters. - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { - this.skip(); - } - - const fixture = new TestFixture(); - await testEnablingDisablingOfLinter(fixture, product, enabled); - }); - } - } - for (const useMinimal of [true, false]) { - for (const enabled of [true, false]) { - test(`PyLint ${enabled ? 'enabled' : 'disabled'} with${ - useMinimal ? '' : 'out' - } minimal checkers`, async () => { - const fixture = new TestFixture(); - await testEnablingDisablingOfLinter(fixture, Product.pylint, enabled); - }); - } - } - - async function testLinterMessages(fixture: TestFixture, product: Product) { - const messagesToBeReceived = fixture.setDefaultMessages(product); - const linter = await fixture.getEnabledLinter(product); - - const messages = await linter.lint( - fixture.makeDocument(product, 'spam.py'), - new CancellationTokenSource().token, - ); - - if (messagesToBeReceived.length === 0) { - assert.strictEqual(messages.length, 0, `No errors in linter, Output - ${fixture.output}`); - } else if (fixture.output.indexOf('ENOENT') === -1) { - // Pylint for Python Version 2.7 could return 80 linter messages, where as in 3.5 it might only return 1. - // Looks like pylint stops linting as soon as it comes across any ERRORS. - assert.notStrictEqual(messages.length, 0, `No errors in linter, Output - ${fixture.output}`); - } - } - for (const product of LINTERID_BY_PRODUCT.keys()) { - test(`Check ${getProductName(product)} messages`, async function () { - // TODO: Add coverage for these linters. - if ([Product.bandit, Product.mypy, Product.pylama, Product.prospector].some((p) => p === product)) { - this.skip(); - } - - const fixture = new TestFixture(); - await testLinterMessages(fixture, product); - }); - } - - async function testLinterMessageCount(fixture: TestFixture, product: Product, messageCountToBeReceived: number) { - fixture.setDefaultMessages(product); - const linter = await fixture.getEnabledLinter(product); - - const messages = await linter.lint( - fixture.makeDocument(product, 'spam.py'), - new CancellationTokenSource().token, - ); - - assert.strictEqual( - messages.length, - messageCountToBeReceived, - `Expected number of lint errors does not match lint error count, Output - ${fixture.output}`, - ); - } - test('Three line output counted as one message (Pylint)', async () => { - const maxErrors = 5; - const fixture = new TestFixture(); - fixture.lintingSettings.maxNumberOfProblems = maxErrors; - - await testLinterMessageCount(fixture, Product.pylint, maxErrors); - }); -}); - -suite('Linting Products', () => { - const prodService = new ProductService(); - - test('All linting products are represented by linters', async () => { - const products = Object.keys(Product) - .filter((item) => Number.isNaN(Number(item))) - .map((key) => Product[Number(key)]); - - products.forEach((p) => { - const product = (p as unknown) as Product; - if (prodService.getProductType(product) === ProductType.Linter) { - const found = LINTERID_BY_PRODUCT.get(product); - assert.notStrictEqual(found, undefined, `did find linter ${Product[product]}`); - } - }); - }); - - test('All linters match linting products', async () => { - for (const product of LINTERID_BY_PRODUCT.keys()) { - const prodType = prodService.getProductType(product); - assert.notStrictEqual(prodType, undefined, `${Product[product]} is not not properly registered`); - assert.strictEqual(prodType, ProductType.Linter, `${Product[product]} is not a linter product`); - } - }); - - test('All linting product names match linter IDs', async () => { - for (const [product, linterID] of LINTERID_BY_PRODUCT) { - const prodName = ProductNames.get(product); - assert.strictEqual(prodName, linterID, 'product name does not match linter ID'); - } - }); -}); diff --git a/src/test/linters/lintengine.test.ts b/src/test/linters/lintengine.test.ts deleted file mode 100644 index 1bf77c502af5..000000000000 --- a/src/test/linters/lintengine.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as TypeMoq from 'typemoq'; -import { TextDocument, Uri } from 'vscode'; -import { IDocumentManager, IWorkspaceService } from '../../client/common/application/types'; -import { PYTHON_LANGUAGE } from '../../client/common/constants'; -import '../../client/common/extensions'; -import { IFileSystem } from '../../client/common/platform/types'; -import { IConfigurationService, ILintingSettings, ILogOutputChannel, IPythonSettings } from '../../client/common/types'; -import { IInterpreterService } from '../../client/interpreter/contracts'; -import { IServiceContainer } from '../../client/ioc/types'; -import { LintingEngine } from '../../client/linters/lintingEngine'; -import { ILinterManager, ILintingEngine } from '../../client/linters/types'; -import { PythonEnvironment } from '../../client/pythonEnvironments/info'; -import { initialize } from '../initialize'; - -suite('Linting - LintingEngine', () => { - let serviceContainer: TypeMoq.IMock; - let lintManager: TypeMoq.IMock; - let settings: TypeMoq.IMock; - let lintSettings: TypeMoq.IMock; - let fileSystem: TypeMoq.IMock; - let lintingEngine: ILintingEngine; - - suiteSetup(initialize); - setup(async () => { - serviceContainer = TypeMoq.Mock.ofType(); - - const docManager = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IDocumentManager), TypeMoq.It.isAny())) - .returns(() => docManager.object); - - const workspaceService = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService), TypeMoq.It.isAny())) - .returns(() => workspaceService.object); - - fileSystem = TypeMoq.Mock.ofType(); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IFileSystem), TypeMoq.It.isAny())) - .returns(() => fileSystem.object); - - lintSettings = TypeMoq.Mock.ofType(); - settings = TypeMoq.Mock.ofType(); - - const configService = TypeMoq.Mock.ofType(); - configService.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - configService.setup((x) => x.isTestExecution()).returns(() => true); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService), TypeMoq.It.isAny())) - .returns(() => configService.object); - - const outputChannel = TypeMoq.Mock.ofType(); - serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ILogOutputChannel))).returns(() => outputChannel.object); - - lintManager = TypeMoq.Mock.ofType(); - lintManager.setup((x) => x.isLintingEnabled(TypeMoq.It.isAny())).returns(async () => true); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(ILinterManager), TypeMoq.It.isAny())) - .returns(() => lintManager.object); - - lintingEngine = new LintingEngine(serviceContainer.object); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(ILintingEngine), TypeMoq.It.isAny())) - .returns(() => lintingEngine); - - const interpreterService = TypeMoq.Mock.ofType(); - interpreterService - .setup((i) => i.getActiveInterpreter(TypeMoq.It.isAny())) - .returns(() => Promise.resolve(({ path: 'ps' } as unknown) as PythonEnvironment)); - serviceContainer.setup((c) => c.get(IInterpreterService)).returns(() => interpreterService.object); - }); - - test('Ensure document.uri is passed into isLintingEnabled', () => { - const doc = mockTextDocument('a.py', PYTHON_LANGUAGE, true); - try { - lintingEngine.lintDocument(doc, 'auto').ignoreErrors(); - } catch { - lintManager.verify((l) => l.isLintingEnabled(TypeMoq.It.isValue(doc.uri)), TypeMoq.Times.once()); - } - }); - test('Ensure document.uri is passed into createLinter', () => { - const doc = mockTextDocument('a.py', PYTHON_LANGUAGE, true); - try { - lintingEngine.lintDocument(doc, 'auto').ignoreErrors(); - } catch { - lintManager.verify( - (l) => - l.createLinter( - TypeMoq.It.isAny(), - - TypeMoq.It.isAny(), - TypeMoq.It.isValue(doc.uri), - ), - TypeMoq.Times.atLeastOnce(), - ); - } - }); - - test('Verify files that match ignore pattern are not linted', async () => { - const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, true, ['a*.py']); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - - test('Ensure non-Python files are not linted', async () => { - const doc = mockTextDocument('a.ts', 'typescript', true); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - - test('Ensure files with git scheme are not linted', async () => { - const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'git'); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - test('Ensure files with showModifications scheme are not linted', async () => { - const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'showModifications'); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - test('Ensure files with svn scheme are not linted', async () => { - const doc = mockTextDocument('a1.py', PYTHON_LANGUAGE, false, [], 'svn'); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - - test('Ensure non-existing files are not linted', async () => { - const doc = mockTextDocument('file.py', PYTHON_LANGUAGE, false, []); - await lintingEngine.lintDocument(doc, 'auto'); - lintManager.verify( - (l) => l.createLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), - TypeMoq.Times.never(), - ); - }); - - function mockTextDocument( - fileName: string, - language: string, - exists: boolean, - ignorePattern: string[] = [], - scheme?: string, - ): TextDocument { - fileSystem.setup((x) => x.fileExists(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(exists)); - - lintSettings.setup((l) => l.ignorePatterns).returns(() => ignorePattern); - settings.setup((x) => x.linting).returns(() => lintSettings.object); - - const doc = TypeMoq.Mock.ofType(); - if (scheme) { - doc.setup((d) => d.uri).returns(() => Uri.parse(`${scheme}:${fileName}`)); - } else { - doc.setup((d) => d.uri).returns(() => Uri.file(fileName)); - } - doc.setup((d) => d.fileName).returns(() => fileName); - doc.setup((d) => d.languageId).returns(() => language); - return doc.object; - } -}); diff --git a/src/test/linters/linterManager.unit.test.ts b/src/test/linters/linterManager.unit.test.ts deleted file mode 100644 index 42feb642ce8c..000000000000 --- a/src/test/linters/linterManager.unit.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import * as assert from 'assert'; -import { expect } from 'chai'; -import { anything, instance, mock, verify, when } from 'ts-mockito'; -import { Uri } from 'vscode'; -import { ApplicationShell } from '../../client/common/application/applicationShell'; -import { CommandManager } from '../../client/common/application/commandManager'; -import { DocumentManager } from '../../client/common/application/documentManager'; -import { - IApplicationShell, - ICommandManager, - IDocumentManager, - IWorkspaceService, -} from '../../client/common/application/types'; -import { WorkspaceService } from '../../client/common/application/workspace'; -import { ConfigurationService } from '../../client/common/configuration/service'; -import { ProductNames } from '../../client/common/installer/productNames'; -import { ProductService } from '../../client/common/installer/productService'; -import { IConfigurationService, Product, ProductType } from '../../client/common/types'; -import { getNamesAndValues } from '../../client/common/utils/enum'; -import { ServiceContainer } from '../../client/ioc/container'; -import { LinterInfo } from '../../client/linters/linterInfo'; -import { LinterManager } from '../../client/linters/linterManager'; -import { LintingEngine } from '../../client/linters/lintingEngine'; -import { ILinterInfo, ILintingEngine } from '../../client/linters/types'; - -suite('Linting - Linter Manager', () => { - let linterManager: LinterManagerTest; - let shell: IApplicationShell; - let docManager: IDocumentManager; - let cmdManager: ICommandManager; - let lintingEngine: ILintingEngine; - let configService: IConfigurationService; - let workspaceService: IWorkspaceService; - class LinterManagerTest extends LinterManager { - // Override base class property to make it public. - public linters!: ILinterInfo[]; - } - setup(() => { - const svcContainer = mock(ServiceContainer); - shell = mock(ApplicationShell); - docManager = mock(DocumentManager); - cmdManager = mock(CommandManager); - lintingEngine = mock(LintingEngine); - configService = mock(ConfigurationService); - workspaceService = mock(WorkspaceService); - when(svcContainer.get(IApplicationShell)).thenReturn(instance(shell)); - when(svcContainer.get(IDocumentManager)).thenReturn(instance(docManager)); - when(svcContainer.get(ICommandManager)).thenReturn(instance(cmdManager)); - when(svcContainer.get(ILintingEngine)).thenReturn(instance(lintingEngine)); - when(svcContainer.get(IConfigurationService)).thenReturn(instance(configService)); - when(svcContainer.get(IWorkspaceService)).thenReturn(instance(workspaceService)); - linterManager = new LinterManagerTest(instance(configService)); - }); - - test('Get all linters will return a list of all linters', () => { - const linters = linterManager.getAllLinterInfos(); - - expect(linters).to.be.lengthOf(8); - - const productService = new ProductService(); - const linterProducts = getNamesAndValues(Product) - .filter((product) => productService.getProductType(product.value) === ProductType.Linter) - .map((item) => ProductNames.get(item.value)); - expect(linters.map((item) => item.id).sort()).to.be.deep.equal(linterProducts.sort()); - }); - - test('Get linter info for non-linter product should throw an exception', () => { - const productService = new ProductService(); - getNamesAndValues(Product).forEach((prod) => { - if (productService.getProductType(prod.value) === ProductType.Linter) { - const info = linterManager.getLinterInfo(prod.value); - expect(info.id).to.equal(ProductNames.get(prod.value)); - expect(info).not.to.be.equal(undefined, 'should not be unedfined'); - } else { - expect(() => linterManager.getLinterInfo(prod.value)).to.throw(); - } - }); - }); - test('Pylint configuration file watch', async () => { - const pylint = linterManager.getLinterInfo(Product.pylint); - assert.strictEqual(pylint.configFileNames.length, 2, 'Pylint configuration file count is incorrect.'); - assert.notStrictEqual( - pylint.configFileNames.indexOf('pylintrc'), - -1, - 'Pylint configuration files miss pylintrc.', - ); - assert.notStrictEqual( - pylint.configFileNames.indexOf('.pylintrc'), - -1, - 'Pylint configuration files miss .pylintrc.', - ); - }); - - [undefined, Uri.parse('something')].forEach((resource) => { - const testResourceSuffix = `(${resource ? 'with a resource' : 'without a resource'})`; - [true, false].forEach((enabled) => { - const testSuffix = `(${enabled ? 'enable' : 'disable'}) & ${testResourceSuffix}`; - test(`Enable linting should update config ${testSuffix}`, async () => { - when(configService.updateSetting('linting.enabled', enabled, resource)).thenResolve(); - - await linterManager.enableLintingAsync(enabled, resource); - - verify(configService.updateSetting('linting.enabled', enabled, resource)).once(); - }); - }); - test(`getActiveLinters will check if linter is enabled and in silent mode ${testResourceSuffix}`, async () => { - const linterInfo = mock(LinterInfo); - const instanceOfLinterInfo = instance(linterInfo); - linterManager.linters = [instanceOfLinterInfo]; - when(linterInfo.isEnabled(resource)).thenReturn(true); - - const linters = await linterManager.getActiveLinters(resource); - - verify(linterInfo.isEnabled(resource)).once(); - expect(linters[0]).to.deep.equal(instanceOfLinterInfo); - }); - - test(`setActiveLintersAsync with invalid products does nothing ${testResourceSuffix}`, async () => { - let getActiveLintersInvoked = false; - linterManager.getActiveLinters = async () => { - getActiveLintersInvoked = true; - return []; - }; - - await linterManager.setActiveLintersAsync([Product.pytest], resource); - - expect(getActiveLintersInvoked).to.be.equal(false, 'Should not be invoked'); - }); - test(`setActiveLintersAsync with single product will disable it then enable it ${testResourceSuffix}`, async () => { - const linterInfo = mock(LinterInfo); - const instanceOfLinterInfo = instance(linterInfo); - linterManager.linters = [instanceOfLinterInfo]; - when(linterInfo.product).thenReturn(Product.flake8); - when(linterInfo.enableAsync(false, resource)).thenResolve(); - linterManager.getActiveLinters = () => Promise.resolve([instanceOfLinterInfo]); - linterManager.enableLintingAsync = () => Promise.resolve(); - - await linterManager.setActiveLintersAsync([Product.flake8], resource); - - verify(linterInfo.enableAsync(false, resource)).atLeast(1); - verify(linterInfo.enableAsync(true, resource)).atLeast(1); - }); - test(`setActiveLintersAsync with single product will disable all existing then enable the necessary two ${testResourceSuffix}`, async () => { - const linters = new Map(); - const linterInstances = new Map(); - linterManager.linters = []; - [Product.flake8, Product.mypy, Product.prospector, Product.bandit, Product.pydocstyle].forEach( - (product) => { - const linterInfo = mock(LinterInfo); - const instanceOfLinterInfo = instance(linterInfo); - linterManager.linters.push(instanceOfLinterInfo); - linters.set(product, linterInfo); - linterInstances.set(product, instanceOfLinterInfo); - when(linterInfo.product).thenReturn(product); - when(linterInfo.enableAsync(anything(), resource)).thenResolve(); - }, - ); - - linterManager.getActiveLinters = () => Promise.resolve(Array.from(linterInstances.values())); - linterManager.enableLintingAsync = () => Promise.resolve(); - - const lintersToEnable = [Product.flake8, Product.mypy, Product.pydocstyle]; - await linterManager.setActiveLintersAsync([Product.flake8, Product.mypy, Product.pydocstyle], resource); - - linters.forEach((item, product) => { - verify(item.enableAsync(false, resource)).atLeast(1); - if (lintersToEnable.indexOf(product) >= 0) { - verify(item.enableAsync(true, resource)).atLeast(1); - } - }); - }); - }); -}); diff --git a/src/test/linters/mypy.unit.test.ts b/src/test/linters/mypy.unit.test.ts deleted file mode 100644 index b697a719a475..000000000000 --- a/src/test/linters/mypy.unit.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { parseLine } from '../../client/linters/baseLinter'; -import { getRegex } from '../../client/linters/mypy'; -import { ILintMessage, LinterId } from '../../client/linters/types'; - -// This following is a real-world example. See gh=2380. - -const output = ` -provider.pyi:10: error: Incompatible types in assignment (expression has type "str", variable has type "int") -provider.pyi:11: error: Name 'not_declared_var' is not defined -provider.pyi:12:21: error: Expression has type "Any" -`; - -suite('Linting - MyPy', () => { - test('regex', async () => { - const lines = output.split('\n'); - const tests: [string, ILintMessage][] = [ - [ - lines[1], - { - code: undefined, - message: 'Incompatible types in assignment (expression has type "str", variable has type "int")', - column: 0, - line: 10, - type: 'error', - provider: 'mypy', - } as ILintMessage, - ], - [ - lines[2], - { - code: undefined, - message: "Name 'not_declared_var' is not defined", - column: 0, - line: 11, - type: 'error', - provider: 'mypy', - } as ILintMessage, - ], - [ - lines[3], - { - code: undefined, - message: 'Expression has type "Any"', - column: 20, - line: 12, - type: 'error', - provider: 'mypy', - } as ILintMessage, - ], - ]; - for (const [line, expected] of tests) { - const msg = parseLine(line, getRegex('provider.pyi'), LinterId.MyPy, 1); - - expect(msg).to.deep.equal(expected); - } - }); - test('regex excludes unexpected files', () => { - // mypy run against `foo/bar.py` returning errors for foo/__init__.py - const outputWithUnexpectedFile = `\ -foo/__init__.py:4:5: error: Statement is unreachable [unreachable] -foo/bar.py:2:14: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] -Found 2 errors in 2 files (checked 1 source file) -`; - - const lines = outputWithUnexpectedFile.split('\n'); - const tests: [string, ILintMessage | undefined][] = [ - [lines[0], undefined], - [ - lines[1], - { - code: undefined, - message: - 'Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]', - column: 13, - line: 2, - type: 'error', - provider: 'mypy', - }, - ], - [lines[2], undefined], - ]; - for (const [line, expected] of tests) { - const msg = parseLine(line, getRegex('foo/bar.py'), LinterId.MyPy, 1); - - expect(msg).to.deep.equal(expected); - } - }); - test('getRegex escapes filename correctly', () => { - expect(getRegex('foo/bar.py')).to.eql( - String.raw`foo/bar\.py:(?\d+)(:(?\d+))?: (?\w+): (?.*)\r?(\n|$)`, - ); - }); -}); diff --git a/src/test/linters/prompts/flake8Prompt.unit.test.ts b/src/test/linters/prompts/flake8Prompt.unit.test.ts deleted file mode 100644 index 7bbe52ae6d96..000000000000 --- a/src/test/linters/prompts/flake8Prompt.unit.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { assert } from 'chai'; -import * as sinon from 'sinon'; -import * as TypeMoq from 'typemoq'; -import { IApplicationEnvironment } from '../../../client/common/application/types'; -import { IPersistentState } from '../../../client/common/types'; -import { Common, ToolsExtensions } from '../../../client/common/utils/localize'; -import * as commandApis from '../../../client/common/vscodeApis/commandApis'; -import * as windowsApis from '../../../client/common/vscodeApis/windowApis'; -import { IServiceContainer } from '../../../client/ioc/types'; -import * as promptCommons from '../../../client/linters/prompts/common'; -import { Flake8ExtensionPrompt, FLAKE8_EXTENSION } from '../../../client/linters/prompts/flake8Prompt'; -import { IToolsExtensionPrompt } from '../../../client/linters/prompts/types'; - -suite('Flake8 Extension prompt tests', () => { - let isExtensionEnabledStub: sinon.SinonStub; - let isExtensionDisabledStub: sinon.SinonStub; - let doNotShowPromptStateStub: sinon.SinonStub; - let inToolsExtensionsExperimentStub: sinon.SinonStub; - let showInformationMessageStub: sinon.SinonStub; - let executeCommandStub: sinon.SinonStub; - let serviceContainer: TypeMoq.IMock; - let doNotState: TypeMoq.IMock>; - let appEnv: TypeMoq.IMock; - let prompt: IToolsExtensionPrompt; - - setup(() => { - isExtensionEnabledStub = sinon.stub(promptCommons, 'isExtensionEnabled'); - isExtensionDisabledStub = sinon.stub(promptCommons, 'isExtensionDisabled'); - doNotShowPromptStateStub = sinon.stub(promptCommons, 'doNotShowPromptState'); - inToolsExtensionsExperimentStub = sinon.stub(promptCommons, 'inToolsExtensionsExperiment'); - showInformationMessageStub = sinon.stub(windowsApis, 'showInformationMessage'); - executeCommandStub = sinon.stub(commandApis, 'executeCommand'); - - appEnv = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(IApplicationEnvironment)) - .returns(() => appEnv.object); - - doNotState = TypeMoq.Mock.ofType>(); - prompt = new Flake8ExtensionPrompt(serviceContainer.object); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Extension already installed and enabled', async () => { - isExtensionEnabledStub.returns(true); - - assert.isTrue(await prompt.showPrompt()); - }); - - test('Extension already installed, but disabled', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(true); - - assert.isTrue(await prompt.showPrompt()); - }); - - test('Test do not show again persistent state', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => true); - doNotShowPromptStateStub.returns(doNotState.object); - - assert.isFalse(await prompt.showPrompt()); - }); - - test('User not in experiment', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(false); - - assert.isFalse(await prompt.showPrompt()); - }); - - test('User selected: install extension (insiders)', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - appEnv.setup((a) => a.extensionChannel).returns(() => 'insiders'); - executeCommandStub.resolves(undefined); - - showInformationMessageStub.resolves(ToolsExtensions.installFlake8Extension); - assert.isTrue(await prompt.showPrompt()); - - executeCommandStub.calledOnceWith('workbench.extensions.installExtension', FLAKE8_EXTENSION, { - installPreReleaseVersion: true, - }); - }); - - test('User selected: install extension (stable)', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - appEnv.setup((a) => a.extensionChannel).returns(() => 'stable'); - executeCommandStub.resolves(undefined); - - showInformationMessageStub.resolves(ToolsExtensions.installFlake8Extension); - assert.isTrue(await prompt.showPrompt()); - - executeCommandStub.calledOnceWith('workbench.extensions.installExtension', FLAKE8_EXTENSION, { - installPreReleaseVersion: false, - }); - }); - - test('User selected: do not show again', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - doNotState - .setup((d) => d.updateValue(true)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - showInformationMessageStub.resolves(Common.doNotShowAgain); - assert.isFalse(await prompt.showPrompt()); - - doNotState.verifyAll(); - }); - - test('User selected: close', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - showInformationMessageStub.resolves(undefined); - assert.isFalse(await prompt.showPrompt()); - }); -}); diff --git a/src/test/linters/prompts/pylintPrompt.unit.test.ts b/src/test/linters/prompts/pylintPrompt.unit.test.ts deleted file mode 100644 index 65b579f258af..000000000000 --- a/src/test/linters/prompts/pylintPrompt.unit.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { assert } from 'chai'; -import * as sinon from 'sinon'; -import * as TypeMoq from 'typemoq'; -import { IApplicationEnvironment } from '../../../client/common/application/types'; -import { IPersistentState } from '../../../client/common/types'; -import { Common, ToolsExtensions } from '../../../client/common/utils/localize'; -import * as commandApis from '../../../client/common/vscodeApis/commandApis'; -import * as windowsApis from '../../../client/common/vscodeApis/windowApis'; -import { IServiceContainer } from '../../../client/ioc/types'; -import * as promptCommons from '../../../client/linters/prompts/common'; -import { PylintExtensionPrompt, PYLINT_EXTENSION } from '../../../client/linters/prompts/pylintPrompt'; -import { IToolsExtensionPrompt } from '../../../client/linters/prompts/types'; - -suite('Pylint Extension prompt tests', () => { - let isExtensionEnabledStub: sinon.SinonStub; - let isExtensionDisabledStub: sinon.SinonStub; - let doNotShowPromptStateStub: sinon.SinonStub; - let inToolsExtensionsExperimentStub: sinon.SinonStub; - let showInformationMessageStub: sinon.SinonStub; - let executeCommandStub: sinon.SinonStub; - let serviceContainer: TypeMoq.IMock; - let doNotState: TypeMoq.IMock>; - let appEnv: TypeMoq.IMock; - let prompt: IToolsExtensionPrompt; - - setup(() => { - isExtensionEnabledStub = sinon.stub(promptCommons, 'isExtensionEnabled'); - isExtensionDisabledStub = sinon.stub(promptCommons, 'isExtensionDisabled'); - doNotShowPromptStateStub = sinon.stub(promptCommons, 'doNotShowPromptState'); - inToolsExtensionsExperimentStub = sinon.stub(promptCommons, 'inToolsExtensionsExperiment'); - showInformationMessageStub = sinon.stub(windowsApis, 'showInformationMessage'); - executeCommandStub = sinon.stub(commandApis, 'executeCommand'); - - appEnv = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(IApplicationEnvironment)) - .returns(() => appEnv.object); - - doNotState = TypeMoq.Mock.ofType>(); - prompt = new PylintExtensionPrompt(serviceContainer.object); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Extension already installed and enabled', async () => { - isExtensionEnabledStub.returns(true); - - assert.isTrue(await prompt.showPrompt()); - }); - - test('Extension already installed, but disabled', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(true); - - assert.isTrue(await prompt.showPrompt()); - }); - - test('User not in experiment', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(false); - - assert.isFalse(await prompt.showPrompt()); - }); - - test('User selected: install extension (insiders)', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - appEnv.setup((a) => a.extensionChannel).returns(() => 'insiders'); - executeCommandStub.resolves(undefined); - - showInformationMessageStub.resolves(ToolsExtensions.installPylintExtension); - assert.isTrue(await prompt.showPrompt()); - - executeCommandStub.calledOnceWith('workbench.extensions.installExtension', PYLINT_EXTENSION, { - installPreReleaseVersion: true, - }); - }); - - test('User selected: install extension (stable)', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - appEnv.setup((a) => a.extensionChannel).returns(() => 'stable'); - executeCommandStub.resolves(undefined); - - showInformationMessageStub.resolves(ToolsExtensions.installPylintExtension); - assert.isTrue(await prompt.showPrompt()); - - executeCommandStub.calledOnceWith('workbench.extensions.installExtension', PYLINT_EXTENSION, { - installPreReleaseVersion: false, - }); - }); - - test('User selected: do not show again', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - doNotState - .setup((d) => d.updateValue(true)) - .returns(() => Promise.resolve()) - .verifiable(TypeMoq.Times.once()); - showInformationMessageStub.resolves(Common.doNotShowAgain); - assert.isFalse(await prompt.showPrompt()); - - doNotState.verifyAll(); - }); - - test('User selected: close', async () => { - isExtensionEnabledStub.returns(false); - isExtensionDisabledStub.returns(false); - - doNotState.setup((d) => d.value).returns(() => false); - doNotShowPromptStateStub.returns(doNotState.object); - inToolsExtensionsExperimentStub.resolves(true); - - showInformationMessageStub.resolves(undefined); - assert.isFalse(await prompt.showPrompt()); - }); -}); diff --git a/src/test/linters/pylint.test.ts b/src/test/linters/pylint.test.ts deleted file mode 100644 index e1cec249c662..000000000000 --- a/src/test/linters/pylint.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { expect } from 'chai'; -import { Container } from 'inversify'; -import * as os from 'os'; -import * as path from 'path'; -import * as TypeMoq from 'typemoq'; -import { - CancellationTokenSource, - DiagnosticSeverity, - TextDocument, - Uri, - WorkspaceConfiguration, - WorkspaceFolder, -} from 'vscode'; -import { LanguageServerType } from '../../client/activation/types'; -import { IWorkspaceService } from '../../client/common/application/types'; -import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { IPythonToolExecutionService } from '../../client/common/process/types'; -import { IConfigurationService, IExtensions, IInstaller, IPythonSettings } from '../../client/common/types'; -import { - IInterpreterAutoSelectionService, - IInterpreterAutoSelectionProxyService, -} from '../../client/interpreter/autoSelection/types'; -import { ServiceContainer } from '../../client/ioc/container'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { LinterManager } from '../../client/linters/linterManager'; -import { Pylint } from '../../client/linters/pylint'; -import { ILinterManager } from '../../client/linters/types'; -import { MockLintingSettings } from '../mockClasses'; -import { MockAutoSelectionService } from '../mocks/autoSelector'; - -suite('Linting - Pylint', () => { - let fileSystem: TypeMoq.IMock; - let platformService: TypeMoq.IMock; - let workspace: TypeMoq.IMock; - let execService: TypeMoq.IMock; - let config: TypeMoq.IMock; - let workspaceConfig: TypeMoq.IMock; - let pythonSettings: TypeMoq.IMock; - let serviceContainer: ServiceContainer; - let extensionsService: TypeMoq.IMock; - - setup(() => { - fileSystem = TypeMoq.Mock.ofType(); - fileSystem - .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) - .returns((a, b) => a === b); - - platformService = TypeMoq.Mock.ofType(); - platformService.setup((x) => x.isWindows).returns(() => false); - - extensionsService = TypeMoq.Mock.ofType(); - extensionsService.setup((e) => e.getExtension(TypeMoq.It.isAny())).returns(() => undefined); - - workspace = TypeMoq.Mock.ofType(); - execService = TypeMoq.Mock.ofType(); - - const cont = new Container(); - const serviceManager = new ServiceManager(cont); - serviceContainer = new ServiceContainer(cont); - - serviceManager.addSingletonInstance(IFileSystem, fileSystem.object); - serviceManager.addSingletonInstance(IWorkspaceService, workspace.object); - serviceManager.addSingletonInstance( - IPythonToolExecutionService, - execService.object, - ); - serviceManager.addSingletonInstance(IPlatformService, platformService.object); - serviceManager.addSingleton( - IInterpreterAutoSelectionService, - MockAutoSelectionService, - ); - serviceManager.addSingleton( - IInterpreterAutoSelectionProxyService, - MockAutoSelectionService, - ); - serviceManager.addSingletonInstance(IExtensions, extensionsService.object); - - pythonSettings = TypeMoq.Mock.ofType(); - pythonSettings.setup((p) => p.languageServer).returns(() => LanguageServerType.Jedi); - - config = TypeMoq.Mock.ofType(); - config.setup((c) => c.getSettings()).returns(() => pythonSettings.object); - - workspaceConfig = TypeMoq.Mock.ofType(); - workspace.setup((w) => w.getConfiguration('python')).returns(() => workspaceConfig.object); - - serviceManager.addSingletonInstance(IConfigurationService, config.object); - const linterManager = new LinterManager(config.object); - serviceManager.addSingletonInstance(ILinterManager, linterManager); - const installer = TypeMoq.Mock.ofType(); - serviceManager.addSingletonInstance(IInstaller, installer.object); - }); - - test('Negative column numbers should be treated 0', async () => { - const fileFolder = '/user/a/b/c'; - const pylinter = new Pylint(serviceContainer, { showPrompt: () => Promise.resolve(false) }); - - const document = TypeMoq.Mock.ofType(); - document.setup((x) => x.uri).returns(() => Uri.file(path.join(fileFolder, 'test.py'))); - - const wsf = TypeMoq.Mock.ofType(); - wsf.setup((x) => x.uri).returns(() => Uri.file(fileFolder)); - - workspace.setup((x) => x.getWorkspaceFolder(TypeMoq.It.isAny())).returns(() => wsf.object); - - const linterOutput = [ - '[', - ' {', - ' "type": "convention",', - ' "module": "test",', - ' "obj": "",', - ' "line": 1,', - ' "column": 1,', - ` "path": "${fileFolder}/test.py",`, - ' "symbol": "missing-module-docstring",', - ' "message": "Missing module docstring",', - ' "message-id": "C0114",', - ' "endLine": null,', - ' "endColumn": null', - ' },', - ' {', - ' "type": "error",', - ' "module": "test",', - ' "obj": "",', - ' "line": 3,', - ' "column": -1,', - ` "path": "${fileFolder}/test.py",`, - ' "symbol": "too-many-format-args",', - ' "message": "Too many arguments for format string",', - ' "message-id": "E1305"', - ' }', - ']', - ].join(os.EOL); - execService - .setup((x) => x.execForLinter(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) - .returns(() => Promise.resolve({ stdout: linterOutput, stderr: '' })); - - const lintSettings = new MockLintingSettings(); - lintSettings.maxNumberOfProblems = 1000; - lintSettings.pylintPath = 'pyLint'; - lintSettings.pylintEnabled = true; - lintSettings.pylintCategorySeverity = { - convention: DiagnosticSeverity.Hint, - error: DiagnosticSeverity.Error, - fatal: DiagnosticSeverity.Error, - refactor: DiagnosticSeverity.Hint, - warning: DiagnosticSeverity.Warning, - }; - - const settings = TypeMoq.Mock.ofType(); - settings.setup((x) => x.linting).returns(() => lintSettings); - settings.setup((x) => x.languageServer).returns(() => LanguageServerType.Jedi); - config.setup((x) => x.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); - - const messages = await pylinter.lint(document.object, new CancellationTokenSource().token); - expect(messages).to.be.lengthOf(2); - expect(messages[0].column).to.be.equal(1); - expect(messages[1].column).to.be.equal(0); - }); -}); diff --git a/src/test/linters/pylint.unit.test.ts b/src/test/linters/pylint.unit.test.ts deleted file mode 100644 index ee6954e870a5..000000000000 --- a/src/test/linters/pylint.unit.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { assert } from 'chai'; -import * as sinon from 'sinon'; -import { mock } from 'ts-mockito'; -import * as TypeMoq from 'typemoq'; -import * as vscode from 'vscode'; -import { IWorkspaceService } from '../../client/common/application/types'; -import { IFileSystem, IPlatformService } from '../../client/common/platform/types'; -import { IConfigurationService, IExtensions, IPythonSettings } from '../../client/common/types'; -import { IServiceContainer } from '../../client/ioc/types'; -import { IToolsExtensionPrompt } from '../../client/linters/prompts/types'; -import { Pylint } from '../../client/linters/pylint'; -import { ILinterInfo, ILinterManager, ILintMessage, LinterId, LintMessageSeverity } from '../../client/linters/types'; - -suite('Pylint - Function runLinter()', () => { - let fileSystem: TypeMoq.IMock; - let serviceContainer: TypeMoq.IMock; - let workspaceService: TypeMoq.IMock; - let configService: TypeMoq.IMock; - let manager: TypeMoq.IMock; - let _info: TypeMoq.IMock; - let platformService: TypeMoq.IMock; - let extensionsService: TypeMoq.IMock; - let run: sinon.SinonStub; - let parseMessagesSeverity: sinon.SinonStub; - let extensionPrompt: TypeMoq.IMock; - const doc = { - uri: vscode.Uri.file('path/to/doc'), - }; - const args = [doc.uri.fsPath]; - class PylintTest extends Pylint { - // eslint-disable-next-line class-methods-use-this - public async run( - _args: string[], - _document: vscode.TextDocument, - _cancellation: vscode.CancellationToken, - _regEx: string, - ): Promise { - return []; - } - - // eslint-disable-next-line class-methods-use-this - public parseMessagesSeverity(_error: string, _categorySeverity: unknown): LintMessageSeverity { - return ('Severity' as unknown) as LintMessageSeverity; - } - - // eslint-disable-next-line class-methods-use-this - public get info(): ILinterInfo { - return _info.object; - } - - public async runLinter( - document: vscode.TextDocument, - cancellation: vscode.CancellationToken, - ): Promise { - return super.runLinter(document, cancellation); - } - - // eslint-disable-next-line class-methods-use-this - public getWorkingDirectoryPath(_document: vscode.TextDocument): string { - return 'path/to/workspaceRoot'; - } - - public async parseMessages( - output: string, - _document: vscode.TextDocument, - _token: vscode.CancellationToken, - ): Promise { - return super.parseMessages(output, _document, _token, ''); - } - } - - setup(() => { - platformService = TypeMoq.Mock.ofType(); - _info = TypeMoq.Mock.ofType(); - serviceContainer = TypeMoq.Mock.ofType(); - workspaceService = TypeMoq.Mock.ofType(); - configService = TypeMoq.Mock.ofType(); - extensionsService = TypeMoq.Mock.ofType(); - manager = TypeMoq.Mock.ofType(); - serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ILinterManager))).returns(() => manager.object); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IConfigurationService))) - .returns(() => configService.object); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IWorkspaceService))) - .returns(() => workspaceService.object); - serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IFileSystem))).returns(() => fileSystem.object); - serviceContainer - .setup((c) => c.get(TypeMoq.It.isValue(IPlatformService))) - .returns(() => platformService.object); - serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(IExtensions))).returns(() => extensionsService.object); - fileSystem = TypeMoq.Mock.ofType(); - fileSystem - .setup((x) => x.arePathsSame(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())) - .returns((a, b) => a === b); - manager.setup((m) => m.getLinterInfo(TypeMoq.It.isAny())).returns(() => (undefined as unknown) as ILinterInfo); - _info.setup((x) => x.id).returns(() => LinterId.PyLint); - extensionPrompt = TypeMoq.Mock.ofType(); - extensionPrompt.setup((e) => e.showPrompt()).returns(() => Promise.resolve(false)); - }); - - teardown(() => { - sinon.restore(); - }); - - test('Test pylint with default settings.', async () => { - const settings = { - linting: { - pylintEnabled: true, - }, - }; - configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as IPythonSettings); - _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); - run = sinon.stub(PylintTest.prototype, 'run'); - run.callsFake(() => Promise.resolve([])); - parseMessagesSeverity = sinon.stub(PylintTest.prototype, 'parseMessagesSeverity'); - parseMessagesSeverity.callsFake(() => 'Severity'); - const pylint = new PylintTest(serviceContainer.object, extensionPrompt.object); - await pylint.runLinter(doc as vscode.TextDocument, mock(vscode.CancellationTokenSource).token); - assert.deepEqual(run.args[0][0], args); - assert.ok(parseMessagesSeverity.notCalled); - assert.ok(run.calledOnce); - }); - - test('Message returned by runLinter() is as expected', async () => { - const message = [ - { - type: 'messageType', - }, - ]; - const expectedResult = [ - { - type: 'messageType', - severity: 'LintMessageSeverity', - }, - ]; - const settings = { - linting: { - pylintEnabled: true, - }, - }; - configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as IPythonSettings); - _info.setup((info) => info.linterArgs(doc.uri)).returns(() => []); - run = sinon.stub(PylintTest.prototype, 'run'); - run.callsFake(() => Promise.resolve(message)); - parseMessagesSeverity = sinon.stub(PylintTest.prototype, 'parseMessagesSeverity'); - parseMessagesSeverity.callsFake(() => 'LintMessageSeverity'); - const pylint = new PylintTest(serviceContainer.object, extensionPrompt.object); - const result = await pylint.runLinter(doc as vscode.TextDocument, mock(vscode.CancellationTokenSource).token); - assert.deepEqual(result, (expectedResult as unknown) as ILintMessage[]); - assert.ok(parseMessagesSeverity.calledOnce); - assert.ok(run.calledOnce); - }); - - test('Parse json output', async () => { - // If 'endLine' and 'endColumn' are missing in JSON output, - // both should be set to 'undefined' - const jsonOutput = `[ - { - "type": "error", - "module": "file", - "obj": "Foo.meth3", - "line": 26, - "column": 15, - "path": "file.py", - "symbol": "no-member", - "message": "Instance of 'Foo' has no 'blop' member", - "message-id": "E1101" - } -]`; - const expectedMessages: ILintMessage[] = [ - { - code: 'no-member', - message: "Instance of 'Foo' has no 'blop' member", - column: 15, - line: 26, - type: 'error', - provider: LinterId.PyLint, - endLine: undefined, - endColumn: undefined, - }, - ]; - const settings = { - linting: { - pylintEnabled: true, - }, - }; - configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as IPythonSettings); - const pylint = new PylintTest(serviceContainer.object, extensionPrompt.object); - const result = await pylint.parseMessages( - jsonOutput, - doc as vscode.TextDocument, - mock(vscode.CancellationTokenSource).token, - ); - assert.deepEqual(result, expectedMessages); - }); - - test('Parse json output with endLine', async () => { - const jsonOutput = `[ - { - "type": "error", - "module": "file", - "obj": "Foo.meth3", - "line": 26, - "column": 15, - "endLine": 26, - "endColumn": 24, - "path": "file.py", - "symbol": "no-member", - "message": "Instance of 'Foo' has no 'blop' member", - "message-id": "E1101" - } -]`; - const expectedMessages: ILintMessage[] = [ - { - code: 'no-member', - message: "Instance of 'Foo' has no 'blop' member", - column: 15, - line: 26, - type: 'error', - provider: LinterId.PyLint, - endLine: 26, - endColumn: 24, - }, - ]; - const settings = { - linting: { - pylintEnabled: true, - }, - }; - configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as IPythonSettings); - const pylint = new PylintTest(serviceContainer.object, extensionPrompt.object); - const result = await pylint.parseMessages( - jsonOutput, - doc as vscode.TextDocument, - mock(vscode.CancellationTokenSource).token, - ); - assert.deepEqual(result, expectedMessages); - }); - - test('Parse json output with unknown endLine', async () => { - // If 'endLine' and 'endColumn' are present in JSON output - // but 'null', 'endLine' should be set to 'undefined'. - // 'endColumn' defaults to 0. - const jsonOutput = `[ - { - "type": "error", - "module": "file", - "obj": "Foo.meth3", - "line": 26, - "column": 15, - "endLine": null, - "endColumn": null, - "path": "file.py", - "symbol": "no-member", - "message": "Instance of 'Foo' has no 'blop' member", - "message-id": "E1101" - } -]`; - const expectedMessages: ILintMessage[] = [ - { - code: 'no-member', - message: "Instance of 'Foo' has no 'blop' member", - column: 15, - line: 26, - type: 'error', - provider: LinterId.PyLint, - endLine: undefined, - endColumn: undefined, - }, - ]; - const settings = { - linting: { - pylintEnabled: true, - }, - }; - configService.setup((c) => c.getSettings(doc.uri)).returns(() => settings as IPythonSettings); - const pylint = new PylintTest(serviceContainer.object, extensionPrompt.object); - const result = await pylint.parseMessages( - jsonOutput, - doc as vscode.TextDocument, - mock(vscode.CancellationTokenSource).token, - ); - assert.deepEqual(result, expectedMessages); - }); -}); diff --git a/src/test/linters/serviceRegistry.unit.test.ts b/src/test/linters/serviceRegistry.unit.test.ts deleted file mode 100644 index a27c244af344..000000000000 --- a/src/test/linters/serviceRegistry.unit.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { instance, mock, verify } from 'ts-mockito'; -import { IExtensionActivationService } from '../../client/activation/types'; -import { ServiceManager } from '../../client/ioc/serviceManager'; -import { IServiceManager } from '../../client/ioc/types'; -import { LinterManager } from '../../client/linters/linterManager'; -import { LintingEngine } from '../../client/linters/lintingEngine'; -import { registerTypes } from '../../client/linters/serviceRegistry'; -import { ILinterManager, ILintingEngine } from '../../client/linters/types'; -import { LinterProvider } from '../../client/providers/linterProvider'; - -suite('Linters Service Registry', () => { - let serviceManager: IServiceManager; - - setup(() => { - serviceManager = mock(ServiceManager); - }); - - test('Ensure services are registered', async () => { - registerTypes(instance(serviceManager)); - verify(serviceManager.addSingleton(ILintingEngine, LintingEngine)).once(); - verify(serviceManager.addSingleton(ILinterManager, LinterManager)).once(); - verify( - serviceManager.addSingleton(IExtensionActivationService, LinterProvider), - ).once(); - }); -}); diff --git a/src/test/mockClasses.ts b/src/test/mockClasses.ts index c962c4d67ca4..e2de7e649b87 100644 --- a/src/test/mockClasses.ts +++ b/src/test/mockClasses.ts @@ -1,12 +1,5 @@ import * as vscode from 'vscode'; import * as util from 'util'; -import { - Flake8CategorySeverity, - ILintingSettings, - IMypyCategorySeverity, - IPycodestyleCategorySeverity, - IPylintCategorySeverity, -} from '../client/common/types'; export class MockOutputChannel implements vscode.LogOutputChannel { public name: string; @@ -79,39 +72,3 @@ export class MockStatusBarItem implements vscode.StatusBarItem { public dispose(): void {} } - -export class MockLintingSettings implements ILintingSettings { - public enabled!: boolean; - public cwd?: string; - public ignorePatterns!: string[]; - public prospectorEnabled!: boolean; - public prospectorArgs!: string[]; - public pylintEnabled!: boolean; - public pylintArgs!: string[]; - public pycodestyleEnabled!: boolean; - public pycodestyleArgs!: string[]; - public pylamaEnabled!: boolean; - public pylamaArgs!: string[]; - public flake8Enabled!: boolean; - public flake8Args!: string[]; - public pydocstyleEnabled!: boolean; - public pydocstyleArgs!: string[]; - public lintOnSave!: boolean; - public maxNumberOfProblems!: number; - public pylintCategorySeverity!: IPylintCategorySeverity; - public pycodestyleCategorySeverity!: IPycodestyleCategorySeverity; - public flake8CategorySeverity!: Flake8CategorySeverity; - public mypyCategorySeverity!: IMypyCategorySeverity; - public prospectorPath!: string; - public pylintPath!: string; - public pycodestylePath!: string; - public pylamaPath!: string; - public flake8Path!: string; - public pydocstylePath!: string; - public mypyEnabled!: boolean; - public mypyArgs!: string[]; - public mypyPath!: string; - public banditEnabled!: boolean; - public banditArgs!: string[]; - public banditPath!: string; -} diff --git a/src/test/serviceRegistry.ts b/src/test/serviceRegistry.ts index e7b11d2b745b..a175b3303223 100644 --- a/src/test/serviceRegistry.ts +++ b/src/test/serviceRegistry.ts @@ -45,7 +45,6 @@ import { registerInterpreterTypes } from '../client/interpreter/serviceRegistry' import { ServiceContainer } from '../client/ioc/container'; import { ServiceManager } from '../client/ioc/serviceManager'; import { IServiceContainer, IServiceManager } from '../client/ioc/types'; -import { registerTypes as lintersRegisterTypes } from '../client/linters/serviceRegistry'; import { registerTypes as unittestsRegisterTypes } from '../client/testing/serviceRegistry'; import { LegacyFileSystem } from './legacyFileSystem'; import { MockOutputChannel } from './mockClasses'; @@ -142,10 +141,6 @@ export class IocContainer { unittestsRegisterTypes(this.serviceManager); } - public registerLinterTypes(): void { - lintersRegisterTypes(this.serviceManager); - } - public registerPlatformTypes(): void { platformRegisterTypes(this.serviceManager); }