diff --git a/.travis.yml b/.travis.yml index 4b9bd922..0a2fbc97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_install: install: - npm install - - npm audit + - npm audit || npm audit - npm run compile script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8715c6..3c0797d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.4.1] + +### Added + +- `executables`'s `dependOn` (type: _string[]_) property is no longer experimental. + Be careful with it. It eats [file descriptors](https://en.wikipedia.org/wiki/File_descriptor) and defecates test executions. +- section result stat to description and tooltip. + ## [2.4.0] - 2019-03-25 ### Added diff --git a/README.md b/README.md index 41e41ed1..2e6188bc 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,14 @@ This variable can be If it is an object it can contains the following properties: -| Property | Description | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | The name of the test suite (file). Can contains variables related to `pattern`. | -| `pattern` | A relative (to workspace directory) or an absolute path or [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). āš ļø**Avoid backslash!**: 🚫`\`; āœ…`/`; (required) | -| `description` | A less prominent text after the `name`. Can contains variables related to `pattern`. | -| `cwd` | The current working directory for the test executable. If it isn't provided and `defaultCwd` does, then that will be used. Can contains variables related to `pattern`. | -| `env` | Environment variables for the test executable. If it isn't provided and `defaultEnv` does, then that will be used. Can contains variables related to `pattern`. | -| `dependsOn` | (Experimental) Array of _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ it will run the related executables. | +| Property | Description | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | The name of the test suite (file). Can contains variables related to `pattern`. | +| `pattern` | A relative (to workspace directory) or an absolute path or [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). āš ļø**Avoid backslash!**: 🚫`\`; āœ…`/`; (required) | +| `description` | A less prominent text after the `name`. Can contains variables related to `pattern`. | +| `cwd` | The current working directory for the test executable. If it isn't provided and `defaultCwd` does, then that will be used. Can contains variables related to `pattern`. | +| `env` | Environment variables for the test executable. If it isn't provided and `defaultEnv` does, then that will be used. Can contains variables related to `pattern`. | +| `dependsOn` | Array of (relative / absolute) _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ and autorun is enabled in "..." menu it will run the related executables. | The `pattern` (or the `executables` used as string or an array of strings) can contains [_search-pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options). @@ -76,10 +76,14 @@ If the pattern is too general like `out/**/*test*`, it could cause unexpected ex which would not just increase the test-loading duration but also could have other unexpeced effects. I suggest to have a stricter file-name convention and a corresponding pattern like `out/**/*.test.*` or `out/**/Test.*` -**Remark** that `dependsOn` is experimental and **only** works well with paths **inside** of the workspace directory. -Also note that if it is set, it will run the related tests automatically. +**Note** that `dependsOn` **only** works well with paths/patterns **inside** of the workspace directory. +If "Enable autorun" is enabled in "**...**" menu (next to the play button), it will trigger the related tests. It accumulates events by waiting for 2 seconds after the last event. +**Also** `dependsOn` cannot contain variables like the others and outside of the workspace directory it works with only existing directory. +So if one `dependsOn` a file which is deleted, the related tests will be triggered once, but re-reation won't trigger it agian. +(It is harder to find a suitable fswatcher than I thought. Tried: chokidar, gaze) + #### Variables which can be used in `name`, `cwd` and `env` of `executables`: | Variable | Description | @@ -198,6 +202,7 @@ Note that `name` and `request` are filled, if they are undefined, so it is not n - (2018-09-03) On windows the navigate to source button isn't working. It is a framework bug. - (2018-11-17) Catch2: Long (>80 character) filename, test-name or description can cause test-list parsing failures. Workaround: `#define CATCH_CONFIG_CONSOLE_WIDTH 300` +- (2019-04-02) `dependsOn` limitation: See `dependsOn`. For solving issues use: `catch2TestExplorer.logpanel: true` and check the output window. @@ -205,5 +210,6 @@ For solving issues use: `catch2TestExplorer.logpanel: true` and check the output - Test cases: google test, catch2: info, warn, fail, stdout, stderr, capture, gtest_skip - gaze is not good enough: detects change and delete, but not creation +- `dependsOn` could contain variables ## [Contribution guideline here](CONTRIBUTING.md) diff --git a/package-lock.json b/package-lock.json index f2388723..7ced989e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-catch2-test-adapter", - "version": "2.4.0-dev", + "version": "2.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -44,9 +44,9 @@ } }, "@sinonjs/samsam": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.0.tgz", - "integrity": "sha512-beHeJM/RRAaLLsMJhsCvHK31rIqZuobfPLa/80yGH5hnD8PV1hyh9xJBJNFfNmO7yWqm+zomijHsXpI6iTQJfQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.1.tgz", + "integrity": "sha512-wRSfmyd81swH0hA1bxJZJ57xr22kC07a1N4zuIL47yTS04bDk6AoCkczcqHEjcRPmJ+FruGJ9WBQiJwMtIElFw==", "dev": true, "requires": { "@sinonjs/commons": "^1.0.2", @@ -121,9 +121,9 @@ "dev": true }, "@types/node": { - "version": "10.14.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.3.tgz", - "integrity": "sha512-2lhc7S28vo8FwR3Jv3Ifyd77AxEsx+Nl9ajWiac6/eWuvZ84zPK4RE05pfqcn3acIzlZDpQj5F1rIKQZX3ptLQ==", + "version": "10.14.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz", + "integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==", "dev": true }, "@types/request": { @@ -676,6 +676,11 @@ "assert-plus": "^1.0.0" } }, + "debounce-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/debounce-collect/-/debounce-collect-1.0.2.tgz", + "integrity": "sha1-KhkGWTbVG/LhlymxqhC+pclqM88=" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -879,9 +884,9 @@ "dev": true }, "eslint": { - "version": "5.15.3", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.15.3.tgz", - "integrity": "sha512-vMGi0PjCHSokZxE0NLp2VneGw5sio7SSiDNgIUn2tC0XkWJRNOIoHIg3CliLVfXnJsiHxGAYrkw0PieAu8+KYQ==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -904,7 +909,7 @@ "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "inquirer": "^6.2.2", - "js-yaml": "^3.12.0", + "js-yaml": "^3.13.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.11", @@ -923,9 +928,9 @@ }, "dependencies": { "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true } } @@ -2432,9 +2437,9 @@ } }, "parent-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz", - "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" @@ -2858,14 +2863,14 @@ "dev": true }, "sinon": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.0.tgz", - "integrity": "sha512-0pYvgRv46fODzT/PByqb79MVNpyxsxf38WEiXTABOF8RfIMcIARfZ+1ORuxwAmHkreZ/jST3UDBdKCRhUy/e1A==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.3.1.tgz", + "integrity": "sha512-eQKMaeWovtOtYe2xThEvaHmmxf870Di+bim10c3ZPrL5bZhLGtu8cz+rOBTFz0CwBV4Q/7dYwZiqZbGVLZ+vjQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.4.0", "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.0", + "@sinonjs/samsam": "^3.3.1", "diff": "^3.5.0", "lolex": "^3.1.0", "nise": "^1.4.10", @@ -3271,9 +3276,9 @@ } }, "typescript": { - "version": "3.3.4000", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.4000.tgz", - "integrity": "sha512-jjOcCZvpkl2+z7JFn0yBOoLQyLoIkNZAs/fYJkUG6VKy6zLPHJGfQJYFHzibB6GJaF/8QrcECtlQ5cpvRHSMEA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.1.tgz", + "integrity": "sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==", "dev": true }, "uc.micro": { @@ -3511,9 +3516,9 @@ } }, "vsce": { - "version": "1.58.0", - "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.58.0.tgz", - "integrity": "sha512-4QDcY2ig4u+2/Bx+ipIua4ZQm3P19QjQu9IYNNNRHRt0We3P30qi5JLDaJz2Ihg9ixHniPOtebxfEbpMlimG7w==", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.59.0.tgz", + "integrity": "sha512-tkB97885k5ce25Brbe9AZTCAXAkBh7oa5EOzY0BCJQ51W/mfRaQuCluCd9gZpWdgiU4AbPvwxtoVKKsenlSt8w==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -3580,9 +3585,9 @@ } }, "vscode-test-adapter-api": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.6.0.tgz", - "integrity": "sha512-mrQVVkVB2NEz+KYZBoaMi/JUNYTbPmg0h7z8gC8yNE1RjwMppSAxSwK1XqJogoBUFmZAYf4kzz9u1gmM4Q3tzw==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.7.0.tgz", + "integrity": "sha512-X0rTcoDhDBmpmJuev2C5+GHGZD41nmcRYoSe7iw5e9/aIPTOFve1T1F5x9gb+zXoNQnkXSDibyMkeHDKtIkqCg==" }, "vscode-test-adapter-util": { "version": "0.6.4", diff --git a/package.json b/package.json index 185e7f54..e6ec546e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "icon": "resources/icon.png", "author": "Mate Pek", "publisher": "matepek", - "version": "2.4.0", + "version": "2.4.1-dev", "license": "Unlicense", "homepage": "https://github.com/matepek/vscode-catch2-test-adapter", "repository": { @@ -45,10 +45,11 @@ "hbenl.vscode-test-explorer" ], "dependencies": { + "debounce-collect": "^1.0.2", "entities": "^1.1.2", "gaze": "^1.1.3", "tslib": "^1.9.3", - "vscode-test-adapter-api": "^1.6.0", + "vscode-test-adapter-api": "^1.7.0", "vscode-test-adapter-util": "^0.6.4", "xml2js": "^0.4.19" }, @@ -58,23 +59,23 @@ "@types/entities": "^1.1.1", "@types/fs-extra": "^5.0.5", "@types/mocha": "^5.2.6", - "@types/node": "^10.14.3", + "@types/node": "^10.14.4", "@types/request-promise": "4.1.42", "@types/sinon": "^5.0.7", "@types/xml2js": "^0.4.3", "@typescript-eslint/eslint-plugin": "^1.5.0", "@typescript-eslint/parser": "^1.5.0", "deep-equal": "^1.0.1", - "eslint": "^5.15.3", + "eslint": "^5.16.0", "eslint-config-prettier": "^4.1.0", "eslint-plugin-prettier": "^3.0.1", "fs-extra": "^7.0.1", "mocha-eslint": "^5.0.0", "prettier": "^1.16.4", "request-promise": "4.2.2", - "sinon": "^7.2.7", - "typescript": "^3.3.3333", - "vsce": "^1.58.0", + "sinon": "^7.3.1", + "typescript": "^3.4.1", + "vsce": "^1.59.0", "vscode": "1.1.30", "vscode-test": "0.1.5" }, @@ -92,6 +93,7 @@ "properties": { "catch2TestExplorer.executables": { "markdownDescription": "The location of your test executables (relative to the workspace folder or absolute path) and with a lot of other setting. [Details](https://github.com/matepek/vscode-catch2-test-adapter#catch2TestExplorerexecutables)", + "description": "The location of your test executables (relative to the workspace folder or absolute path) and with a lot of other setting. [Details](https://github.com/matepek/vscode-catch2-test-adapter#catch2TestExplorerexecutables)", "scope": "resource", "default": [ { @@ -215,7 +217,7 @@ }, "dependsOn": { "type": "array", - "description": "(Experimental) Array of _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ it will run the related executables.", + "description": "Array of (relative / absolute) _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ and autorun is enabled in \"...\" menu it will run the related executables.", "additionalItems": { "type": "string", "minLength": 1 @@ -340,7 +342,7 @@ }, "dependsOn": { "type": "array", - "description": "(Experimental) Array of _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ it will run the related executables.", + "description": "Array of (relative / absolute) _paths_ / [_glob pattern_](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) (string[]). If a related file is _changed/created/deleted_ and autorun is enabled in \"...\" menu it will run the related executables.", "additionalItems": { "type": "string", "minLength": 1 @@ -361,6 +363,7 @@ }, "catch2TestExplorer.defaultCwd": { "markdownDescription": "The working directory where the test is run (relative to the workspace folder or absolue path), if it isn't provided in \"executables\". (It resolves variables.)", + "description": "The working directory where the test is run (relative to the workspace folder or absolue path), if it isn't provided in \"executables\". (It resolves variables.)", "scope": "resource", "type": "string", "default": "${absDirpath}", @@ -382,6 +385,7 @@ }, "catch2TestExplorer.defaultEnv": { "markdownDescription": "Environment variables to be set when running the tests. (It resolves variables.)", + "description": "Environment variables to be set when running the tests. (It resolves variables.)", "type": "object", "additionalProperties": { "anyOf": [ @@ -418,6 +422,7 @@ }, "catch2TestExplorer.defaultRngSeed": { "markdownDescription": "Shuffles the tests with the given random. Catch2: [--rng-seed ( or 'time')](https://github.com/catchorg/Catch2/blob/master/docs/command-line.md#rng-seed); Google Test: [--gtest_random_seed=](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#shuffling-the-tests);", + "description": "Shuffles the tests with the given random. Catch2: [--rng-seed ( or 'time')](https://github.com/catchorg/Catch2/blob/master/docs/command-line.md#rng-seed); Google Test: [--gtest_random_seed=](https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#shuffling-the-tests);", "scope": "resource", "default": null, "oneOf": [ @@ -438,6 +443,7 @@ }, "catch2TestExplorer.defaultWatchTimeoutSec": { "markdownDescription": "Test executables are being watched (only inside the workspace directory). In case of one recompiles it will try to preserve the test states. If compilation reaches timeout it will drop the suite.", + "description": "Test executables are being watched (only inside the workspace directory). In case of one recompiles it will try to preserve the test states. If compilation reaches timeout it will drop the suite.", "scope": "resource", "type": "integer", "default": 10, @@ -445,6 +451,7 @@ }, "catch2TestExplorer.defaultRunningTimeoutSec": { "markdownDescription": "Test executable is running in a process. In case of an inifinite loop, it will run forever, unless this parameter is set. It applies instantly. (0 means infinite)", + "description": "Test executable is running in a process. In case of an inifinite loop, it will run forever, unless this parameter is set. It applies instantly. (0 means infinite)", "scope": "resource", "type": "integer", "default": 0, @@ -452,6 +459,7 @@ }, "catch2TestExplorer.workerMaxNumber": { "markdownDescription": "The variable maximize the number of the parallel test execution. It applies instantly.", + "description": "The variable maximize the number of the parallel test execution. It applies instantly.", "scope": "resource", "type": "integer", "default": 1, @@ -459,6 +467,7 @@ }, "catch2TestExplorer.debugConfigTemplate": { "markdownDescription": "Set the necessary debug configuraitons and the debug button will work. [Details](https://github.com/matepek/vscode-catch2-test-adapter#catch2TestExplorerdebugConfigTemplate)", + "description": "Set the necessary debug configuraitons and the debug button will work. [Details](https://github.com/matepek/vscode-catch2-test-adapter#catch2TestExplorerdebugConfigTemplate)", "scope": "resource", "default": null, "oneOf": [ @@ -507,30 +516,35 @@ }, "catch2TestExplorer.debugBreakOnFailure": { "markdownDescription": "Debugger breaks on failure while debugging the test. Catch2: --break; Google Test: --gtest_break_on_failure;", + "description": "Debugger breaks on failure while debugging the test. Catch2: --break; Google Test: --gtest_break_on_failure;", "scope": "resource", "type": "boolean", "default": true }, "catch2TestExplorer.defaultNoThrow": { "markdownDescription": "Skips all assertions that test that an exception is thrown, e.g. REQUIRE_THROWS. This is a Catch2 parameter: --nothrow", + "description": "Skips all assertions that test that an exception is thrown, e.g. REQUIRE_THROWS. This is a Catch2 parameter: --nothrow", "scope": "resource", "type": "boolean", "default": false }, "catch2TestExplorer.logpanel": { "markdownDescription": "Creates a new output channel and write the log messages there. For debugging. Enabling it could slow down your vscode.", + "description": "Creates a new output channel and write the log messages there. For debugging. Enabling it could slow down your vscode.", "scope": "resource", "type": "boolean", "default": false }, "catch2TestExplorer.logfile": { "markdownDescription": "Writes the log message into the given file. Empty means disabled.", + "description": "Writes the log message into the given file. Empty means disabled.", "scope": "resource", "type": "string", "default": "" }, "catch2TestExplorer.enableTestListCaching": { "markdownDescription": "(Experimental) In case your executable took too much time to list the tests, one can set this. It will preserve the output of `--gtest_list_tests --gtest_output=xml:...`. (Beware: Older Google Test doesn't support xml test list format.) (Click [here](http://bit.ly/2HFcAC6), if you think it is a useful feature!)", + "description": "(Experimental) In case your executable took too much time to list the tests, one can set this. It will preserve the output of `--gtest_list_tests --gtest_output=xml:...`. (Beware: Older Google Test doesn't support xml test list format.) (Click [here](http://bit.ly/2HFcAC6), if you think it is a useful feature!)", "scope": "resource", "type": "boolean", "default": false diff --git a/src/@types/debounce-collect/index.d.ts b/src/@types/debounce-collect/index.d.ts new file mode 100644 index 00000000..b7b21ac2 --- /dev/null +++ b/src/@types/debounce-collect/index.d.ts @@ -0,0 +1,17 @@ +declare module 'debounce-collect' { + function debounce(func: (aggregatedArgs: [T1][]) => void, wait: number, immediate?: boolean): (arg1: T1) => void; + + function debounce( + func: (aggregatedArgs: [T1, T2][]) => void, + wait: number, + immediate?: boolean, + ): (arg1: T1, arg2: T2) => void; + + function debounce( + func: (aggregatedArgs: [T1, T2, T3][]) => void, + wait: number, + immediate?: boolean, + ): (arg1: T1, arg2: T2) => void; + + export = debounce; +} diff --git a/src/Catch2TestInfo.ts b/src/Catch2TestInfo.ts index 1c209b00..6ecaf78a 100644 --- a/src/Catch2TestInfo.ts +++ b/src/Catch2TestInfo.ts @@ -119,8 +119,6 @@ export class Catch2TestInfo extends AbstractTestInfo { } private _processXmlTagTestCaseInner(testCase: XmlObject, testEvent: TestEvent): void { - const title: Catch2Section = new Catch2Section(testCase.$.name, testCase.$.filename, testCase.$.line); - if (testCase.OverallResult[0].$.hasOwnProperty('durationInSeconds')) { testEvent.message += 'ā± Duration: ' + testCase.OverallResult[0].$.durationInSeconds + ' second(s).\n'; this._extendDescriptionAndTooltip( @@ -129,6 +127,8 @@ export class Catch2TestInfo extends AbstractTestInfo { ); } + const title: Catch2Section = new Catch2Section(testCase.$.name, testCase.$.filename, testCase.$.line); + this._processInfoWarningAndFailureTags(testCase, title, [], testEvent); this._processXmlTagExpressions(testCase, title, [], testEvent); @@ -160,6 +160,27 @@ export class Catch2TestInfo extends AbstractTestInfo { if (testCase.OverallResult[0].$.success === 'true') { testEvent.state = 'passed'; } + + if (this._sections.length) { + let failedBranch = 0; + let succBranch = 0; + + const traverse = (section: Catch2Section): void => { + if (section.children.length === 0) { + section.failed ? ++failedBranch : ++succBranch; + } else { + for (let i = 0; i < section.children.length; ++i) { + traverse(section.children[i]); + } + } + }; + + this._sections.forEach(section => traverse(section)); + + const branchMsg = failedBranch ? 'āœ— ' + failedBranch + ' /' : ' ' + 'āœ“ ' + succBranch; + testEvent.description += ' [' + branchMsg + ']'; + testEvent.tooltip += '\nšŸ”€ ' + branchMsg + ' branches'; + } } private _processInfoWarningAndFailureTags( diff --git a/src/RootTestSuiteInfo.ts b/src/RootTestSuiteInfo.ts index e5224262..5f8d3ba0 100644 --- a/src/RootTestSuiteInfo.ts +++ b/src/RootTestSuiteInfo.ts @@ -16,8 +16,8 @@ export class RootTestSuiteInfo extends AbstractTestSuiteInfoBase implements vsco private _executables: TestExecutableInfo[] = []; private readonly _taskPool: TaskPool; - public constructor(shared: SharedVariables, workerMaxNumber: number) { - super(shared, 'Catch2 and Google tests', undefined, undefined); + public constructor(id: string | undefined, shared: SharedVariables, workerMaxNumber: number) { + super(shared, 'Catch2 and Google tests', undefined, id); this._taskPool = new TaskPool(workerMaxNumber); } diff --git a/src/SharedVariables.ts b/src/SharedVariables.ts index 9b475073..d9674138 100644 --- a/src/SharedVariables.ts +++ b/src/SharedVariables.ts @@ -18,7 +18,7 @@ export class SharedVariables implements vscode.Disposable { >, public readonly loadWithTaskEmitter: vscode.EventEmitter<() => void | PromiseLike>, public readonly sendTestEventEmitter: vscode.EventEmitter, - public readonly autorunEmitter: vscode.EventEmitter>, + public readonly retire: vscode.EventEmitter, public rngSeed: string | number | null, public execWatchTimeout: number, private _execRunningTimeout: null | number, diff --git a/src/TestAdapter.ts b/src/TestAdapter.ts index 768a797d..6d05d24b 100644 --- a/src/TestAdapter.ts +++ b/src/TestAdapter.ts @@ -12,9 +12,11 @@ import { TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, + RetireEvent, } from 'vscode-test-adapter-api'; import * as api from 'vscode-test-adapter-api'; import * as util from 'vscode-test-adapter-util'; +import debounce = require('debounce-collect'); import { RootTestSuiteInfo } from './RootTestSuiteInfo'; import { resolveVariables, generateUniqueId } from './Util'; @@ -31,7 +33,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { private readonly _testStatesEmitter = new vscode.EventEmitter< TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent >(); - private readonly _autorunEmitter = new vscode.EventEmitter(); + private readonly _retireEmitter = new vscode.EventEmitter(); private readonly _variableToValue: [string, string][] = [ ['${workspaceDirectory}', this.workspaceFolder.uri.fsPath], @@ -44,7 +46,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { private readonly _sendTestEventEmitter = new vscode.EventEmitter(); - private readonly _sendAutorunEmitter = new vscode.EventEmitter>(); + private readonly _sendRetireEmitter = new vscode.EventEmitter(); private readonly _mainTaskQueue = new TaskQueue([], 'TestAdapter'); private readonly _disposables: vscode.Disposable[] = []; @@ -71,7 +73,28 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._disposables.push(this._testsEmitter); this._disposables.push(this._testStatesEmitter); - this._disposables.push(this._autorunEmitter); + + this._disposables.push(this._sendRetireEmitter); + { + const unique = new Set(); + + const retire = (aggregatedArgs: [AbstractTestSuiteInfo[]][]): void => { + const isScheduled = unique.size > 0; + aggregatedArgs.forEach(args => args[0].forEach(test => unique.add(test))); + + if (!isScheduled) + this._mainTaskQueue.then(() => { + if (unique.size > 0) { + this._retireEmitter.fire({ tests: [...unique].map(t => t.id) }); + unique.clear(); + } + }); + }; + + const retireDelayInSec = 2; // could be a config + + this._disposables.push(this._sendRetireEmitter.event(debounce(retire, retireDelayInSec * 1000))); + } this._disposables.push(this._loadWithTaskEmitter); this._disposables.push( @@ -133,48 +156,6 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { }), ); - this._disposables.push(this._sendAutorunEmitter); - { - const retireDelayInSec = 2; // it should be a config - - const data = new class { - public idBuffer: Set = new Set(); - public lastArrivedAt: number | undefined = undefined; // undefined means: not scheduled - }(); - - const s = (): void => { - if (data.lastArrivedAt === undefined) return; // shouldn't be here - - if (Date.now() - data.lastArrivedAt >= retireDelayInSec * 1000) { - const ids = [...data.idBuffer.values()]; - - data.idBuffer = new Set(); - data.lastArrivedAt = undefined; - - if (ids.length > 0) this.run(ids); - } else { - setTimeout(s, retireDelayInSec * 1000 - (Date.now() - data.lastArrivedAt)); - } - }; - - this._disposables.push( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this._sendAutorunEmitter.event((tests: IterableIterator) => { - if (data.idBuffer !== undefined) { - for (const test of tests) { - data.idBuffer.add(test.id); - } - } - - if (data.lastArrivedAt === undefined) { - setTimeout(s, retireDelayInSec); - } - - data.lastArrivedAt = Date.now(); - }), - ); - } - const config = this._getConfiguration(); this._disposables.push( @@ -195,7 +176,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._testStatesEmitter, this._loadWithTaskEmitter, this._sendTestEventEmitter, - this._sendAutorunEmitter, + this._sendRetireEmitter, this._getDefaultRngSeed(config), this._getDefaultExecWatchTimeout(config), this._getDefaultExecRunningTimeout(config), @@ -207,7 +188,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { vscode.workspace.onDidChangeConfiguration(configChange => { if (configChange.affectsConfiguration('catch2TestExplorer.defaultRngSeed', this.workspaceFolder.uri)) { this._shared.rngSeed = this._getDefaultRngSeed(this._getConfiguration()); - this._autorunEmitter.fire(); + this._retireEmitter.fire({}); } if (configChange.affectsConfiguration('catch2TestExplorer.defaultWatchTimeoutSec', this.workspaceFolder.uri)) { this._shared.execWatchTimeout = this._getDefaultExecWatchTimeout(this._getConfiguration()); @@ -229,7 +210,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { }), ); - this._rootSuite = new RootTestSuiteInfo(this._shared, 1); + this._rootSuite = new RootTestSuiteInfo(undefined, this._shared, 1); } public dispose(): void { @@ -270,8 +251,8 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { return this._testsEmitter.event; } - public get autorun(): vscode.Event { - return this._autorunEmitter.event; + public get retire(): vscode.Event { + return this._retireEmitter.event; } public load(): Promise { @@ -282,7 +263,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._rootSuite.dispose(); - this._rootSuite = new RootTestSuiteInfo(this._shared, this._getWorkerMaxNumber(config)); + this._rootSuite = new RootTestSuiteInfo(this._rootSuite.id, this._shared, this._getWorkerMaxNumber(config)); return this._mainTaskQueue.then(() => { this._log.info('load started'); diff --git a/src/TestExecutableInfo.ts b/src/TestExecutableInfo.ts index cbb0fc45..90e7c278 100644 --- a/src/TestExecutableInfo.ts +++ b/src/TestExecutableInfo.ts @@ -129,7 +129,7 @@ export class TestExecutableInfo implements vscode.Disposable { w.onAll(fsPath => { this._shared.log.info('dependsOn watcher event:', fsPath); - this._shared.autorunEmitter.fire(this._executables.values()); + this._shared.retire.fire([...this._executables.values()]); }); } else { absPatterns.push(p.absPattern); @@ -144,7 +144,7 @@ export class TestExecutableInfo implements vscode.Disposable { w.onAll(fsPath => { this._shared.log.info('dependsOn watcher event:', fsPath); - this._shared.autorunEmitter.fire(this._executables.values()); + this._shared.retire.fire([...this._executables.values()]); }); } } catch (e) { @@ -308,6 +308,7 @@ export class TestExecutableInfo implements vscode.Disposable { this._executables.set(filePath, suite); } this._lastEventArrivedAt.delete(filePath); + this._shared.retire.fire([suite]); }) .then(resolve, reject); }); diff --git a/test/Common.ts b/test/Common.ts index ee406e3a..5592fc31 100644 --- a/test/Common.ts +++ b/test/Common.ts @@ -327,45 +327,54 @@ export class TestAdapter extends my.TestAdapter { export class ChildProcessStub extends EventEmitter { public readonly stdout: Readable; + private _stdoutChunks: (string | null)[] = []; + private _canPushOut: boolean = false; + public readonly stderr: Readable; + private _stderrChunks: (string | null)[] = []; + private _canPushErr: boolean = false; public closed: boolean = false; - private _read(): void { - //this.stdout.push(null); + private _writeStdOut(): void { + while (this._stdoutChunks.length && this._canPushOut) + this._canPushOut = this.stdout.push(this._stdoutChunks.shift()); + } + + private _writeStdErr(): void { + while (this._stderrChunks.length && this._canPushErr) + this._canPushErr = this.stderr.push(this._stderrChunks.shift()); } public constructor(stdout?: string | Iterable, close?: number | string, stderr?: string) { super(); + + if (stdout === undefined) this._stdoutChunks = []; + else if (typeof stdout === 'string') this._stdoutChunks = [stdout, null]; + else this._stdoutChunks = [...stdout, null]; + + if (stderr === undefined) this._stderrChunks = [null]; + else if (typeof stderr === 'string') this._stderrChunks = [stderr, null]; + else throw new Error('assert'); + this.stdout = new Readable({ read: () => { - this._read(); + this._canPushOut = true; + this._writeStdOut(); }, }); this.stderr = new Readable({ read: () => { - this._read(); + this._canPushErr = true; + this._writeStdErr(); }, }); + this.stdout.on('end', () => { this.closed = true; if (close === undefined) this.emit('close', 1, null); else if (typeof close === 'string') this.emit('close', null, close); else this.emit('close', close, null); }); - if (stderr !== undefined) { - this.stderr.push(stderr); - this.stderr.push(null); - } - if (stdout !== undefined) { - if (typeof stdout !== 'string') { - for (let line of stdout) { - this.write(line); - } - this.close(); - } else { - this.writeAndClose(stdout); - } - } } public kill(signal?: string): void { @@ -376,12 +385,15 @@ export class ChildProcessStub extends EventEmitter { } public write(data: string): void { - this.stdout.push(data); + this._stdoutChunks.push(data); + this._writeStdOut(); } public close(): void { - this.stdout.push(null); - this.stderr.push(null); + this._stdoutChunks.push(null); + this._writeStdOut(); + this._stderrChunks.push(null); + this._writeStdErr(); } public writeAndClose(data: string): void { @@ -392,7 +404,7 @@ export class ChildProcessStub extends EventEmitter { public writeLineByLineAndClose(data: string): void { const lines = data.split('\n'); lines.forEach(l => { - this.write(l); + this.write(l + '\n'); }); this.close(); } diff --git a/test/Documentation.test.ts b/test/Documentation.test.ts index 8b535b99..d9f836f5 100644 --- a/test/Documentation.test.ts +++ b/test/Documentation.test.ts @@ -50,7 +50,9 @@ describe(path.basename(__filename), function() { if (key === 'catch2TestExplorer.logfile') { // skip } else { - assert.strictEqual(findDescriptionInReadmeTable(key), properties[key]['markdownDescription'], key); + const descriptionInReadme = findDescriptionInReadmeTable(key); + assert.strictEqual(descriptionInReadme, properties[key]['markdownDescription'], key); + assert.strictEqual(descriptionInReadme, properties[key]['description'], key); } }); } diff --git a/test/FSWrapper.test.ts b/test/FSWrapper.test.ts index 6ab3b186..a78413b2 100644 --- a/test/FSWrapper.test.ts +++ b/test/FSWrapper.test.ts @@ -179,7 +179,7 @@ describe('vscode.Uri', function() { }); }); -describe('ChildProcessStub', function() { +describe('ChildProcessFake', function() { it('should works', async function() { const cp = new ChildProcessStub('alma'); let output = ''; @@ -194,7 +194,7 @@ describe('ChildProcessStub', function() { }); it('should works2', async function() { - this.timeout(700); + this.timeout(800); this.slow(600); const cp = new ChildProcessStub(); let output = ''; diff --git a/test/RealCatch2Executables.test.ts b/test/RealCatch2Executables.test.ts index da50c853..5d5ef5b5 100644 --- a/test/RealCatch2Executables.test.ts +++ b/test/RealCatch2Executables.test.ts @@ -141,35 +141,35 @@ describe(path.basename(__filename), function() { adapter = new TestAdapter(); - let autorunCounter = 0; - adapter.autorun(() => { - ++autorunCounter; + let retireCounter = 0; + adapter.retire(() => { + ++retireCounter; }); await adapter.load(); assert.strictEqual(adapter.root.children.length, 0); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 0); await adapter.doAndWaitForReloadEvent(this, () => { return copy('../suite1.exe', 'out/suite1.exe'); }); assert.strictEqual(adapter.root.children.length, 1); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 0); await adapter.doAndWaitForReloadEvent(this, () => { return copy('../suite2.exe', 'out/sub/suite2X.exe'); }); assert.strictEqual(adapter.root.children.length, 2); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 0); await adapter.doAndWaitForReloadEvent(this, () => { return copy('../suite2.exe', 'out/sub/suite2.exe'); }); assert.strictEqual(adapter.root.children.length, 3); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 0); await settings.updateConfig('defaultWatchTimeoutSec', 1); @@ -178,12 +178,13 @@ describe(path.basename(__filename), function() { }); assert.strictEqual(adapter.root.children.length, 2); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 1); const eventCount = adapter.testStatesEvents.length; await adapter.run([adapter.root.id]); + assert.strictEqual(adapter.testStatesEvents.length, eventCount + 14); - assert.strictEqual(autorunCounter, 0); + assert.strictEqual(retireCounter, 1); }); }); }); diff --git a/tsconfig.json b/tsconfig.json index b86d2a16..81c368ef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,8 @@ "noImplicitThis": true, "noUnusedLocals": true, "removeComments": true, - "skipLibCheck": true + "skipLibCheck": true, + "typeRoots": ["node_modules/@types", "src/@types"] }, "include": ["src/main.ts", "test/**/*.ts"] }