diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2ed8543c..9d660ed6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,6 @@ --- name: Bug report about: Create a report to help us improve - --- **Describe the bug** @@ -9,6 +8,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -21,8 +21,11 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - Extension Version [e.g. 22] - - VS Code Version [e.g. 22] + +- Extension Version [e.g. 22] +- VS Code Version [e.g. 22] +- Catch2 Version +- OS Type and Version **Additional context** Add any other context about the problem here. diff --git a/.vscode/launch.json b/.vscode/launch.json index c83478d6..f8c8243f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,22 +1,6 @@ { "version": "0.2.0", "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Deploy", - "program": "${workspaceFolder}/out/repo_scripts/deploy.js", - "cwd": "${workspaceFolder}" - }, - { - "type": "extensionHost", - "request": "launch", - "name": "Catch2 adapter", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceFolder}"], - "outFiles": ["${workspaceFolder}/out"], - "preLaunchTask": "npm: watch" - }, { "name": "Extension Tests", "type": "extensionHost", @@ -33,6 +17,15 @@ "env": { "C2AVCVA": "c:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat" } + }, + { + "type": "extensionHost", + "request": "launch", + "name": "Catch2 adapter", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/out"], + "preLaunchTask": "npm: watch" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 2087e6b7..f3cb01fe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,5 +12,9 @@ }, "prettier.printWidth": 100, "prettier.jsxBracketSameLine": true, - "editor.formatOnSave": true + "editor.formatOnSave": true, + "files.associations": { + "*.txt": "cpp" + }, + "editor.formatOnSaveTimeout": 1750 } diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f4a5bf..93d424c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.0.2] + +### Fixed + +- A bug related to jumping to source. + ## [2.0.1] - 2018-11-07 ### Fixed diff --git a/README.md b/README.md index 9b77648a..c6780a6f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Update your settings! ([See changelog](CHANGELOG.md) or [configuration below](#C [![GitHub license](https://img.shields.io/github/license/matepek/vscode-catch2-test-adapter.svg)](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/LICENSE) [![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/d/matepek.vscode-catch2-test-adapter.svg)](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter) [![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/v/matepek.vscode-catch2-test-adapter.svg)](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter) +[![GitHub stars](https://img.shields.io/github/stars/badges/shields.svg?style=social&label=Stars)](https://github.com/matepek/vscode-catch2-test-adapter) This extension allows you to run your [Catch2 tests](https://github.com/catchorg/Catch2) using the [Test Explorer for VS Code](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer). @@ -21,8 +22,8 @@ This extension allows you to run your [Catch2 tests](https://github.com/catchorg | Property | Description | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `catch2TestExplorer.executables` | The location of your test executables (relative to the workspace folder or absolute path) and with a lot of other setting. Details: [below](#catch2TestExplorer.executables) | -| `catch2TestExplorer.defaultEnv` | Default environment variables to be set when running the tests, if it isn't provided in 'executables'. (Resolves: ${workspaceFolder}) | -| `catch2TestExplorer.defaultCwd` | The working directory where the test is run (relative to the workspace folder or absolue path), if it isn't provided in "executables". (Resolves: ${workspaceFolder}) | +| `catch2TestExplorer.defaultEnv` | Default environment variables to be set when running the tests, if it isn't provided in 'executables'. (Resolves: \${workspaceFolder}) | +| `catch2TestExplorer.defaultCwd` | The working directory where the test is run (relative to the workspace folder or absolue path), if it isn't provided in "executables". (Resolves: \${workspaceFolder}) | | `catch2TestExplorer.defaultRngSeed` | Specify a seed for the Random Number Generator. For details see [Catch2 documentation](https://github.com/catchorg/Catch2/blob/master/docs/command-line.md#rng-seed) | | `catch2TestExplorer.defaultWatchTimeoutSec` | Test executables are being watched. In case of one compiles too much this variable can help with it. Unit: second. | | `catch2TestExplorer.workerMaxNumber` | The variable maximize the number of the parallel test execution. | @@ -138,6 +139,7 @@ Example: ## Known issues - (2018-09-03) On windows the navigate to source button isn't working. It is a framework bug. +- (2018-11-17) Long (>80 character) filename, test-name or description can cause test-list parsing failures. Workaround: `#define CATCH_CONFIG_CONSOLE_WIDTH 9999` ## TODOs diff --git a/package.json b/package.json index 3c2696c6..46f91747 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@types/fs-extra": "^5.0.4", "@types/mocha": "^5.2.5", "@types/request-promise": "4.1.42", - "@types/sinon": "^5.0.5", + "@types/sinon": "5.0.5", "@types/xml2js": "^0.4.3", "deep-equal": "^1.0.1", "fs-extra": "^7.0.1", @@ -145,16 +145,7 @@ "object", "null" ], - "default": { - "type": "cppdbg", - "MIMode": "lldb", - "program": "${exec}", - "args": "${args}", - "cwd": "${cwd}", - "env": "${envObj}", - "stopAtEntry": false, - "externalConsole": true - }, + "default": null, "scope": "resource" } } diff --git a/src/C2AllTestSuiteInfo.ts b/src/C2AllTestSuiteInfo.ts index 93c8c78e..26e34ad3 100644 --- a/src/C2AllTestSuiteInfo.ts +++ b/src/C2AllTestSuiteInfo.ts @@ -5,10 +5,10 @@ import {SpawnOptions} from 'child_process'; import {inspect} from 'util'; import * as vscode from 'vscode'; -import {TestRunFinishedEvent, TestRunStartedEvent, TestSuiteInfo} from 'vscode-test-adapter-api'; +import {TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, TestSuiteInfo} from 'vscode-test-adapter-api'; +import * as util from 'vscode-test-adapter-util'; import {C2ExecutableInfo} from './C2ExecutableInfo' -import {C2TestAdapter} from './C2TestAdapter'; import {C2TestInfo} from './C2TestInfo'; import {C2TestSuiteInfo} from './C2TestSuiteInfo'; import {generateUniqueId} from './IdGenerator'; @@ -21,7 +21,19 @@ export class C2AllTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { readonly children: C2TestSuiteInfo[] = []; private readonly _executables: C2ExecutableInfo[] = []; - constructor(private readonly adapter: C2TestAdapter) { + constructor( + public readonly log: util.Log, + public readonly workspaceFolder: vscode.WorkspaceFolder, + public readonly testsEmitter: + vscode.EventEmitter, + public readonly testStatesEmitter: + vscode.EventEmitter, + public readonly variableToValue: [string, string][], + public isEnabledSourceDecoration: boolean, + public rngSeed: string|number|null, + public execWatchTimeout: number, + ) { this.id = generateUniqueId(); } @@ -64,8 +76,7 @@ export class C2AllTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { createChildSuite(label: string, execPath: string, execOptions: SpawnOptions): C2TestSuiteInfo { - const suite = - new C2TestSuiteInfo(label, this.adapter, execPath, execOptions); + const suite = new C2TestSuiteInfo(label, this, execPath, execOptions); let i = this.children.findIndex((v: C2TestSuiteInfo) => { return suite.label.trim().localeCompare(v.label.trim()) < 0; @@ -89,13 +100,13 @@ export class C2AllTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { await executable.load(); this._executables.push(executable); } catch (e) { - this.adapter.log.error(inspect([e, i, executables])); + this.log.error(inspect([e, i, executables])); } } } run(tests: string[], workerMaxNumber: number): Promise { - this.adapter.testStatesEmitter.fire( + this.testStatesEmitter.fire( {type: 'started', tests: tests}); const taskPool = new TaskPool(workerMaxNumber); @@ -117,12 +128,11 @@ export class C2AllTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { } if (testSet.size > 0) { - this.adapter.log.error('Some tests have remained.'); + this.log.error('Some tests have remained.'); } const always = () => { - this.adapter.testStatesEmitter.fire( - {type: 'finished'}); + this.testStatesEmitter.fire({type: 'finished'}); }; return Promise.all(ps).then(always, always); diff --git a/src/C2ExecutableInfo.ts b/src/C2ExecutableInfo.ts index 84d14e63..d44bce37 100644 --- a/src/C2ExecutableInfo.ts +++ b/src/C2ExecutableInfo.ts @@ -7,18 +7,15 @@ import {inspect, promisify} from 'util'; import * as vscode from 'vscode'; import {C2AllTestSuiteInfo} from './C2AllTestSuiteInfo'; -import {C2TestAdapter} from './C2TestAdapter'; import {C2TestSuiteInfo} from './C2TestSuiteInfo'; import * as c2fs from './FsWrapper'; import {resolveVariables} from './Helpers'; export class C2ExecutableInfo implements vscode.Disposable { constructor( - private _adapter: C2TestAdapter, - private readonly _allTests: C2AllTestSuiteInfo, - public readonly name: string, public readonly pattern: string, - public readonly cwd: string, public readonly env: {[prop: string]: any}) { - } + private _allTest: C2AllTestSuiteInfo, public readonly name: string, + public readonly pattern: string, public readonly cwd: string, + public readonly env: {[prop: string]: any}) {} private _disposables: vscode.Disposable[] = []; private _watcher: vscode.FileSystemWatcher|undefined = undefined; @@ -36,7 +33,7 @@ export class C2ExecutableInfo implements vscode.Disposable { } async load(): Promise { - const wsUri = this._adapter.workspaceFolder.uri; + const wsUri = this._allTest.workspaceFolder.uri; const pattern = this.pattern.startsWith('./') ? this.pattern.substr(2) : this.pattern; const isAbsolute = path.isAbsolute(pattern); @@ -47,17 +44,17 @@ export class C2ExecutableInfo implements vscode.Disposable { const isPartOfWs = !relativeToWs.startsWith('..'); if (isAbsolute && isPartOfWs) - this._adapter.log.info( + this._allTest.log.info( 'Absolute path is used for workspace directory: ' + inspect([this])); if (this.pattern.indexOf('\\') != -1) - this._adapter.log.warn( + this._allTest.log.warn( 'Pattern contains backslash character: ' + this.pattern); let fileUris: vscode.Uri[] = []; if (!isAbsolute) { const relativePattern = - new vscode.RelativePattern(this._adapter.workspaceFolder, pattern); + new vscode.RelativePattern(this._allTest.workspaceFolder, pattern); try { fileUris = @@ -74,7 +71,7 @@ export class C2ExecutableInfo implements vscode.Disposable { this._disposables.push( this._watcher.onDidDelete(this._handleDelete, this)); } catch (e) { - this._adapter.log.error(inspect([e, this])); + this._allTest.log.error(inspect([e, this])); } } else { fileUris.push(absPatternAsUri); @@ -83,19 +80,24 @@ export class C2ExecutableInfo implements vscode.Disposable { for (let i = 0; i < fileUris.length; i++) { const file = fileUris[i]; if (await this._verifyIsCatch2TestExecutable(file.fsPath)) { - this._addFile(file); + const suite = this._addFile(file); + this._executables.set(file.fsPath, suite); } } this._uniquifySuiteNames(); for (const suite of this._executables.values()) { - await suite.reloadChildren(); + await suite.reloadChildren().catch((err: any) => { + this._allTest.log.error( + 'Couldn\'t load suite: ' + inspect([err, suite])); + // we could remove it, but now the user still sees the dead leaf + }); } } private _addFile(file: vscode.Uri) { - const wsUri = this._adapter.workspaceFolder.uri; + const wsUri = this._allTest.workspaceFolder.uri; let resolvedName = this.name; let resolvedCwd = this.cwd; @@ -112,7 +114,7 @@ export class C2ExecutableInfo implements vscode.Disposable { const base3Filename = path.basename(base2Filename, ext3Filename); const varToValue: [string, string][] = [ - ...this._adapter.variableToValue, + ...this._allTest.variableToValue, ['${absPath}', file.fsPath], ['${relPath}', relPath], ['${absDirpath}', path.dirname(file.fsPath)], @@ -129,14 +131,12 @@ export class C2ExecutableInfo implements vscode.Disposable { resolvedCwd = path.normalize(resolveVariables(this.cwd, varToValue)); resolvedEnv = resolveVariables(this.env, varToValue); } catch (e) { - this._adapter.log.error(inspect([e, this])); + this._allTest.log.error(inspect([e, this])); } - const suite = this._allTests.createChildSuite( + const suite = this._allTest.createChildSuite( resolvedName, file.fsPath, {cwd: resolvedCwd, env: resolvedEnv}); - this._executables.set(file.fsPath, suite); - return suite; } @@ -145,6 +145,7 @@ export class C2ExecutableInfo implements vscode.Disposable { if (suite == undefined) { suite = this._addFile(uri); + this._executables.set(uri.fsPath, suite); this._uniquifySuiteNames(); } @@ -160,31 +161,31 @@ export class C2ExecutableInfo implements vscode.Disposable { (exists: boolean, timeout: number, delay: number): Promise => { let lastEventArrivedAt = this._lastEventArrivedAt.get(uri.fsPath); if (lastEventArrivedAt === undefined) { - this._adapter.log.error('assert in ' + __filename); + this._allTest.log.error('assert in ' + __filename); debugger; return Promise.resolve(); } if (Date.now() - lastEventArrivedAt! > timeout) { this._lastEventArrivedAt.delete(uri.fsPath); this._executables.delete(uri.fsPath); - this._adapter.testsEmitter.fire({type: 'started'}); - this._allTests.removeChild(suite!); - this._adapter.testsEmitter.fire( - {type: 'finished', suite: this._allTests}); + this._allTest.testsEmitter.fire({type: 'started'}); + this._allTest.removeChild(suite!); + this._allTest.testsEmitter.fire( + {type: 'finished', suite: this._allTest}); return Promise.resolve(); } else if (exists) { - return this._adapter.queue.then(() => { - this._adapter.testsEmitter.fire({type: 'started'}); + return Promise.resolve().then(() => { + this._allTest.testsEmitter.fire({type: 'started'}); return suite!.reloadChildren().then( () => { - this._adapter.testsEmitter.fire( - {type: 'finished', suite: this._allTests}); + this._allTest.testsEmitter.fire( + {type: 'finished', suite: this._allTest}); this._lastEventArrivedAt.delete(uri.fsPath); }, (err: any) => { - this._adapter.testsEmitter.fire( - {type: 'finished', suite: this._allTests}); - this._adapter.log.warn(inspect(err)); + this._allTest.testsEmitter.fire( + {type: 'finished', suite: this._allTest}); + this._allTest.log.warn(inspect(err)); return x(false, timeout, Math.min(delay * 2, 2000)); }); }); @@ -197,7 +198,7 @@ export class C2ExecutableInfo implements vscode.Disposable { }; // change event can arrive during debug session on osx (why?) // if (!this.isDebugging) { - x(false, this._adapter.getExecWatchTimeout(), 64); + x(false, this._allTest.execWatchTimeout, 64); } private _handleCreate(uri: vscode.Uri) { @@ -213,7 +214,7 @@ export class C2ExecutableInfo implements vscode.Disposable { } private _uniquifySuiteNames() { - const uniqueNames: Map = new Map(); + const uniqueNames: Map = new Map(); for (const suite of this._executables.values()) { const suites = uniqueNames.get(suite.origLabel); @@ -240,7 +241,7 @@ export class C2ExecutableInfo implements vscode.Disposable { return res.stdout.indexOf('Catch v2.') != -1; }) .catch(e => { - this._adapter.log.error(inspect(e)); + this._allTest.log.error(inspect(e)); return false; }); } diff --git a/src/C2TestAdapter.ts b/src/C2TestAdapter.ts index 26f2efc2..8b2fe567 100644 --- a/src/C2TestAdapter.ts +++ b/src/C2TestAdapter.ts @@ -12,136 +12,141 @@ import {C2AllTestSuiteInfo} from './C2AllTestSuiteInfo'; import {C2ExecutableInfo} from './C2ExecutableInfo'; import {C2TestInfo} from './C2TestInfo'; import {resolveVariables} from './Helpers'; -import {QueueGraphNode} from './QueueGraph'; export class C2TestAdapter implements TestAdapter, vscode.Disposable { - readonly testsEmitter = + private readonly _testsEmitter = new vscode.EventEmitter(); - readonly testStatesEmitter = + private readonly _testStatesEmitter = new vscode.EventEmitter(); - private readonly autorunEmitter = new vscode.EventEmitter(); + private readonly _autorunEmitter = new vscode.EventEmitter(); - readonly variableToValue: [string, string][] = [ - ['${workspaceDirectory}', this.workspaceFolder.uri.fsPath], - ['${workspaceFolder}', this.workspaceFolder.uri.fsPath] + private readonly _variableToValue: [string, string][] = [ + ['${workspaceDirectory}', this._workspaceFolder.uri.fsPath], + ['${workspaceFolder}', this._workspaceFolder.uri.fsPath] ]; - private allTests: C2AllTestSuiteInfo; - private isEnabledSourceDecoration = true; + private _isDebugging: boolean = false; + private _isRunning: number = 0; - readonly queue: QueueGraphNode = new QueueGraphNode(); - private readonly disposables: Array = new Array(); + private _allTests: C2AllTestSuiteInfo; + private readonly _disposables: vscode.Disposable[] = []; constructor( - public readonly workspaceFolder: vscode.WorkspaceFolder, - public readonly log: util.Log) { - this.disposables.push(this.testsEmitter); - this.disposables.push(this.testStatesEmitter); - this.disposables.push(this.autorunEmitter); - - this.disposables.push( + private readonly _workspaceFolder: vscode.WorkspaceFolder, + private readonly _log: util.Log, + ) { + this._disposables.push(this._testsEmitter); + this._disposables.push(this._testStatesEmitter); + this._disposables.push(this._autorunEmitter); + + this._disposables.push( vscode.workspace.onDidChangeConfiguration(configChange => { if (configChange.affectsConfiguration( - 'catch2TestExplorer.defaultEnv', this.workspaceFolder.uri) || + 'catch2TestExplorer.defaultEnv', this._workspaceFolder.uri) || configChange.affectsConfiguration( - 'catch2TestExplorer.defaultCwd', this.workspaceFolder.uri) || + 'catch2TestExplorer.defaultCwd', this._workspaceFolder.uri) || configChange.affectsConfiguration( - 'catch2TestExplorer.executables', this.workspaceFolder.uri)) { + 'catch2TestExplorer.executables', + this._workspaceFolder.uri)) { this.load(); } })); - this.disposables.push( + this._disposables.push( vscode.workspace.onDidChangeConfiguration(configChange => { if (configChange.affectsConfiguration( 'catch2TestExplorer.enableSourceDecoration', - this.workspaceFolder.uri)) { - this.isEnabledSourceDecoration = - this.getEnableSourceDecoration(this.getConfiguration()); + this._workspaceFolder.uri)) { + this._allTests.isEnabledSourceDecoration = + this._getEnableSourceDecoration(this._getConfiguration()); } if (configChange.affectsConfiguration( 'catch2TestExplorer.defaultRngSeed', - this.workspaceFolder.uri)) { - this.autorunEmitter.fire(); + this._workspaceFolder.uri)) { + this._allTests.rngSeed = + this._getDefaultRngSeed(this._getConfiguration()); + this._autorunEmitter.fire(); + } + if (configChange.affectsConfiguration( + 'catch2TestExplorer.defaultWatchTimeoutSec', + this._workspaceFolder.uri)) { + this._allTests.execWatchTimeout = + this._getDefaultExecWatchTimeout(this._getConfiguration()); } })); - this.allTests = new C2AllTestSuiteInfo(this); + const config = this._getConfiguration(); + this._allTests = new C2AllTestSuiteInfo( + this._log, this._workspaceFolder, this._testsEmitter, + this._testStatesEmitter, this._variableToValue, + this._getEnableSourceDecoration(config), + this._getDefaultRngSeed(config), + this._getDefaultExecWatchTimeout(config)); } dispose() { - this.disposables.forEach(d => { + this._disposables.forEach(d => { d.dispose(); }); - this.allTests.dispose(); - this.log.dispose(); + this._allTests.dispose(); + this._log.dispose(); } get testStates(): vscode.Event { - return this.testStatesEmitter.event; + return this._testStatesEmitter.event; } get tests(): vscode.Event { - return this.testsEmitter.event; + return this._testsEmitter.event; } get autorun(): vscode.Event { - return this.autorunEmitter.event; + return this._autorunEmitter.event; } - getIsEnabledSourceDecoration(): boolean { - return this.isEnabledSourceDecoration; - } - - getRngSeed(): string|number|null { - return this.getDefaultRngSeed(this.getConfiguration()); - } - - getExecWatchTimeout(): number { - return this.getDefaultExecWatchTimeout(this.getConfiguration()); - } - - private isDebugging: boolean = false; - private isRunning: number = 0; - async load(): Promise { try { this.cancel(); + const config = this._getConfiguration(); - this.allTests.dispose(); - this.allTests = new C2AllTestSuiteInfo(this); + this._allTests.dispose(); - this.testsEmitter.fire({type: 'started'}); + this._allTests = new C2AllTestSuiteInfo( + this._log, this._workspaceFolder, this._testsEmitter, + this._testStatesEmitter, this._variableToValue, + this._getEnableSourceDecoration(config), + this._getDefaultRngSeed(config), + this._getDefaultExecWatchTimeout(config)); - const config = this.getConfiguration(); + this._testsEmitter.fire({type: 'started'}); - await this.allTests.load(this.getExecutables(config, this.allTests)); + await this._allTests.load(this._getExecutables(config, this._allTests)); - this.testsEmitter.fire({type: 'finished', suite: this.allTests}); + this._testsEmitter.fire({type: 'finished', suite: this._allTests}); } catch (e) { - this.testsEmitter.fire( + this._testsEmitter.fire( {type: 'finished', suite: undefined, errorMessage: e.message}); } } cancel(): void { - this.allTests.cancel(); + this._allTests.cancel(); } run(tests: string[]): Promise { - if (this.isDebugging) { + if (this._isDebugging) { throw 'Catch2: Test is currently being debugged.'; } - if (this.isRunning == 0) { - this.isRunning += 1; + if (this._isRunning == 0) { + this._isRunning += 1; const always = () => { - this.isRunning -= 1; + this._isRunning -= 1; }; - return this.allTests - .run(tests, this.getWorkerMaxNumber(this.getConfiguration())) + return this._allTests + .run(tests, this._getWorkerMaxNumber(this._getConfiguration())) .then(always, always); } @@ -149,16 +154,16 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { } async debug(tests: string[]): Promise { - if (this.isDebugging) { + if (this._isDebugging) { throw 'Catch2: Test is currently being debugged.'; } - if (this.isRunning > 0) { + if (this._isRunning > 0) { throw 'Catch2: Test(s) are currently being run.'; } console.assert(tests.length === 1); - const info = this.allTests.findChildById(tests[0]); + const info = this._allTests.findChildById(tests[0]); console.assert(info !== undefined); if (!(info instanceof C2TestInfo)) { @@ -175,16 +180,17 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { }; const template = - this.getDebugConfigurationTemplate(this.getConfiguration()); - let resolveDebugVariables: [string, any][] = this.variableToValue; + this._getDebugConfigurationTemplate(this._getConfiguration()); + let resolveDebugVariables: [string, any][] = this._variableToValue; + const args = + [testInfo.getEscapedTestName(), '--reporter', 'console', '--break']; + resolveDebugVariables = resolveDebugVariables.concat([ - ['${label}', testInfo.label], ['${exec}', testInfo.parent.execPath], - [ - '${args}', - [testInfo.getEscapedTestName(), '--reporter', 'console', '--break'] - ], + ['${label}', testInfo.label], + ['${exec}', testInfo.parent.execPath], + ['${args}', args], ['${cwd}', testInfo.parent.execOptions.cwd!], - ['${envObj}', testInfo.parent.execOptions.env!] + ['${envObj}', testInfo.parent.execOptions.env!], ]); if (template !== null) { @@ -194,11 +200,21 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { debug[prop] = resolveVariables(val, resolveDebugVariables); } } - return debug; - } else { - // lets try to recognise existing extensions - vscode.extensions.getExtension(''); + } else if (vscode.extensions.getExtension('vadimcn.vscode-lldb')) { + debug['type'] = 'lldb'; + debug['program'] = testInfo.parent.execPath; + debug['args'] = args; + debug['cwd'] = testInfo.parent.execOptions.cwd!; + debug['env'] = testInfo.parent.execOptions.env!; + return debug; + } else if (vscode.extensions.getExtension('ms-vscode.cpptools')) { + debug['type'] = 'cppvsdbg'; + debug['program'] = testInfo.parent.execPath; + debug['args'] = args; + debug['cwd'] = testInfo.parent.execOptions.cwd!; + debug['environment'] = [testInfo.parent.execOptions.env!]; + return debug; } throw 'Catch2: For debug \'debugConfigTemplate\' should be set.'; @@ -206,26 +222,26 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { const debugConfig = getDebugConfiguration(); - this.isDebugging = true; + this._isDebugging = true; const debugSessionStarted = - await vscode.debug.startDebugging(this.workspaceFolder, debugConfig); + await vscode.debug.startDebugging(this._workspaceFolder, debugConfig); if (!debugSessionStarted) { console.error('Failed starting the debug session - aborting'); - this.isDebugging = false; + this._isDebugging = false; return; } const currentSession = vscode.debug.activeDebugSession; if (!currentSession) { console.error('No active debug session - aborting'); - this.isDebugging = false; + this._isDebugging = false; return; } const always = () => { - this.isDebugging = false; + this._isDebugging = false; }; await new Promise((resolve, reject) => { @@ -238,12 +254,12 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { }).then(always, always); } - private getConfiguration(): vscode.WorkspaceConfiguration { + private _getConfiguration(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration( - 'catch2TestExplorer', this.workspaceFolder.uri); + 'catch2TestExplorer', this._workspaceFolder.uri); } - private getDebugConfigurationTemplate(config: vscode.WorkspaceConfiguration): + private _getDebugConfigurationTemplate(config: vscode.WorkspaceConfiguration): {[prop: string]: string}|null { const o = config.get('debugConfigTemplate', null); @@ -256,13 +272,13 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { if (val === undefined || val === null) { delete result.prop; } else { - result[prop] = resolveVariables(String(val), this.variableToValue); + result[prop] = resolveVariables(String(val), this._variableToValue); } } return result; } - private getGlobalAndDefaultEnvironmentVariables( + private _getGlobalAndDefaultEnvironmentVariables( config: vscode.WorkspaceConfiguration): {[prop: string]: string|undefined} { const processEnv = process.env; @@ -275,38 +291,39 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { if (val === undefined || val === null) { delete resultEnv.prop; } else { - resultEnv[prop] = resolveVariables(String(val), this.variableToValue); + resultEnv[prop] = resolveVariables(String(val), this._variableToValue); } } return resultEnv; } - private getDefaultCwd(config: vscode.WorkspaceConfiguration): string { - const dirname = this.workspaceFolder.uri.fsPath; + private _getDefaultCwd(config: vscode.WorkspaceConfiguration): string { + const dirname = this._workspaceFolder.uri.fsPath; const cwd = resolveVariables( - config.get('defaultCwd', dirname), this.variableToValue); + config.get('defaultCwd', dirname), this._variableToValue); if (path.isAbsolute(cwd)) { return cwd; } else { - return this.resolveRelPath(cwd); + return path.resolve(this._workspaceFolder.uri.fsPath, cwd); } } - private getDefaultRngSeed(config: vscode.WorkspaceConfiguration): string + private _getDefaultRngSeed(config: vscode.WorkspaceConfiguration): string |number|null { return config.get('defaultRngSeed', null); } - getDefaultExecWatchTimeout(config: vscode.WorkspaceConfiguration): number { - return config.get('defaultWatchTimeoutSec', 10) * 1000; + private _getWorkerMaxNumber(config: vscode.WorkspaceConfiguration): number { + return config.get('workerMaxNumber', 4); } - private getWorkerMaxNumber(config: vscode.WorkspaceConfiguration): number { - return config.get('workerMaxNumber', 4); + private _getDefaultExecWatchTimeout(config: vscode.WorkspaceConfiguration): + number { + return config.get('defaultWatchTimeoutSec', 10) * 1000; } - private getGlobalAndCurrentEnvironmentVariables( + private _getGlobalAndCurrentEnvironmentVariables( config: vscode.WorkspaceConfiguration, configEnv: {[prop: string]: any}): {[prop: string]: any} { const processEnv = process.env; @@ -317,22 +334,22 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { if (val === undefined || val === null) { delete resultEnv.prop; } else { - resultEnv[prop] = resolveVariables(String(val), this.variableToValue); + resultEnv[prop] = resolveVariables(String(val), this._variableToValue); } } return resultEnv; } - private getEnableSourceDecoration(config: vscode.WorkspaceConfiguration): + private _getEnableSourceDecoration(config: vscode.WorkspaceConfiguration): boolean { return config.get('enableSourceDecoration', true); } - private getExecutables( + private _getExecutables( config: vscode.WorkspaceConfiguration, allTests: C2AllTestSuiteInfo): C2ExecutableInfo[] { - const globalWorkingDirectory = this.getDefaultCwd(config); + const globalWorkingDirectory = this._getDefaultCwd(config); let executables: C2ExecutableInfo[] = []; @@ -355,17 +372,16 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { obj.hasOwnProperty('cwd') ? obj.cwd : globalWorkingDirectory; const env: {[prop: string]: any} = obj.hasOwnProperty('env') ? - this.getGlobalAndCurrentEnvironmentVariables(config, obj.env) : - this.getGlobalAndDefaultEnvironmentVariables(config); + this._getGlobalAndCurrentEnvironmentVariables(config, obj.env) : + this._getGlobalAndDefaultEnvironmentVariables(config); - return new C2ExecutableInfo(this, allTests, name, pattern, cwd, env); + return new C2ExecutableInfo(allTests, name, pattern, cwd, env); }; if (typeof configExecs === 'string') { if (configExecs.length == 0) return []; executables.push(new C2ExecutableInfo( - this, allTests, configExecs, configExecs, globalWorkingDirectory, - {})); + allTests, configExecs, configExecs, globalWorkingDirectory, {})); } else if (Array.isArray(configExecs)) { for (var i = 0; i < configExecs.length; ++i) { const configExe = configExecs[i]; @@ -373,14 +389,14 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { const configExecsName = String(configExe); if (configExecsName.length > 0) { executables.push(new C2ExecutableInfo( - this, allTests, configExecsName, configExecsName, + allTests, configExecsName, configExecsName, globalWorkingDirectory, {})); } } else { try { executables.push(createFromObject(configExe)); } catch (e) { - this.log.error(inspect(e)); + this._log.error(inspect(e)); } } } @@ -388,7 +404,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { try { executables.push(createFromObject(configExecs)); } catch (e) { - this.log.error(inspect(e)); + this._log.error(inspect(e)); } } else { throw 'Catch2 config error: wrong type: executables'; @@ -396,9 +412,4 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return executables; } - - private resolveRelPath(relPath: string): string { - const dirname = this.workspaceFolder.uri.fsPath; - return path.resolve(dirname, relPath); - } } diff --git a/src/C2TestInfo.ts b/src/C2TestInfo.ts index 79a176cb..b63090f6 100644 --- a/src/C2TestInfo.ts +++ b/src/C2TestInfo.ts @@ -16,16 +16,21 @@ export class C2TestInfo implements TestInfo { readonly testNameTrimmed: string; constructor( - public readonly testNameFull: string, description: string, tags: string[], - public readonly file: string, public readonly line: number, - public readonly parent: C2TestSuiteInfo) { + id: string|undefined, + public readonly testNameFull: string, + description: string, + tags: string[], + public readonly file: string, + public readonly line: number, + public readonly parent: C2TestSuiteInfo, + ) { this.testNameTrimmed = this.testNameFull.trim(); this.skipped = tags.some((v: string) => { return v.startsWith('[.') || v == '[hide]'; }) || this.testNameFull.startsWith('./'); this.label = C2TestInfo.generateLabel(this.testNameFull, description, tags); - this.id = generateUniqueId(); + this.id = id ? id : generateUniqueId(); } static generateLabel( diff --git a/src/C2TestSuiteInfo.ts b/src/C2TestSuiteInfo.ts index 75d030ba..c949adfe 100644 --- a/src/C2TestSuiteInfo.ts +++ b/src/C2TestSuiteInfo.ts @@ -8,7 +8,7 @@ import {inspect} from 'util'; import {TestEvent, TestSuiteEvent, TestSuiteInfo} from 'vscode-test-adapter-api'; import * as xml2js from 'xml2js'; -import {C2TestAdapter} from './C2TestAdapter'; +import {C2AllTestSuiteInfo} from './C2AllTestSuiteInfo'; import {C2TestInfo} from './C2TestInfo'; import * as c2fs from './FsWrapper'; import {generateUniqueId} from './IdGenerator'; @@ -27,17 +27,18 @@ export class C2TestSuiteInfo implements TestSuiteInfo { constructor( public readonly origLabel: string, - private readonly adapter: C2TestAdapter, public readonly execPath: string, + private readonly allTests: C2AllTestSuiteInfo, + public readonly execPath: string, public readonly execOptions: SpawnOptions) { this.label = origLabel; this.id = generateUniqueId(); } createChildTest( - testName: string, description: string, tags: string[], file: string, - line: number): C2TestInfo { + id: string|undefined, testName: string, description: string, + tags: string[], file: string, line: number): C2TestInfo { const test = - new C2TestInfo(testName, description, tags, file, line - 1, this); + new C2TestInfo(id, testName, description, tags, file, line - 1, this); if (this.children.length == 0) { this.file = file; @@ -96,7 +97,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { }); } - this.adapter.testStatesEmitter.fire( + this.allTests.testStatesEmitter.fire( {type: 'suite', suite: this, state: 'running'}); const execParams: string[] = []; @@ -112,8 +113,8 @@ export class C2TestSuiteInfo implements TestSuiteInfo { for (let i = 0; i < this.children.length; i++) { const c = this.children[i]; if (c.skipped) { - this.adapter.testStatesEmitter.fire(c.getStartEvent()); - this.adapter.testStatesEmitter.fire(c.getSkippedEvent()); + this.allTests.testStatesEmitter.fire(c.getStartEvent()); + this.allTests.testStatesEmitter.fire(c.getSkippedEvent()); } } } @@ -122,7 +123,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { execParams.push('--durations') execParams.push('yes'); { - const rng = this.adapter.getRngSeed(); + const rng = this.allTests.rngSeed; if (rng != null) { execParams.push('--rng-seed') execParams.push(rng.toString()); @@ -148,7 +149,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { const processChunk = (chunk: string) => { data.buffer = data.buffer + chunk; - let invariant = 9999; + let invariant = 99999; do { if (!data.inTestCase) { const b = data.buffer.indexOf('', (err: any, result: any) => { if (err) { - this.adapter.log.error(err.toString()); + this.allTests.log.error(err.toString()); throw err; } else { name = result.TestCase.$.name; @@ -186,9 +187,9 @@ export class C2TestSuiteInfo implements TestSuiteInfo { if (data.currentChild !== undefined) { const ev = data.currentChild.getStartEvent(); - this.adapter.testStatesEmitter.fire(ev); + this.allTests.testStatesEmitter.fire(ev); } else { - this.adapter.log.error('TestCase not found in children: ' + name); + this.allTests.log.error('TestCase not found in children: ' + name); } data.buffer = data.buffer.substr(b); @@ -202,11 +203,11 @@ export class C2TestSuiteInfo implements TestSuiteInfo { const ev: TestEvent = data.currentChild.parseAndProcessTestCase( data.buffer.substring(0, b + endTestCase.length), data.rngSeed); - if (!this.adapter.getIsEnabledSourceDecoration()) + if (!this.allTests.isEnabledSourceDecoration) ev.decorations = undefined; - this.adapter.testStatesEmitter.fire(ev); + this.allTests.testStatesEmitter.fire(ev); } catch (e) { - this.adapter.log.error( + this.allTests.log.error( 'Parsing and processing test: ' + data.currentChild.label); } } @@ -238,7 +239,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { const suiteFinally = () => { this.proc = undefined; taskPool.release(); - this.adapter.testStatesEmitter.fire( + this.allTests.testStatesEmitter.fire( {type: 'suite', suite: this, state: 'completed'}); }; return p.then( @@ -247,7 +248,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { }, (err: Error) => { if (data.inTestCase) { - this.adapter.testStatesEmitter.fire({ + this.allTests.testStatesEmitter.fire({ type: 'test', test: data.currentChild!, state: 'failed', @@ -256,7 +257,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { } suiteFinally(); try { - this.adapter.log.error(err.message); + this.allTests.log.error(err.message); } catch (e) { } }); @@ -279,14 +280,14 @@ export class C2TestSuiteInfo implements TestSuiteInfo { let lines = r.stdout.split(/\r?\n/); - if (lines.length == 0) this.adapter.log.error('Empty test list.'); + if (lines.length == 0) this.allTests.log.error('Empty test list.'); while (lines[lines.length - 1].trim().length == 0) lines.pop(); let i = 1; while (i < lines.length - 1) { if (lines[i][0] != ' ') - this.adapter.log.error( + this.allTests.log.error( 'Wrong test list output format: ' + lines.toString()); const testNameFull = lines[i++].substr(2); @@ -321,16 +322,17 @@ export class C2TestSuiteInfo implements TestSuiteInfo { } const index = oldChildren.findIndex( - (c: C2TestInfo): boolean => {return c.testNameFull == - testNameFull}); + (c: C2TestInfo) => {return c.testNameFull == testNameFull}); if (index != -1 && oldChildren[index].label == C2TestInfo.generateLabel( testNameFull, description, tags)) { - this.children.push(oldChildren[index]); + this.createChildTest( + oldChildren[index].id, testNameFull, description, tags, + filePath, line); } else { this.createChildTest( - testNameFull, description, tags, filePath, line); + undefined, testNameFull, description, tags, filePath, line); } } }); diff --git a/src/QueueGraph.ts b/src/QueueGraph.ts index eda50f2b..c0865170 100644 --- a/src/QueueGraph.ts +++ b/src/QueueGraph.ts @@ -25,16 +25,24 @@ export class QueueGraphNode { this._count++; const previous = this._queue; - const current = - Promise.all(this._depends.map(v => v._queue)) - .then(() => { - return previous.then(task); - }) - .catch(taskErrorHandler ? taskErrorHandler : this._handleError) - .then((value: TResult1|PromiseLike) => { - this._count--; - return value; - }); + const current = Promise.all(this._depends.map(v => v._queue)) + .then(() => { + return previous.then(task); + }) + .then( + (value: TResult1|PromiseLike) => { + this._count--; + return value; + }, + (reason: any) => { + this._count--; + if (taskErrorHandler) + return taskErrorHandler(reason); + else if (this._handleError) + return this._handleError(reason); + else + throw reason; + }); this._queue = current.then(() => {}); @@ -45,7 +53,7 @@ export class QueueGraphNode { for (const dep of depends) { this._depends.push(dep); } - // TODO check recursion + // TODO check circular dependency } private _count: number = 0; diff --git a/src/test/C2TestAdapter.cpp.test.ts b/src/test/C2TestAdapter.cpp.test.ts index 9e748bcf..c3a26c0c 100644 --- a/src/test/C2TestAdapter.cpp.test.ts +++ b/src/test/C2TestAdapter.cpp.test.ts @@ -78,9 +78,6 @@ describe('C2TestAdapter.cpp', function() { before(async function() { this.timeout(82000); - await fse.remove(cppUri.fsPath); - await fse.mkdirp(cppUri.fsPath); - if (!await c2fs.existsAsync(inCpp('../suite1.exe').fsPath)) await compile( inCpp('../../../src/test/cpp/suite1.cpp'), inCpp('../suite1.exe')); @@ -94,6 +91,11 @@ describe('C2TestAdapter.cpp', function() { inCpp('../../../src/test/cpp/suite3.cpp'), inCpp('../suite3.exe')); }) + beforeEach(async function() { + await fse.remove(cppUri.fsPath); + await fse.mkdirp(cppUri.fsPath); + }) + after(async function() { await fse.remove(cppUri.fsPath); }) diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index 2618f266..3def6fc3 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -167,21 +167,7 @@ describe('C2TestAdapter', function() { testsEvents = []; } - function stubsResetToMyDefault() { - spawnStub.reset(); - spawnStub.callThrough(); - vsfsWatchStub.reset(); - vsfsWatchStub.callThrough(); - fsStatStub.reset(); - fsStatStub.callThrough(); - vsFindFilesStub.reset(); - vsFindFilesStub.callThrough(); - } - before(function() { - fse.removeSync(dotVscodePath); - adapter = undefined; - spawnStub = sinonSandbox.stub(child_process, 'spawn').named('spawnStub'); vsfsWatchStub = sinonSandbox.stub(vscode.workspace, 'createFileSystemWatcher') @@ -189,33 +175,44 @@ describe('C2TestAdapter', function() { fsStatStub = sinonSandbox.stub(fs, 'stat').named('fsStat'); vsFindFilesStub = sinonSandbox.stub(vscode.workspace, 'findFiles') .named('vsFindFilesStub'); + }) - stubsResetToMyDefault(); + after(function() { + sinonSandbox.restore(); + }) + + beforeEach(function reset() { + adapter = undefined; + + fse.removeSync(dotVscodePath); + + spawnStub.reset(); + spawnStub.callThrough(); + vsfsWatchStub.reset(); + vsfsWatchStub.callThrough(); + fsStatStub.reset(); + fsStatStub.callThrough(); + vsFindFilesStub.reset(); + vsFindFilesStub.callThrough(); // reset config can cause problem with fse.removeSync(dotVscodePath); return resetConfig(); - }); + }) - after(function() { + afterEach(async function() { disposeAdapterAndSubscribers(); - sinonSandbox.restore(); - }); + }) describe('detect config change', function() { this.slow(200); let adapter: C2TestAdapter; - before(function() { + beforeEach(function() { adapter = createAdapterAndSubscribe(); assert.deepStrictEqual(testsEvents, []); }) - after(function() { - disposeAdapterAndSubscribers(); - return resetConfig(); - }) - it('defaultEnv', function() { return doAndWaitForReloadEvent(this, () => { return updateConfig('defaultEnv', {'APPLE': 'apple'}); @@ -226,17 +223,23 @@ describe('C2TestAdapter', function() { return doAndWaitForReloadEvent(this, () => { return updateConfig('defaultCwd', 'apple/peach'); }); - }); + }) it('enableSourceDecoration', function() { return updateConfig('enableSourceDecoration', false).then(function() { - assert.ok(!adapter.getIsEnabledSourceDecoration()); + assert.ok(!(adapter)._allTests.isEnabledSourceDecoration); }); - }); + }) it('defaultRngSeed', function() { return updateConfig('defaultRngSeed', 987).then(function() { - assert.equal(adapter.getRngSeed(), 987); + assert.equal((adapter)._allTests.rngSeed, 987); + }); + }) + + it('defaultWatchTimeoutSec', function() { + return updateConfig('defaultWatchTimeoutSec', 9876).then(function() { + assert.equal((adapter)._allTests.execWatchTimeout, 9876000); }); }) }) @@ -251,7 +254,6 @@ describe('C2TestAdapter', function() { const suite = (testsEvents[1]).suite; assert.notStrictEqual(suite, undefined); assert.equal(suite!.children.length, 0); - disposeAdapterAndSubscribers(); }) context('example1', function() { @@ -305,7 +307,7 @@ describe('C2TestAdapter', function() { }); } - before(function() { + beforeEach(function() { for (let suite of example1.outputs) { for (let scenario of suite[1]) { spawnStub.withArgs(suite[0], scenario[0]).callsFake(function() { @@ -338,15 +340,11 @@ describe('C2TestAdapter', function() { vsFindFilesStub.withArgs(matchRelativePattern(p.fsPath)).returns([p]); } }); - }); - - after(function() { - stubsResetToMyDefault(); - }); + }) afterEach(function() { watchers.clear(); - }); + }) describe('load', function() { const uniqueIdC = new Set(); @@ -361,15 +359,7 @@ describe('C2TestAdapter', function() { let s2t2: TestInfo|any; let s2t3: TestInfo|any; - before(function() { - return updateConfig('workerMaxNumber', 4); - }); - - after(function() { - return updateConfig('workerMaxNumber', undefined); - }); - - beforeEach(async function() { + async function loadAdapter() { adapter = createAdapterAndSubscribe(); await adapter.load(); @@ -390,23 +380,23 @@ describe('C2TestAdapter', function() { example1.assertWithoutChildren(root, uniqueIdC); assert.deepStrictEqual(testStatesEvents, []); - }); + } + + beforeEach(function() { + return updateConfig('workerMaxNumber', 4); + }) afterEach(function() { uniqueIdC.clear(); - disposeAdapterAndSubscribers(); }); context('executables="execPath1"', function() { - before(function() { + beforeEach(function() { return updateConfig('executables', 'execPath1'); }) - after(function() { - return updateConfig('executables', undefined); - }) - - beforeEach(async function() { + async function loadAdapterAndAssert() { + await loadAdapter(); assert.deepStrictEqual( getConfig().get('executables'), 'execPath1'); assert.equal(root.children.length, 1); @@ -419,9 +409,10 @@ describe('C2TestAdapter', function() { s1t1 = suite1.children[0]; assert.equal(suite1.children[1].type, 'test'); s1t2 = suite1.children[1]; - }) + } it('should run with not existing test id', async function() { + await loadAdapterAndAssert(); await adapter.run(['not existing id']); assert.deepStrictEqual(testStatesEvents, [ @@ -430,6 +421,7 @@ describe('C2TestAdapter', function() { }) it('should run s1t1 with success', async function() { + await loadAdapterAndAssert(); assert.equal(getConfig().get('executables'), 'execPath1'); await adapter.run([s1t1.id]); const expected = [ @@ -453,6 +445,7 @@ describe('C2TestAdapter', function() { }) it('should run suite1', async function() { + await loadAdapterAndAssert(); await adapter.run([suite1.id]); const expected = [ {type: 'started', tests: [suite1.id]}, @@ -484,6 +477,7 @@ describe('C2TestAdapter', function() { }) it('should run all', async function() { + await loadAdapterAndAssert(); await adapter.run([root.id]); const expected = [ {type: 'started', tests: [root.id]}, @@ -515,6 +509,7 @@ describe('C2TestAdapter', function() { }) it('cancels without any problem', async function() { + await loadAdapterAndAssert(); adapter.cancel(); assert.deepStrictEqual(testsEvents, []); assert.deepStrictEqual(testStatesEvents, []); @@ -545,15 +540,12 @@ describe('C2TestAdapter', function() { }) context('with config: defaultRngSeed=2', function() { - before(function() { + beforeEach(function() { return updateConfig('defaultRngSeed', 2); - }); - - after(function() { - return updateConfig('defaultRngSeed', undefined); - }); + }) it('should run s1t1 with success', async function() { + await loadAdapterAndAssert(); await adapter.run([s1t1.id]); const expected = [ {type: 'started', tests: [s1t1.id]}, @@ -578,8 +570,13 @@ describe('C2TestAdapter', function() { }) }) - context('suite1 and suite2 are used', function() { - beforeEach(function() { + context('executables=["execPath1", "execPath2"]', function() { + this.slow(300); + + let suite1Watcher: FileSystemWatcherStub; + + async function loadAdapterAndAssert() { + await loadAdapter(); assert.equal(root.children.length, 2); assert.equal(root.children[0].type, 'suite'); @@ -606,796 +603,676 @@ describe('C2TestAdapter', function() { s2t2 = suite2.children[1]; assert.equal(suite2.children[2].type, 'test'); s2t3 = suite2.children[2]; - }); - const testsForAdapterWithSuite1AndSuite2: Mocha.Test[] = [ - new Mocha.Test( - 'test variables are fine, suite1 and suite1 are loaded', - function() { - assert.equal(root.children.length, 2); - assert.ok(suite1 != undefined); - assert.ok(s1t1 != undefined); - assert.ok(s1t2 != undefined); - assert.ok(suite2 != undefined); - assert.ok(s2t1 != undefined); - assert.ok(s2t2 != undefined); - assert.ok(s2t3 != undefined); - }), - new Mocha.Test( - 'should run all', - async function() { - assert.equal(root.children.length, 2); - await adapter.run([root.id]); - - const running = {type: 'started', tests: [root.id]}; - - const s1running = { - type: 'suite', - state: 'running', - suite: suite1 - }; - const s1finished = { - type: 'suite', - state: 'completed', - suite: suite1 - }; - assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); - assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); - - const s2running = { - type: 'suite', - state: 'running', - suite: suite2 - }; - const s2finished = { - type: 'suite', - state: 'completed', - suite: suite2 - }; - assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); - - const s1t1running = { - type: 'test', - state: 'running', - test: s1t1 - }; - assert.ok( - testStatesEvI(s1running) < testStatesEvI(s1t1running)); - - const s1t1finished = { - type: 'test', - state: 'passed', - test: s1t1, - decorations: undefined, - message: 'Duration: 0.000132 second(s)\n' - }; - assert.ok( - testStatesEvI(s1t1running) < testStatesEvI(s1t1finished)); - assert.ok( - testStatesEvI(s1t1finished) < testStatesEvI(s1finished)); + assert.equal(watchers.size, 2); + assert.ok(watchers.has(example1.suite1.execPath)); + suite1Watcher = watchers.get(example1.suite1.execPath)!; - const s1t2running = { - type: 'test', - state: 'running', - test: s1t2 - }; - assert.ok( - testStatesEvI(s1running) < testStatesEvI(s1t2running)); - - const s1t2finished = { - type: 'test', - state: 'failed', - test: s1t2, - decorations: [{line: 14, message: 'Expanded: false'}], - message: - 'Duration: 0.000204 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' - }; - assert.ok( - testStatesEvI(s1t2running) < testStatesEvI(s1t2finished)); - assert.ok( - testStatesEvI(s1t2finished) < testStatesEvI(s1finished)); + example1.suite1.assert( + 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); - const s2t1running = { - type: 'test', - state: 'running', - test: s2t1 - }; - assert.ok( - testStatesEvI(s2running) < testStatesEvI(s2t1running)); - - const s2t1finished = { - type: 'test', - state: 'passed', - test: s2t1, - decorations: undefined, - message: 'Duration: 0.00037 second(s)\n' - }; - assert.ok( - testStatesEvI(s2t1running) < testStatesEvI(s2t1finished)); - assert.ok( - testStatesEvI(s2t1finished) < testStatesEvI(s2finished)); + example1.suite2.assert( + 'execPath2', ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); + } - const s2t2running = { - type: 'test', - state: 'running', - test: s2t2 - }; - assert.ok( - testStatesEvI(s2running) < testStatesEvI(s2t2running)); + beforeEach(function() { + return updateConfig('executables', ['execPath1', 'execPath2']); + }) - const s2t2finished = { - type: 'test', - state: 'skipped', - test: s2t2 - }; - assert.ok( - testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); - assert.ok( - testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); + it('test variables are fine, suite1 and suite1 are loaded', + async function() { + await loadAdapterAndAssert(); + assert.equal(root.children.length, 2); + assert.ok(suite1 != undefined); + assert.ok(s1t1 != undefined); + assert.ok(s1t2 != undefined); + assert.ok(suite2 != undefined); + assert.ok(s2t1 != undefined); + assert.ok(s2t2 != undefined); + assert.ok(s2t3 != undefined); + }) - const s2t3running = { - type: 'test', - state: 'running', - test: s2t3 - }; - assert.ok( - testStatesEvI(s2running) < testStatesEvI(s2t3running)); - - const s2t3finished = { - type: 'test', - state: 'failed', - test: s2t3, - decorations: [{line: 20, message: 'Expanded: false'}], - message: - 'Duration: 0.000178 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' - }; - assert.ok( - testStatesEvI(s2t3running) < testStatesEvI(s2t3finished)); - assert.ok( - testStatesEvI(s2t3finished) < testStatesEvI(s2finished)); - - const finished = {type: 'finished'}; - assert.ok(testStatesEvI(s1finished) < testStatesEvI(finished)); - assert.ok(testStatesEvI(s2finished) < testStatesEvI(finished)); - - assert.equal( - testStatesEvents.length, 16, inspect(testStatesEvents)); - }), - new Mocha.Test( - 'should run with not existing test id', - async function() { - await adapter.run(['not existing id']); - - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: ['not existing id']}, - {type: 'finished'} - ]); - }), - new Mocha.Test( - 'should run s1t1', - async function() { - await adapter.run([s1t1.id]); - const expected = [ - {type: 'started', tests: [s1t1.id]}, - {type: 'suite', state: 'running', suite: suite1}, - {type: 'test', state: 'running', test: s1t1}, { - type: 'test', - state: 'passed', - test: s1t1, - decorations: undefined, - message: 'Duration: 0.000112 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: suite1}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([s1t1.id]); - assert.deepStrictEqual( - testStatesEvents, [...expected, ...expected]); - }), - new Mocha.Test( - 'should run skipped s2t2', - async function() { - await adapter.run([s2t2.id]); - const expected = [ - {type: 'started', tests: [s2t2.id]}, - {type: 'suite', state: 'running', suite: suite2}, - {type: 'test', state: 'running', test: s2t2}, { - type: 'test', - state: 'passed', - test: s2t2, - decorations: undefined, - message: 'Duration: 0.001294 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: suite2}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([s2t2.id]); - assert.deepStrictEqual( - testStatesEvents, [...expected, ...expected]); - }), - new Mocha.Test( - 'should run failing test s2t3', - async function() { - await adapter.run([s2t3.id]); - const expected = [ - {type: 'started', tests: [s2t3.id]}, - {type: 'suite', state: 'running', suite: suite2}, - {type: 'test', state: 'running', test: s2t3}, { - type: 'test', - state: 'failed', - test: s2t3, - decorations: [{line: 20, message: 'Expanded: false'}], - message: - 'Duration: 0.000596 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' - }, - {type: 'suite', state: 'completed', suite: suite2}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([s2t3.id]); - assert.deepStrictEqual( - testStatesEvents, [...expected, ...expected]); - }), - new Mocha.Test( - 'should run failing test s2t3 with chunks', - async function() { - const withArgs = spawnStub.withArgs( - example1.suite2.execPath, example1.suite2.t3.outputs[0][0]); - withArgs.onCall(withArgs.callCount) - .returns( - new ChildProcessStub(example1.suite2.t3.outputs[0][1])); - - await adapter.run([s2t3.id]); - const expected = [ - {type: 'started', tests: [s2t3.id]}, - {type: 'suite', state: 'running', suite: suite2}, - {type: 'test', state: 'running', test: s2t3}, { - type: 'test', - state: 'failed', - test: s2t3, - decorations: [{line: 20, message: 'Expanded: false'}], - message: - 'Duration: 0.000596 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' - }, - {type: 'suite', state: 'completed', suite: suite2}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([s2t3.id]); - assert.deepStrictEqual( - testStatesEvents, [...expected, ...expected]); - }), - new Mocha.Test( - 'should run suite1', - async function() { - await adapter.run([suite1.id]); - const expected = [ - {type: 'started', tests: [suite1.id]}, - {type: 'suite', state: 'running', suite: suite1}, - {type: 'test', state: 'running', test: s1t1}, { - type: 'test', - state: 'passed', - test: s1t1, - decorations: undefined, - message: 'Duration: 0.000132 second(s)\n' - }, - {type: 'test', state: 'running', test: s1t2}, { - type: 'test', - state: 'failed', - test: s1t2, - decorations: [{line: 14, message: 'Expanded: false'}], - message: - 'Duration: 0.000204 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' - }, - {type: 'suite', state: 'completed', suite: suite1}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([suite1.id]); - assert.deepStrictEqual( - testStatesEvents, [...expected, ...expected]); - }), - new Mocha.Test( - 'should run with wrong xml', - async function() { - const m = - example1.suite1.t1.outputs[0][1].match(']+>'); - assert.notStrictEqual(m, undefined); - assert.notStrictEqual(m!.input, undefined); - assert.notStrictEqual(m!.index, undefined); - const part = m!.input!.substr(0, m!.index! + m![0].length); - const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.t1.outputs[0][0]); - withArgs.onCall(withArgs.callCount) - .returns(new ChildProcessStub(part)); - - await adapter.run([s1t1.id]); - - const expected = [ - {type: 'started', tests: [s1t1.id]}, - {type: 'suite', state: 'running', suite: suite1}, - {type: 'test', state: 'running', test: s1t1}, { - type: 'test', - state: 'failed', - test: s1t1, - message: 'Unexpected test error. (Is Catch2 crashed?)\n' - }, - {type: 'suite', state: 'completed', suite: suite1}, - {type: 'finished'} - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - // this tests the sinon stubs too - await adapter.run([s1t1.id]); - assert.deepStrictEqual(testStatesEvents, [ - ...expected, {type: 'started', tests: [s1t1.id]}, - {type: 'suite', state: 'running', suite: suite1}, - {type: 'test', state: 'running', test: s1t1}, { - type: 'test', - state: 'passed', - test: s1t1, - decorations: undefined, - message: 'Duration: 0.000112 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: suite1}, - {type: 'finished'} - ]); - }), - new Mocha.Test( - 'should cancel without error', - function() { - adapter.cancel(); - }), - new Mocha.Test( - 'cancel', - async function() { - let spyKill1: sinon.SinonSpy; - let spyKill2: sinon.SinonSpy; - { - const spawnEvent = - new ChildProcessStub(example1.suite1.outputs[2][1]); - spyKill1 = sinon.spy(spawnEvent, 'kill'); - const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.outputs[2][0]); - withArgs.onCall(withArgs.callCount).returns(spawnEvent); - } - { - const spawnEvent = - new ChildProcessStub(example1.suite2.outputs[2][1]); - spyKill2 = sinon.spy(spawnEvent, 'kill'); - const withArgs = spawnStub.withArgs( - example1.suite2.execPath, example1.suite2.outputs[2][0]); - withArgs.onCall(withArgs.callCount).returns(spawnEvent); - } - const run = adapter.run([root.id]); - adapter.cancel(); - await run; - - assert.equal(spyKill1.callCount, 1); - assert.equal(spyKill2.callCount, 1); - - const running = {type: 'started', tests: [root.id]}; - - const s1running = { - type: 'suite', - state: 'running', - suite: suite1 - }; - const s1finished = { - type: 'suite', - state: 'completed', - suite: suite1 - }; - assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); - assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); - - const s2running = { - type: 'suite', - state: 'running', - suite: suite2 - }; - const s2finished = { - type: 'suite', - state: 'completed', - suite: suite2 - }; - assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); - - const s2t2running = { - type: 'test', - state: 'running', - test: s2t2 - }; - assert.ok( - testStatesEvI(s2running) < testStatesEvI(s2t2running)); + it('should run all', async function() { + await loadAdapterAndAssert(); + assert.equal(root.children.length, 2); + await adapter.run([root.id]); - const s2t2finished = { - type: 'test', - state: 'skipped', - test: s2t2 - }; - assert.ok( - testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); - assert.ok( - testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); - - const finished = {type: 'finished'}; - assert.ok(testStatesEvI(s1finished) < testStatesEvI(finished)); - assert.ok(testStatesEvI(s2finished) < testStatesEvI(finished)); - - assert.equal( - testStatesEvents.length, 8, inspect(testStatesEvents)); - }), - new Mocha.Test( - 'cancel after run finished', - function() { - let spyKill1: sinon.SinonSpy; - let spyKill2: sinon.SinonSpy; - { - const spawnEvent = - new ChildProcessStub(example1.suite1.outputs[2][1]); - spyKill1 = sinon.spy(spawnEvent, 'kill'); - const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.outputs[2][0]); - withArgs.onCall(withArgs.callCount).returns(spawnEvent); - } - { - const spawnEvent = - new ChildProcessStub(example1.suite2.outputs[2][1]); - spyKill2 = sinon.spy(spawnEvent, 'kill'); - const withArgs = spawnStub.withArgs( - example1.suite2.execPath, example1.suite2.outputs[2][0]); - withArgs.onCall(withArgs.callCount).returns(spawnEvent); - } - const run = adapter.run([root.id]); - return run.then(function() { - adapter.cancel(); - assert.equal(spyKill1.callCount, 0); - assert.equal(spyKill2.callCount, 0); - }); - }) - ]; + const running = {type: 'started', tests: [root.id]}; + + const s1running = {type: 'suite', state: 'running', suite: suite1}; + const s1finished = {type: 'suite', state: 'completed', suite: suite1}; + assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); + + const s2running = {type: 'suite', state: 'running', suite: suite2}; + const s2finished = {type: 'suite', state: 'completed', suite: suite2}; + assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); + + const s1t1running = {type: 'test', state: 'running', test: s1t1}; + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t1running)); + + const s1t1finished = { + type: 'test', + state: 'passed', + test: s1t1, + decorations: undefined, + message: 'Duration: 0.000132 second(s)\n' + }; + assert.ok(testStatesEvI(s1t1running) < testStatesEvI(s1t1finished)); + assert.ok(testStatesEvI(s1t1finished) < testStatesEvI(s1finished)); + + const s1t2running = {type: 'test', state: 'running', test: s1t2}; + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t2running)); + + const s1t2finished = { + type: 'test', + state: 'failed', + test: s1t2, + decorations: [{line: 14, message: 'Expanded: false'}], + message: + 'Duration: 0.000204 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + }; + assert.ok(testStatesEvI(s1t2running) < testStatesEvI(s1t2finished)); + assert.ok(testStatesEvI(s1t2finished) < testStatesEvI(s1finished)); + + const s2t1running = {type: 'test', state: 'running', test: s2t1}; + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t1running)); + + const s2t1finished = { + type: 'test', + state: 'passed', + test: s2t1, + decorations: undefined, + message: 'Duration: 0.00037 second(s)\n' + }; + assert.ok(testStatesEvI(s2t1running) < testStatesEvI(s2t1finished)); + assert.ok(testStatesEvI(s2t1finished) < testStatesEvI(s2finished)); + + const s2t2running = {type: 'test', state: 'running', test: s2t2}; + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t2running)); + + const s2t2finished = {type: 'test', state: 'skipped', test: s2t2}; + assert.ok(testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); + assert.ok(testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); + + const s2t3running = {type: 'test', state: 'running', test: s2t3}; + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t3running)); + + const s2t3finished = { + type: 'test', + state: 'failed', + test: s2t3, + decorations: [{line: 20, message: 'Expanded: false'}], + message: + 'Duration: 0.000178 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + }; + assert.ok(testStatesEvI(s2t3running) < testStatesEvI(s2t3finished)); + assert.ok(testStatesEvI(s2t3finished) < testStatesEvI(s2finished)); + + const finished = {type: 'finished'}; + assert.ok(testStatesEvI(s1finished) < testStatesEvI(finished)); + assert.ok(testStatesEvI(s2finished) < testStatesEvI(finished)); + + assert.equal(testStatesEvents.length, 16, inspect(testStatesEvents)); + }) - context('executables=["execPath1", "execPath2"]', function() { - before(function() { - return updateConfig('executables', ['execPath1', 'execPath2']); - }); + it('should run with not existing test id', async function() { + await loadAdapterAndAssert(); + await adapter.run(['not existing id']); - after(function() { - return updateConfig('executables', undefined); - }); + assert.deepStrictEqual(testStatesEvents, [ + {type: 'started', tests: ['not existing id']}, {type: 'finished'} + ]); + }) - let suite1Watcher: FileSystemWatcherStub; + it('should run s1t1', async function() { + await loadAdapterAndAssert(); + await adapter.run([s1t1.id]); + const expected = [ + {type: 'started', tests: [s1t1.id]}, + {type: 'suite', state: 'running', suite: suite1}, + {type: 'test', state: 'running', test: s1t1}, { + type: 'test', + state: 'passed', + test: s1t1, + decorations: undefined, + message: 'Duration: 0.000112 second(s)\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); - beforeEach(async function() { - assert.equal(watchers.size, 2); - assert.ok(watchers.has(example1.suite1.execPath)); - suite1Watcher = watchers.get(example1.suite1.execPath)!; + await adapter.run([s1t1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) - example1.suite1.assert( - 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); + it('should run skipped s2t2', async function() { + await loadAdapterAndAssert(); + await adapter.run([s2t2.id]); + const expected = [ + {type: 'started', tests: [s2t2.id]}, + {type: 'suite', state: 'running', suite: suite2}, + {type: 'test', state: 'running', test: s2t2}, { + type: 'test', + state: 'passed', + test: s2t2, + decorations: undefined, + message: 'Duration: 0.001294 second(s)\n' + }, + {type: 'suite', state: 'completed', suite: suite2}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); - example1.suite2.assert( - 'execPath2', ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); - }); + await adapter.run([s2t2.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) - for (let t of testsForAdapterWithSuite1AndSuite2) - this.addTest(t.clone()); - - it('reload because of fswatcher event: touch(changed)', - async function() { - this.slow(200); - const newRoot = await doAndWaitForReloadEvent(this, async () => { - suite1Watcher.sendChange(); - }); - assert.deepStrictEqual(newRoot, root); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: root}]); - }); + it('should run failing test s2t3', async function() { + await loadAdapterAndAssert(); + await adapter.run([s2t3.id]); + const expected = [ + {type: 'started', tests: [s2t3.id]}, + {type: 'suite', state: 'running', suite: suite2}, + {type: 'test', state: 'running', test: s2t3}, { + type: 'test', + state: 'failed', + test: s2t3, + decorations: [{line: 20, message: 'Expanded: false'}], + message: + 'Duration: 0.000596 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + }, + {type: 'suite', state: 'completed', suite: suite2}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); - it('reload because of fswatcher event: double touch(changed)', - async function() { - this.slow(300); - const oldRoot = root; - suite1Watcher.sendChange(); - suite1Watcher.sendChange(); - await waitFor(this, async () => { - return testsEvents.length >= 2; - }); - await promisify(setTimeout)(100); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: oldRoot}]); - testsEvents.pop(); - testsEvents.pop(); - }); + await adapter.run([s2t3.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) - it('reload because of fswatcher event: double touch(changed) with delay', - async function() { - this.slow(300); - const oldRoot = root; - suite1Watcher.sendChange(); - setTimeout(() => { - suite1Watcher.sendChange(); - }, 20); - await waitFor(this, async () => { - return testsEvents.length >= 2; - }); - await promisify(setTimeout)(100); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: oldRoot}]); - testsEvents.pop(); - testsEvents.pop(); - }); + it('should run failing test s2t3 with chunks', async function() { + await loadAdapterAndAssert(); + const withArgs = spawnStub.withArgs( + example1.suite2.execPath, example1.suite2.t3.outputs[0][0]); + withArgs.onCall(withArgs.callCount) + .returns(new ChildProcessStub(example1.suite2.t3.outputs[0][1])); - it('reload because of fswatcher event: touch(delete,create)', - async function() { - this.slow(200); - const newRoot = await doAndWaitForReloadEvent(this, async () => { - suite1Watcher.sendDelete(); - suite1Watcher.sendCreate(); - }); - assert.deepStrictEqual(newRoot, root); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: root}]); - }); + await adapter.run([s2t3.id]); + const expected = [ + {type: 'started', tests: [s2t3.id]}, + {type: 'suite', state: 'running', suite: suite2}, + {type: 'test', state: 'running', test: s2t3}, { + type: 'test', + state: 'failed', + test: s2t3, + decorations: [{line: 20, message: 'Expanded: false'}], + message: + 'Duration: 0.000596 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + }, + {type: 'suite', state: 'completed', suite: suite2}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); - it('reload because of fswatcher event: double touch(delete,create)', - async function() { - this.slow(300); - const oldRoot = root; - suite1Watcher.sendChange(); - suite1Watcher.sendChange(); - await waitFor(this, async () => { - return testsEvents.length >= 2; - }); - await promisify(setTimeout)(100); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: oldRoot}]); - testsEvents.pop(); - testsEvents.pop(); - }); + await adapter.run([s2t3.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) - it('reload because of fswatcher event: double touch(delete,create) with delay', - async function() { - this.slow(300); - const oldRoot = root; - suite1Watcher.sendChange(); - setTimeout(() => { - suite1Watcher.sendChange(); - }, 20); - await waitFor(this, async () => { - return testsEvents.length >= 2; - }); - await promisify(setTimeout)(100); - assert.deepStrictEqual( - testsEvents, - [{type: 'started'}, {type: 'finished', suite: oldRoot}]); - testsEvents.pop(); - testsEvents.pop(); - }); + it('should run suite1', async function() { + await loadAdapterAndAssert(); + await adapter.run([suite1.id]); + const expected = [ + {type: 'started', tests: [suite1.id]}, + {type: 'suite', state: 'running', suite: suite1}, + {type: 'test', state: 'running', test: s1t1}, { + type: 'test', + state: 'passed', + test: s1t1, + decorations: undefined, + message: 'Duration: 0.000132 second(s)\n' + }, + {type: 'test', state: 'running', test: s1t2}, { + type: 'test', + state: 'failed', + test: s1t2, + decorations: [{line: 14, message: 'Expanded: false'}], + message: + 'Duration: 0.000204 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); + + await adapter.run([suite1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) + + it('should run with wrong xml', async function() { + await loadAdapterAndAssert(); + const m = example1.suite1.t1.outputs[0][1].match(']+>'); + assert.notStrictEqual(m, undefined); + assert.notStrictEqual(m!.input, undefined); + assert.notStrictEqual(m!.index, undefined); + const part = m!.input!.substr(0, m!.index! + m![0].length); + const withArgs = spawnStub.withArgs( + example1.suite1.execPath, example1.suite1.t1.outputs[0][0]); + withArgs.onCall(withArgs.callCount) + .returns(new ChildProcessStub(part)); + + await adapter.run([s1t1.id]); + + const expected = [ + {type: 'started', tests: [s1t1.id]}, + {type: 'suite', state: 'running', suite: suite1}, + {type: 'test', state: 'running', test: s1t1}, { + type: 'test', + state: 'failed', + test: s1t1, + message: 'Unexpected test error. (Is Catch2 crashed?)\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'} + ]; + assert.deepStrictEqual(testStatesEvents, expected); + + // this tests the sinon stubs too + await adapter.run([s1t1.id]); + assert.deepStrictEqual(testStatesEvents, [ + ...expected, {type: 'started', tests: [s1t1.id]}, + {type: 'suite', state: 'running', suite: suite1}, + {type: 'test', state: 'running', test: s1t1}, { + type: 'test', + state: 'passed', + test: s1t1, + decorations: undefined, + message: 'Duration: 0.000112 second(s)\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'} + ]); + }) + + it('should cancel without error', async function() { + await loadAdapterAndAssert(); + adapter.cancel(); + }) - it('reload because of fswatcher event: test added', async function() { - this.slow(200); - const testListOutput = example1.suite1.outputs[1][1].split('\n'); - assert.equal(testListOutput.length, 10); - testListOutput.splice( - 1, 0, ' s1t0', ' suite1.cpp:6', ' tag1'); + it('cancel', async function() { + await loadAdapterAndAssert(); + let spyKill1: sinon.SinonSpy; + let spyKill2: sinon.SinonSpy; + { + const spawnEvent = + new ChildProcessStub(example1.suite1.outputs[2][1]); + spyKill1 = sinon.spy(spawnEvent, 'kill'); const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.outputs[1][0]); - withArgs.onCall(withArgs.callCount) - .returns(new ChildProcessStub(testListOutput.join(EOL))); + example1.suite1.execPath, example1.suite1.outputs[2][0]); + withArgs.onCall(withArgs.callCount).returns(spawnEvent); + } + { + const spawnEvent = + new ChildProcessStub(example1.suite2.outputs[2][1]); + spyKill2 = sinon.spy(spawnEvent, 'kill'); + const withArgs = spawnStub.withArgs( + example1.suite2.execPath, example1.suite2.outputs[2][0]); + withArgs.onCall(withArgs.callCount).returns(spawnEvent); + } + const run = adapter.run([root.id]); + adapter.cancel(); + await run; - const oldRootChildren = [...root.children]; - const oldSuite1Children = [...suite1.children]; - const oldSuite2Children = [...suite2.children]; + assert.equal(spyKill1.callCount, 1); + assert.equal(spyKill2.callCount, 1); - const newRoot = await doAndWaitForReloadEvent(this, async () => { - suite1Watcher.sendDelete(); - suite1Watcher.sendCreate(); - }); + const running = {type: 'started', tests: [root.id]}; - assert.equal(newRoot, root); - assert.equal(root.children.length, oldRootChildren.length); - for (let i = 0; i < oldRootChildren.length; i++) { - assert.equal(root.children[i], oldRootChildren[i]); - } - - assert.equal(suite1.children.length, oldSuite1Children.length + 1); - for (let i = 0; i < suite1.children.length; i++) { - assert.equal(suite1.children[i + 1], oldSuite1Children[i]); - } - const newTest = suite1.children[0]; - assert.ok(!uniqueIdC.has(newTest.id)); - assert.equal(newTest.label, 's1t0'); - - assert.equal(suite2.children.length, oldSuite2Children.length); - for (let i = 0; i < suite2.children.length; i++) { - assert.equal(suite2.children[i], oldSuite2Children[i]); - } - }) + const s1running = {type: 'suite', state: 'running', suite: suite1}; + const s1finished = {type: 'suite', state: 'completed', suite: suite1}; + assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); - it('reload because of fswatcher event: test deleted', - async function() { - this.slow(200); - const testListOutput = example1.suite1.outputs[1][1].split('\n'); - assert.equal(testListOutput.length, 10); - testListOutput.splice(1, 3); - const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.outputs[1][0]); - withArgs.onCall(withArgs.callCount) - .returns(new ChildProcessStub(testListOutput.join('\n'))); - - const oldRootChildren = [...root.children]; - const oldSuite1Children = [...suite1.children]; - const oldSuite2Children = [...suite2.children]; - - const newRoot = await doAndWaitForReloadEvent(this, async () => { - suite1Watcher.sendDelete(); - suite1Watcher.sendCreate(); - }); - - assert.equal(newRoot, root); - assert.equal(root.children.length, oldRootChildren.length); - for (let i = 0; i < oldRootChildren.length; i++) { - assert.equal(root.children[i], oldRootChildren[i]); - } - - assert.equal( - suite1.children.length + 1, oldSuite1Children.length); - for (let i = 0; i < suite1.children.length; i++) { - assert.equal(suite1.children[i], oldSuite1Children[i + 1]); - } - - assert.equal(suite2.children.length, oldSuite2Children.length); - for (let i = 0; i < suite2.children.length; i++) { - assert.equal(suite2.children[i], oldSuite2Children[i]); - } - }) - - it('data arrives in pieces', async function() { - this.slow(200); - const testListOutput = example1.suite1.outputs[2][1].split('\n'); - assert.equal(testListOutput.length, 21); - const newOutput: string[] = [ - testListOutput[0] + EOL + testListOutput[1].substr(10) + EOL, - testListOutput[2].substr(10) + EOL, - testListOutput[3].substr(10) + EOL, - testListOutput - .filter((v: string, i: number) => { - return i > 3; - }) - .map((v: string) => { - return v.substr(10); - }) - .join(EOL) + - EOL + EOL, - ]; + const s2running = {type: 'suite', state: 'running', suite: suite2}; + const s2finished = {type: 'suite', state: 'completed', suite: suite2}; + assert.ok(testStatesEvI(running) < testStatesEvI(s1running)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); + + const s2t2running = {type: 'test', state: 'running', test: s2t2}; + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t2running)); + + const s2t2finished = {type: 'test', state: 'skipped', test: s2t2}; + assert.ok(testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); + assert.ok(testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); + + const finished = {type: 'finished'}; + assert.ok(testStatesEvI(s1finished) < testStatesEvI(finished)); + assert.ok(testStatesEvI(s2finished) < testStatesEvI(finished)); + + assert.equal(testStatesEvents.length, 8, inspect(testStatesEvents)); + }) + + it('cancel after run finished', async function() { + await loadAdapterAndAssert(); + let spyKill1: sinon.SinonSpy; + let spyKill2: sinon.SinonSpy; + { + const spawnEvent = + new ChildProcessStub(example1.suite1.outputs[2][1]); + spyKill1 = sinon.spy(spawnEvent, 'kill'); const withArgs = spawnStub.withArgs( example1.suite1.execPath, example1.suite1.outputs[2][0]); - withArgs.onCall(withArgs.callCount) - .returns(new ChildProcessStub(newOutput)); + withArgs.onCall(withArgs.callCount).returns(spawnEvent); + } + { + const spawnEvent = + new ChildProcessStub(example1.suite2.outputs[2][1]); + spyKill2 = sinon.spy(spawnEvent, 'kill'); + const withArgs = spawnStub.withArgs( + example1.suite2.execPath, example1.suite2.outputs[2][0]); + withArgs.onCall(withArgs.callCount).returns(spawnEvent); + } + const run = adapter.run([root.id]); + return await run.then(function() { + adapter.cancel(); + assert.equal(spyKill1.callCount, 0); + assert.equal(spyKill2.callCount, 0); + }); + }) - await adapter.run([suite1.id]); - }) + it('reload because of fswatcher event: touch(changed)', + async function() { + await loadAdapterAndAssert(); + const newRoot = await doAndWaitForReloadEvent(this, async () => { + suite1Watcher.sendChange(); + }); + assert.deepStrictEqual(newRoot, root); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: root}]); + }); + + it('reload because of fswatcher event: double touch(changed)', + async function() { + await loadAdapterAndAssert(); + const oldRoot = root; + suite1Watcher.sendChange(); + suite1Watcher.sendChange(); + await waitFor(this, async () => { + return testsEvents.length >= 2; + }); + await promisify(setTimeout)(100); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: oldRoot}]); + testsEvents.pop(); + testsEvents.pop(); + }); + + it('reload because of fswatcher event: double touch(changed) with delay', + async function() { + await loadAdapterAndAssert(); + const oldRoot = root; + suite1Watcher.sendChange(); + setTimeout(() => { + suite1Watcher.sendChange(); + }, 20); + await waitFor(this, async () => { + return testsEvents.length >= 2; + }); + await promisify(setTimeout)(100); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: oldRoot}]); + testsEvents.pop(); + testsEvents.pop(); + }); + + it('reload because of fswatcher event: touch(delete,create)', + async function() { + await loadAdapterAndAssert(); + const newRoot = await doAndWaitForReloadEvent(this, async () => { + suite1Watcher.sendDelete(); + suite1Watcher.sendCreate(); + }); + assert.deepStrictEqual(newRoot, root); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: root}]); + }); + + it('reload because of fswatcher event: double touch(delete,create)', + async function() { + await loadAdapterAndAssert(); + const oldRoot = root; + suite1Watcher.sendChange(); + suite1Watcher.sendChange(); + await waitFor(this, async () => { + return testsEvents.length >= 2; + }); + await promisify(setTimeout)(100); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: oldRoot}]); + testsEvents.pop(); + testsEvents.pop(); + }); + + it('reload because of fswatcher event: double touch(delete,create) with delay', + async function() { + await loadAdapterAndAssert(); + const oldRoot = root; + suite1Watcher.sendChange(); + setTimeout(() => { + suite1Watcher.sendChange(); + }, 20); + await waitFor(this, async () => { + return testsEvents.length >= 2; + }); + await promisify(setTimeout)(100); + assert.deepStrictEqual( + testsEvents, + [{type: 'started'}, {type: 'finished', suite: oldRoot}]); + testsEvents.pop(); + testsEvents.pop(); + }); + + it('reload because of fswatcher event: test added', async function() { + await loadAdapterAndAssert(); + const testListOutput = example1.suite1.outputs[1][1].split('\n'); + assert.equal(testListOutput.length, 10); + testListOutput.splice(1, 0, ' s1t0', ' suite1.cpp:6', ' tag1'); + const withArgs = spawnStub.withArgs( + example1.suite1.execPath, example1.suite1.outputs[1][0]); + withArgs.onCall(withArgs.callCount) + .returns(new ChildProcessStub(testListOutput.join(EOL))); + + const oldRootChildren = [...root.children]; + const oldSuite1Children = [...suite1.children]; + const oldSuite2Children = [...suite2.children]; + + const newRoot = await doAndWaitForReloadEvent(this, async () => { + suite1Watcher.sendDelete(); + suite1Watcher.sendCreate(); + }); + + assert.equal(newRoot, root); + assert.equal(root.children.length, oldRootChildren.length); + for (let i = 0; i < oldRootChildren.length; i++) { + assert.equal(root.children[i], oldRootChildren[i]); + } + + assert.equal(suite1.children.length, oldSuite1Children.length + 1); + for (let i = 0; i < oldSuite1Children.length; i++) { + const c1: TestInfo = suite1.children[i + 1]; + const c2: TestInfo = oldSuite1Children[i]; + assert.deepStrictEqual( + [c1.file, c1.id, c1.label, c1.line, c1.skipped, c1.type], + [c2.file, c2.id, c2.label, c2.line, c2.skipped, c2.type], + inspect(i)); + } + const newTest = suite1.children[0]; + assert.ok(!uniqueIdC.has(newTest.id)); + assert.equal(newTest.label, 's1t0'); + + assert.equal(suite2.children.length, oldSuite2Children.length); + for (let i = 0; i < suite2.children.length; i++) { + assert.equal(suite2.children[i], oldSuite2Children[i]); + } }) - context('executables=[{}] and env={...}', function() { - before(async function() { - await updateConfig( - 'executables', [{ - name: '${relDirpath}/${filename} (${absDirpath})', - path: 'execPath{1,2}', - cwd: '${workspaceFolder}/cwd', - env: { - C2LOCALTESTENV: 'c2localtestenv', - C2OVERRIDETESTENV: 'c2overridetestenv-l' - } - }]); - - vsfsWatchStub - .withArgs(matchRelativePattern( - path.join(workspaceFolderUri.fsPath, 'execPath{1,2}'))) - .callsFake(handleCreateWatcherCb); - - vsFindFilesStub - .withArgs(matchRelativePattern( - path.join(workspaceFolderUri.fsPath, 'execPath{1,2}'))) - .returns([ - vscode.Uri.file(example1.suite1.execPath), - vscode.Uri.file(example1.suite2.execPath), - ]); - await updateConfig('defaultEnv', { - 'C2GLOBALTESTENV': 'c2globaltestenv', - 'C2OVERRIDETESTENV': 'c2overridetestenv-g', - }); + it('reload because of fswatcher event: test deleted', async function() { + await loadAdapterAndAssert(); + const testListOutput = example1.suite1.outputs[1][1].split('\n'); + assert.equal(testListOutput.length, 10); + testListOutput.splice(1, 3); + const withArgs = spawnStub.withArgs( + example1.suite1.execPath, example1.suite1.outputs[1][0]); + withArgs.onCall(withArgs.callCount) + .returns(new ChildProcessStub(testListOutput.join('\n'))); + + const oldRootChildren = [...root.children]; + const oldSuite1Children = [...suite1.children]; + const oldSuite2Children = [...suite2.children]; + + const newRoot = await doAndWaitForReloadEvent(this, async () => { + suite1Watcher.sendDelete(); + suite1Watcher.sendCreate(); }); - after(async function() { - await updateConfig('executables', undefined); - await updateConfig('defaultEnv', undefined); + assert.equal(newRoot, root); + assert.equal(root.children.length, oldRootChildren.length); + for (let i = 0; i < oldRootChildren.length; i++) { + assert.equal(root.children[i], oldRootChildren[i]); + } + + assert.equal(suite1.children.length + 1, oldSuite1Children.length); + for (let i = 0; i < suite1.children.length; i++) { + const c1: TestInfo = suite1.children[i]; + const c2: TestInfo = oldSuite1Children[i + 1]; + assert.deepStrictEqual( + [c1.file, c1.id, c1.label, c1.line, c1.skipped, c1.type], + [c2.file, c2.id, c2.label, c2.line, c2.skipped, c2.type]); + } + + assert.equal(suite2.children.length, oldSuite2Children.length); + for (let i = 0; i < suite2.children.length; i++) { + assert.equal(suite2.children[i], oldSuite2Children[i]); + } + }) + + it('data arrives in pieces', async function() { + await loadAdapterAndAssert(); + const testListOutput = example1.suite1.outputs[2][1].split('\n'); + assert.equal(testListOutput.length, 21); + const newOutput: string[] = [ + testListOutput[0] + EOL + testListOutput[1].substr(10) + EOL, + testListOutput[2].substr(10) + EOL, + testListOutput[3].substr(10) + EOL, + testListOutput + .filter((v: string, i: number) => { + return i > 3; + }) + .map((v: string) => { + return v.substr(10); + }) + .join(EOL) + + EOL + EOL, + ]; + const withArgs = spawnStub.withArgs( + example1.suite1.execPath, example1.suite1.outputs[2][0]); + withArgs.onCall(withArgs.callCount) + .returns(new ChildProcessStub(newOutput)); + + await adapter.run([suite1.id]); + }) + }) + + context('executables=[{}] and env={...}', function() { + beforeEach(async function() { + await updateConfig( + 'executables', [{ + name: '${relDirpath}/${filename} (${absDirpath})', + path: 'execPath{1,2}', + cwd: '${workspaceFolder}/cwd', + env: { + C2LOCALTESTENV: 'c2localtestenv', + C2OVERRIDETESTENV: 'c2overridetestenv-l' + } + }]); + await updateConfig('defaultEnv', { + 'C2GLOBALTESTENV': 'c2globaltestenv', + 'C2OVERRIDETESTENV': 'c2overridetestenv-g', }); - beforeEach(async function() { - example1.suite1.assert( - './execPath1 (' + workspaceFolderUri.fsPath + ')', - ['s1t1', 's1t2'], suite1, uniqueIdC); + vsfsWatchStub + .withArgs(matchRelativePattern( + path.join(workspaceFolderUri.fsPath, 'execPath{1,2}'))) + .callsFake(handleCreateWatcherCb); - example1.suite2.assert( - './execPath2 (' + workspaceFolderUri.fsPath + ')', - ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); - }) + vsFindFilesStub + .withArgs(matchRelativePattern( + path.join(workspaceFolderUri.fsPath, 'execPath{1,2}'))) + .returns([ + vscode.Uri.file(example1.suite1.execPath), + vscode.Uri.file(example1.suite2.execPath), + ]); + }) - for (let t of testsForAdapterWithSuite1AndSuite2) this.addTest( - t.clone()); + it('should get execution options', async function() { + { + await loadAdapter(); + const withArgs = spawnStub.withArgs( + example1.suite1.execPath, example1.suite1.outputs[2][0]); + withArgs.onCall(withArgs.callCount) + .callsFake((p: string, args: string[], ops: any) => { + assert.equal( + ops.cwd, path.join(workspaceFolderUri.fsPath, 'cwd')); + assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); + assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); + assert.equal( + ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); + return new ChildProcessStub(example1.suite1.outputs[2][1]); + }); - it('should get execution options', async function() { - { - const withArgs = spawnStub.withArgs( - example1.suite1.execPath, example1.suite1.outputs[2][0]); - withArgs.onCall(withArgs.callCount) - .callsFake((p: string, args: string[], ops: any) => { - assert.equal( - ops.cwd, path.join(workspaceFolderUri.fsPath, 'cwd')); - assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); - assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); - assert.equal( - ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); - return new ChildProcessStub(example1.suite1.outputs[2][1]); - }); - - const cc = withArgs.callCount; - await adapter.run([suite1.id]); - assert.equal(withArgs.callCount, cc + 1); - } - { - const withArgs = spawnStub.withArgs( - example1.suite2.execPath, example1.suite2.outputs[2][0]); - withArgs.onCall(withArgs.callCount) - .callsFake((p: string, args: string[], ops: any) => { - assert.equal( - ops.cwd, path.join(workspaceFolderUri.fsPath, 'cwd')); - assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); - assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); - assert.equal( - ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); - return new ChildProcessStub(example1.suite2.outputs[2][1]); - }); - const cc = withArgs.callCount; - await adapter.run([suite2.id]); - assert.equal(withArgs.callCount, cc + 1); - } - }) + const cc = withArgs.callCount; + await adapter.run([root.id]); + assert.equal(withArgs.callCount, cc + 1); + } + { + const withArgs = spawnStub.withArgs( + example1.suite2.execPath, example1.suite2.outputs[2][0]); + withArgs.onCall(withArgs.callCount) + .callsFake((p: string, args: string[], ops: any) => { + assert.equal( + ops.cwd, path.join(workspaceFolderUri.fsPath, 'cwd')); + assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); + assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); + assert.equal( + ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); + return new ChildProcessStub(example1.suite2.outputs[2][1]); + }); + const cc = withArgs.callCount; + await adapter.run([root.id]); + assert.equal(withArgs.callCount, cc + 1); + } }) }) context( 'executables=["execPath1", "execPath2", "execPath3"]', async function() { - before(function() { + this.slow(300); + + beforeEach(function() { return updateConfig( 'executables', ['execPath1', 'execPath2', 'execPath3']); - }); - - after(function() { - return updateConfig('executables', undefined); - }); + }) it('run suite3 one-by-one', async function() { - this.slow(300); + await loadAdapter(); assert.equal(root.children.length, 3); assert.equal(root.children[0].type, 'suite'); const suite3 = root.children[2]; @@ -1475,9 +1352,6 @@ describe('C2TestAdapter', function() { await adapter.run([ (testsEvents[testsEvents.length - 1]).suite!.id ]); - - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); }) specify('load executables=', async function() { @@ -1493,9 +1367,6 @@ describe('C2TestAdapter', function() { 1); testsEvents.pop(); testsEvents.pop(); - - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); }) specify( @@ -1513,9 +1384,6 @@ describe('C2TestAdapter', function() { await adapter.load(); testsEvents.pop(); testsEvents.pop(); - - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); }) specify( @@ -1579,25 +1447,6 @@ describe('C2TestAdapter', function() { assert.equal(testsEvents.length, 2); testsEvents.pop(); testsEvents.pop(); - for (let scenario of example1.suite2.outputs) { - spawnStub.withArgs(execPath2CopyPath, scenario[0]).callsFake(() => { - throw Error('restore'); - }); - } - fsStatStub.withArgs(execPath2CopyPath).callsFake(() => { - throw Error('restore'); - }); - vsfsWatchStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - vsFindFilesStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); - await updateConfig('defaultWatchTimeoutSec', undefined); assert.equal(newRoot.children.length, 2); assert.ok(3000 < elapsed, inspect(elapsed)); @@ -1655,25 +1504,6 @@ describe('C2TestAdapter', function() { const elapsed = Date.now() - start; testsEvents.pop(); testsEvents.pop(); - for (let scenario of example1.suite2.outputs) { - spawnStub.withArgs(execPath2CopyPath, scenario[0]).callsFake(() => { - throw Error('restore'); - }); - } - fsStatStub.withArgs(execPath2CopyPath).callsFake(() => { - throw Error('restore'); - }); - vsfsWatchStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - vsFindFilesStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); - await updateConfig('defaultWatchTimeoutSec', undefined); assert.equal(newRoot.children.length, 1); assert.ok(watchTimeout * 1000 < elapsed, inspect(elapsed)); @@ -1693,9 +1523,6 @@ describe('C2TestAdapter', function() { assert.equal(root.children.length, 0); testsEvents.pop(); testsEvents.pop(); - - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); }) specify('variable substitution with executables={...}', async function() { @@ -1769,25 +1596,6 @@ describe('C2TestAdapter', function() { assert.equal(suite.children.length, 3); await adapter.run([suite.children[0].id]); - - for (let scenario of example1.suite2.outputs) { - spawnStub.withArgs(execPath2CopyPath, scenario[0]).callsFake(() => { - throw Error('restore'); - }); - } - fsStatStub.withArgs(execPath2CopyPath).callsFake(() => { - throw Error('restore'); - }); - vsfsWatchStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - vsFindFilesStub.withArgs(matchRelativePattern(execPath2CopyPath)) - .callsFake(() => { - throw Error('restore'); - }); - disposeAdapterAndSubscribers(); - await updateConfig('executables', undefined); }) }) }) diff --git a/src/test/cpp/catch.hpp b/src/test/cpp/catch.hpp.txt similarity index 100% rename from src/test/cpp/catch.hpp rename to src/test/cpp/catch.hpp.txt diff --git a/src/test/cpp/suite1.cpp b/src/test/cpp/suite1.cpp index 0ec23395..0e7822dd 100644 --- a/src/test/cpp/suite1.cpp +++ b/src/test/cpp/suite1.cpp @@ -1,5 +1,5 @@ #define CATCH_CONFIG_MAIN -#include "catch.hpp" +#include "catch.hpp.txt" // c++ -x c++ -std=c++17 -I ../Catch2/single_include -O0 -g -o suite1 // ../vscode-catch2-test-adapter/src/test/suite1.cpp diff --git a/src/test/cpp/suite2.cpp b/src/test/cpp/suite2.cpp index 1028cb4b..6bf77ccd 100644 --- a/src/test/cpp/suite2.cpp +++ b/src/test/cpp/suite2.cpp @@ -1,5 +1,5 @@ #define CATCH_CONFIG_MAIN -#include "catch.hpp" +#include "catch.hpp.txt" // c++ -x c++ -std=c++17 -I ../Catch2/single_include -O0 -g -o suite2 // ../vscode-catch2-test-adapter/src/test/suite2.cpp diff --git a/src/test/cpp/suite3.cpp b/src/test/cpp/suite3.cpp index 8924cf41..1d3cfce7 100644 --- a/src/test/cpp/suite3.cpp +++ b/src/test/cpp/suite3.cpp @@ -1,5 +1,5 @@ #define CATCH_CONFIG_MAIN -#include "catch.hpp" +#include "catch.hpp.txt" // clang-format off // c++ -x c++ -std=c++17 -I ../Catch2/single_include -O0 -g -o suite3 ../vscode-catch2-test-adapter/src/test/suite3.cpp