From 4a7f29d90d5069f18f1840efa7a6aa36d02502e7 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 2 Aug 2021 12:18:47 +0200 Subject: [PATCH 1/4] Add extraParams setting for planemo tests This allows to pass additional arguments to `planemo test` as needed --- client/README.md | 47 ++++++++++--------- client/package.json | 6 +++ .../galaxyToolWorkspaceConfiguration.ts | 7 +++ .../configuration/workspaceConfiguration.ts | 1 + client/src/planemo/configuration.ts | 2 + client/src/planemo/testing/testRunner.ts | 13 ++++- 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/client/README.md b/client/README.md index 00011b6..5a0c7eb 100644 --- a/client/README.md +++ b/client/README.md @@ -20,24 +20,24 @@ To support testing your tools using `planemo test` inside VSCode you need to ins # Table of Content -- [Installation](#installation) - - [Troubleshooting](#troubleshooting) -- [Configuration](#configuration) - - [Completion settings](#completion-settings) - - [Planemo settings](#planemo-settings) -- [Features](#features) - - [Tag and attribute auto-completion](#tag-and-attribute-auto-completion) - - [Documentation on Hover](#documentation-on-hover) - - [Document validation](#document-validation) - - [Document auto-formatting](#document-auto-formatting) - - [Tag auto-closing](#tag-auto-closing) - - [Snippets](#snippets) - - [Embedded syntax highlighting](#embedded-syntax-highlighting) - - [Auto-generate tests](#auto-generate-tests) - - [Auto-generate command section](#auto-generate-command-section) - - [Auto-sort param attributes](#auto-sort-param-attributes) - - [Run planemo tests in the Test Explorer](#run-planemo-tests-in-the-test-explorer) - - [Improved macros support](#improved-macros-support) _New feature!_ :rocket: +- [Installation](#installation) + - [Troubleshooting](#troubleshooting) +- [Configuration](#configuration) + - [Completion settings](#completion-settings) + - [Planemo settings](#planemo-settings) +- [Features](#features) + - [Tag and attribute auto-completion](#tag-and-attribute-auto-completion) + - [Documentation on Hover](#documentation-on-hover) + - [Document validation](#document-validation) + - [Document auto-formatting](#document-auto-formatting) + - [Tag auto-closing](#tag-auto-closing) + - [Snippets](#snippets) + - [Embedded syntax highlighting](#embedded-syntax-highlighting) + - [Auto-generate tests](#auto-generate-tests) + - [Auto-generate command section](#auto-generate-command-section) + - [Auto-sort param attributes](#auto-sort-param-attributes) + - [Run planemo tests in the Test Explorer](#run-planemo-tests-in-the-test-explorer) + - [Improved macros support](#improved-macros-support) _New feature!_ :rocket: # Installation @@ -53,9 +53,9 @@ If you encounter any problem during the language server installation, open the V Some possible errors: -- `The selected file is not a valid Python path!`. This message will appear if you select a Python binary that is not compatible with the required version. You will be given a chance to select the correct version the next time the extension gets activated. You can force it by reloading the extension or restarting VScode. +- `The selected file is not a valid Python path!`. This message will appear if you select a Python binary that is not compatible with the required version. You will be given a chance to select the correct version the next time the extension gets activated. You can force it by reloading the extension or restarting VScode. -- `Error installing the Galaxy Language Server: pip module not found`. The extension needs to create a virtual environment to install the `galaxy-language-server` package and its dependencies. To create a proper environment with `pip` included, in some systems you need to install the `python3-venv` package using the following command: `apt install python3-venv` (you may need to use `sudo`). Once you have `python3-venv` installed, you may need to remove the `glsenv` directory inside the extension installation directory and then restart or reload VSCode to recreate the environment. +- `Error installing the Galaxy Language Server: pip module not found`. The extension needs to create a virtual environment to install the `galaxy-language-server` package and its dependencies. To create a proper environment with `pip` included, in some systems you need to install the `python3-venv` package using the following command: `apt install python3-venv` (you may need to use `sudo`). Once you have `python3-venv` installed, you may need to remove the `glsenv` directory inside the extension installation directory and then restart or reload VSCode to recreate the environment. # Configuration @@ -86,6 +86,7 @@ Planemo integration is currently in **experimental** phase. Please report any pr | ----------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `galaxyTools.planemo.testing.enabled` | Whether to discover and run tests using `planemo test` directly from the Test Explorer. | | `galaxyTools.planemo.testing.autoTestDiscoverOnSaveEnabled` | Whether to try to discover new tests when a Galaxy Tool Wrapper file is saved. | +| `galaxyTools.planemo.testing.extraParams` | Additional arguments that will be passed to `planemo test` command. | ### Configuring Test Explorer UI @@ -182,9 +183,9 @@ Now you can automatically sort the attributes of param elements according to the You can now run `planemo test` for the currently opened tool directly from the `Test Explorer`. -- The tests are automatically discovered by the `galaxy-language-server` when you open a tool or save the document (this can be controlled by the settings). -- You can then run all the tests from the `Test Explorer` by using `planemo test` in the background. Currently running individual tests is not supported as AFAIK `planemo` does not have an option to do so at the moment. -- After successfully running the tests, the results will be displayed in a convenient way directly on your source XML. +- The tests are automatically discovered by the `galaxy-language-server` when you open a tool or save the document (this can be controlled by the settings). +- You can then run all the tests from the `Test Explorer` by using `planemo test` in the background. Currently running individual tests is not supported as AFAIK `planemo` does not have an option to do so at the moment. +- After successfully running the tests, the results will be displayed in a convenient way directly on your source XML. The failing tests will be marked in red and the reason for failure can be seen directly beside the test definition in the same line or more detailed in the `Output`. You can also directly navigate to each of the tests XML source from the `Test Explorer`. This can be very convenient especially when having a large number of tests in your tool. diff --git a/client/package.json b/client/package.json index 593cacc..d17ecbc 100644 --- a/client/package.json +++ b/client/package.json @@ -211,6 +211,12 @@ "type": "boolean", "markdownDescription": "Whether to try to discover new tests when a Galaxy Tool Wrapper file is saved.", "default": true + }, + "galaxyTools.planemo.testing.extraParams": { + "scope": "resource", + "type": "string", + "markdownDescription": "String with additional arguments that will be passed to `planemo test` command. Example: `--no_cleanup --update_test_data`", + "default": "" } } }, diff --git a/client/src/configuration/galaxyToolWorkspaceConfiguration.ts b/client/src/configuration/galaxyToolWorkspaceConfiguration.ts index 631d283..0e09172 100644 --- a/client/src/configuration/galaxyToolWorkspaceConfiguration.ts +++ b/client/src/configuration/galaxyToolWorkspaceConfiguration.ts @@ -30,9 +30,11 @@ class GalaxyToolsPlanemoConfiguration implements IPlanemoConfiguration { public enabled(): boolean { return this.config.get("planemo.enabled", true); } + public binaryPath(): string { return this.config.get("planemo.envPath", "planemo"); } + public galaxyRoot(): string | null { return this.config.get("planemo.galaxyRoot", null); } @@ -87,7 +89,12 @@ class GalaxyToolsPlanemoTestingConfiguration implements IPlanemoTestingConfigura enabled(): boolean { return this.config.get("planemo.testing.enabled", true); } + autoTestDiscoverOnSaveEnabled(): boolean { return this.config.get("planemo.testing.autoTestDiscoverOnSaveEnabled", true); } + + extraParams(): string { + return this.config.get("planemo.testing.extraParams", ""); + } } diff --git a/client/src/configuration/workspaceConfiguration.ts b/client/src/configuration/workspaceConfiguration.ts index f0d9329..cdb341f 100644 --- a/client/src/configuration/workspaceConfiguration.ts +++ b/client/src/configuration/workspaceConfiguration.ts @@ -14,6 +14,7 @@ export namespace Settings { export namespace Testing { export const ENABLED = "galaxyTools.planemo.testing.enabled"; export const AUTO_DISCOVERY_ON_SAVE_ENABLED = "galaxyTools.planemo.testing.autoTestDiscoverOnSaveEnabled"; + export const EXTRA_PARAMS = "galaxyTools.planemo.testing.extraParams"; } } } diff --git a/client/src/planemo/configuration.ts b/client/src/planemo/configuration.ts index c63cb4e..4700f7e 100644 --- a/client/src/planemo/configuration.ts +++ b/client/src/planemo/configuration.ts @@ -19,6 +19,8 @@ export interface IPlanemoTestingConfiguration { enabled(): boolean; autoTestDiscoverOnSaveEnabled(): boolean; + + extraParams(): string; } export interface IConfigurationFactory { diff --git a/client/src/planemo/testing/testRunner.ts b/client/src/planemo/testing/testRunner.ts index bbaf1be..f4eec55 100644 --- a/client/src/planemo/testing/testRunner.ts +++ b/client/src/planemo/testing/testRunner.ts @@ -25,8 +25,9 @@ export class PlanemoTestRunner implements ITestRunner { try { const { file: output_json_file, cleanupCallback } = await this.getJsonReportPath(testFile); const htmlReportFile = this.getTestHtmlReportFilePath(testFile); + const extraParams = this.getTestExtraParams(planemoConfig); - const testRunArguments = [ + const baseArguments = [ `test`, `--galaxy_root`, `${planemoConfig.galaxyRoot()}`, @@ -37,6 +38,8 @@ export class PlanemoTestRunner implements ITestRunner { `${testFile}`, ]; + const testRunArguments = baseArguments.concat(extraParams); + const testExecution = this.runPlanemoTest(planemoConfig, testRunArguments); this.testExecutions.set(testSuiteId, testExecution); @@ -109,4 +112,12 @@ export class PlanemoTestRunner implements ITestRunner { const reportFile = path.resolve(baseDir, `${testFileName}_test_report.html`); return reportFile; } + + private getTestExtraParams(planemoConfig: IPlanemoConfiguration) { + const extraParams = planemoConfig.testing().extraParams(); + if (extraParams != "") { + return extraParams.split(" "); + } + return []; + } } From 5f16256fcc7a62cca0f36972c62e646f169760e8 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 2 Aug 2021 15:14:54 +0200 Subject: [PATCH 2/4] Add log output channel for planemo tests This will provide information about the parameters passed to planemo and will show the log console when the command fails --- client/src/constants.ts | 2 ++ client/src/planemo/testing/testRunner.ts | 33 +++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/client/src/constants.ts b/client/src/constants.ts index 5e4e51d..ecf445c 100644 --- a/client/src/constants.ts +++ b/client/src/constants.ts @@ -13,6 +13,8 @@ export namespace Constants { export const EXPAND_DOCUMENT_SCHEMA = "gls-expand"; export const EXPAND_DOCUMENT_URI_SUFFIX = "%20%28Expanded%29"; + + export const PLANEMO_TEST_OUTPUT_CHANNEL = "Planemo Tests"; } export namespace DiagnosticCodes { diff --git a/client/src/planemo/testing/testRunner.ts b/client/src/planemo/testing/testRunner.ts index f4eec55..27821a6 100644 --- a/client/src/planemo/testing/testRunner.ts +++ b/client/src/planemo/testing/testRunner.ts @@ -1,6 +1,7 @@ import { unlinkSync } from "fs"; import * as path from "path"; import * as tmp from "tmp"; +import { OutputChannel, window } from "vscode"; import { TestEvent, TestSuiteInfo } from "vscode-test-adapter-api"; import { Constants } from "../../constants"; import { IProcessExecution, runProcess } from "../../processRunner"; @@ -10,6 +11,7 @@ import { parseTestStates } from "./testsReportParser"; export class PlanemoTestRunner implements ITestRunner { private readonly testExecutions: Map = new Map(); + private _channel: OutputChannel = window.createOutputChannel(Constants.PLANEMO_TEST_OUTPUT_CHANNEL); constructor(public readonly adapterId: string) {} @@ -35,10 +37,11 @@ export class PlanemoTestRunner implements ITestRunner { `${output_json_file}`, `--test_output`, `${htmlReportFile}`, - `${testFile}`, ]; - const testRunArguments = baseArguments.concat(extraParams); + const testRunArguments = baseArguments.concat(extraParams).concat(`${testFile}`); + + this._channel.appendLine(`Running planemo ${testRunArguments.join(" ")}`); const testExecution = this.runPlanemoTest(planemoConfig, testRunArguments); @@ -49,9 +52,11 @@ export class PlanemoTestRunner implements ITestRunner { cleanupCallback(); + this.showSummaryLog(states); + return states; } catch (err) { - console.log(err); + this.showErrorLog(err); return []; } finally { this.testExecutions.delete(testSuiteId); @@ -66,6 +71,7 @@ export class PlanemoTestRunner implements ITestRunner { console.log(`Cancelling execution of ${test} failed: ${error}`); } }); + this._channel.appendLine("Tests run cancelled."); } public isRunning(): boolean { @@ -113,11 +119,30 @@ export class PlanemoTestRunner implements ITestRunner { return reportFile; } - private getTestExtraParams(planemoConfig: IPlanemoConfiguration) { + private getTestExtraParams(planemoConfig: IPlanemoConfiguration): string[] { const extraParams = planemoConfig.testing().extraParams(); if (extraParams != "") { return extraParams.split(" "); } return []; } + + private showErrorLog(errorMessage: string) { + this._channel.appendLine(errorMessage); + this._channel.show(); + } + + private showSummaryLog(states: TestEvent[]) { + let statesMap = new Map(); + states.forEach((test) => { + let stateCount = statesMap.get(test.state); + stateCount = stateCount === undefined ? 1 : stateCount + 1; + statesMap.set(test.state, stateCount); + }); + this._channel.appendLine(`\n${states.length} tests completed:`); + statesMap.forEach((count, state) => { + this._channel.appendLine(` ${count} ${state}`); + }); + this._channel.append("\n"); + } } From 2442b9542a3c9a66b7455a08b7303253bb3fcf87 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 2 Aug 2021 15:15:14 +0200 Subject: [PATCH 3/4] Fix typos --- client/src/planemo/testing/testsReportParser.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/src/planemo/testing/testsReportParser.ts b/client/src/planemo/testing/testsReportParser.ts index 4f80243..00b29bd 100644 --- a/client/src/planemo/testing/testsReportParser.ts +++ b/client/src/planemo/testing/testsReportParser.ts @@ -65,10 +65,10 @@ function parseTestResults(parserResult: any, testSuite: TestSuiteInfo, htmlRepor const testResults: TestEvent[] = []; testSuiteResults.tests.forEach((testCaseResult) => { const testInfo = getTestInfo(testCaseResult, testSuite); - const adatedResult = adaptTestResult(testCaseResult, testInfo); - if (adatedResult !== undefined) { - adatedResult.message += `${EOL}${EOL}See full test report: ${htmlReportFile}`; - testResults.push(adatedResult); + const adaptedResult = adaptTestResult(testCaseResult, testInfo); + if (adaptedResult !== undefined) { + adaptedResult.message += `${EOL}${EOL}See full test report: ${htmlReportFile}`; + testResults.push(adaptedResult); } }); @@ -88,7 +88,7 @@ function adaptTestResult(testResult: ITestCaseResult, testInfo: TestInfo | undef if (!testResult.has_data) { return undefined; } - const state = adapTestState(testResult); + const state = adaptTestState(testResult); const testId = adaptTestId(testResult); const message = adaptTestMessage(testResult); const line = testInfo ? testInfo.line : 0; @@ -103,7 +103,7 @@ function adaptTestResult(testResult: ITestCaseResult, testInfo: TestInfo | undef return result; } -function adapTestState(testResult: ITestCaseResult): TestState { +function adaptTestState(testResult: ITestCaseResult): TestState { const adapted = stateMap.get(testResult.data.status); if (adapted === undefined) return "errored"; return adapted; From ca14c9d449ba11811f8d1a1244153facd8142885 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:39:34 +0200 Subject: [PATCH 4/4] Fix cancel tests condition The tests results should not be parsed when the test was cancelled --- client/src/planemo/testing/testRunner.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/planemo/testing/testRunner.ts b/client/src/planemo/testing/testRunner.ts index 27821a6..24106cf 100644 --- a/client/src/planemo/testing/testRunner.ts +++ b/client/src/planemo/testing/testRunner.ts @@ -46,7 +46,11 @@ export class PlanemoTestRunner implements ITestRunner { const testExecution = this.runPlanemoTest(planemoConfig, testRunArguments); this.testExecutions.set(testSuiteId, testExecution); - await testExecution.complete(); + const result = await testExecution.complete(); + + if (result.exitCode !== 0) { + return []; + } const states = await parseTestStates(output_json_file, testSuite, htmlReportFile); @@ -71,7 +75,7 @@ export class PlanemoTestRunner implements ITestRunner { console.log(`Cancelling execution of ${test} failed: ${error}`); } }); - this._channel.appendLine("Tests run cancelled."); + this._channel.appendLine("\nTests run cancelled.\n"); } public isRunning(): boolean {