diff --git a/.gitignore b/.gitignore index f683c593..aa5d6d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store node_modules out *.vsix diff --git a/.travis.yml b/.travis.yml index f6565979..f1e53a23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ sudo: false +language: node_js +node_js: + - "10" os: - osx @@ -14,7 +17,6 @@ before_install: install: - npm install - npm run compile - #- npm run vscode:prepublish script: - npm test --silent \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1532ee5e..7ad85ff5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.2.0] + +### Added + +- Configuration: `catch2TestExplorer.defaultExecWatchTimeout`: Test executables are being watched. In case of one compiles too much this variable can help with it. + ## [1.1.2] -Bugfix release. +Bugfix release ## [1.1.1] diff --git a/README.md b/README.md index 9c077ca9..fdbb6a84 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This adapter doesn't support everything. | `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.defaultExecWatchTimeout` | Test executables are being watched. In case of one compiles too much this variable can help with it. | | `catch2TestExplorer.workerMaxNumber` | The variable maximize the number of the parallel test execution. | | `catch2TestExplorer.enableSourceDecoration` | Sets the source code decorations: Errored lines will be highlited. | | `catch2TestExplorer.debugConfigurationTemplate` | Set the necessary debug configuraitons and the debug button will work. Details: [below](#catch2TestExplorer.debugConfigurationTemplate) | diff --git a/package-lock.json b/package-lock.json index f21a8291..8b9f01d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-catch2-test-adapter", - "version": "1.1.2", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a172578f..d6c61b77 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,10 @@ "name": "vscode-catch2-test-adapter", "displayName": "Catch2 Test Explorer", "description": "Run your Catch2 tests in the Sidebar of Visual Studio Code", - "icon": "icon.png", + "icon": "resources/icon.png", "author": "Mate Pek", "publisher": "matepek", - "version": "1.1.2", + "version": "1.2.0", "license": "Unlicense", "homepage": "https://github.com/matepek/vscode-catch2-test-adapter", "repository": { @@ -29,7 +29,7 @@ "scripts": { "postinstall": "node ./node_modules/vscode/bin/install", "reinstall": "rimraf node_modules package-lock.json && npm install", - "clean": "rimraf out vscode-google-test-adapter-*.vsix", + "clean": "rimraf out", "compile": "tsc -p ./", "watch": "tsc -watch -p ./", "rebuild": "npm run clean && npm run build", @@ -120,6 +120,14 @@ "default": null, "scope": "resource" }, + "catch2TestExplorer.defaultExecWatchTimeout": { + "description": "Test executables are being watched. In case of one compiles too much this variable can help with it.", + "type": [ + "number" + ], + "default": 10000, + "scope": "resource" + }, "catch2TestExplorer.workerMaxNumber": { "description": "The variable maximize the number of the parallel test execution.", "type": "number", diff --git a/icon.png b/resources/icon.png similarity index 100% rename from icon.png rename to resources/icon.png diff --git a/resources/logo.xcf b/resources/logo.xcf new file mode 100644 index 00000000..fb23a06c Binary files /dev/null and b/resources/logo.xcf differ diff --git a/src/C2AllTestSuiteInfo.ts b/src/C2AllTestSuiteInfo.ts index 029417be..de546efd 100644 --- a/src/C2AllTestSuiteInfo.ts +++ b/src/C2AllTestSuiteInfo.ts @@ -91,9 +91,10 @@ export class C2AllTestSuiteInfo implements TestSuiteInfo { } const ps: Promise[] = []; - this.children.forEach(child => { + for (let i = 0; i < this.children.length; i++) { + const child = this.children[i]; ps.push(child.run(testSet)); - }); + } if (testSet.size > 0) { this.adapter.log.error('Some tests have remained.'); diff --git a/src/C2TestAdapter.ts b/src/C2TestAdapter.ts index 6e3a7692..118cf884 100644 --- a/src/C2TestAdapter.ts +++ b/src/C2TestAdapter.ts @@ -2,8 +2,8 @@ // vscode-catch2-test-adapter was written by Mate Pek, and is placed in the // public domain. The author hereby disclaims copyright to this source code. -import * as fs from 'fs'; import * as path from 'path'; +import {inspect, promisify} from 'util'; import * as vscode from 'vscode'; import {TestAdapter, TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent} from 'vscode-test-adapter-api'; import * as util from 'vscode-test-adapter-util'; @@ -20,7 +20,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { TestSuiteEvent|TestEvent>(); private readonly autorunEmitter = new vscode.EventEmitter(); - private readonly watchers: Map = new Map(); + private readonly watchers: Map = new Map(); private isRunning: number = 0; private isDebugging: boolean = false; @@ -80,6 +80,9 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { this.disposables.forEach(d => { d.dispose(); }); + this.watchers.forEach((v) => { + v.close(); + }); } get testStates(): vscode.Event { - // need some time here: - const waitAndThenTry = (remainingIteration: number, delay: number) => { - if (remainingIteration == 0) { - watcher!.close(); - this.watchers.delete(suite.execPath); - this.testsEmitter.fire({type: 'started'}); - allTests.removeChild(suite); - this.testsEmitter.fire({type: 'finished', suite: this.allTests}); - } else if (!fs.existsSync(suite.execPath)) { - setTimeout( - waitAndThenTry, delay, - [remainingIteration - 1, Math.max(delay * 2, 2000)]); - } else { - this.testsEmitter.fire({type: 'started'}); - suite.reloadChildren().then(() => { - this.testsEmitter.fire({type: 'finished', suite: this.allTests}); - }); - } - }; + const x = + (exists: boolean, startTime: number, timeout: number, + delay: number): Promise => { + if ((Date.now() - startTime) > timeout) { + watcher!.close(); + this.watchers.delete(suite.execPath); + this.testsEmitter.fire({type: 'started'}); + allTests.removeChild(suite); + this.testsEmitter.fire( + {type: 'finished', suite: this.allTests}); + return Promise.resolve(); + } else if (exists) { + this.testsEmitter.fire({type: 'started'}); + return suite.reloadChildren().then( + () => { + this.testsEmitter.fire( + {type: 'finished', suite: this.allTests}); + }, + (err: any) => { + this.testsEmitter.fire( + {type: 'finished', suite: this.allTests}); + this.log.warn(inspect(err)); + return x( + false, startTime, timeout, Math.min(delay * 2, 2000)); + }); + } + return promisify(setTimeout)(Math.min(delay * 2, 2000)) + .then(() => { + return c2fs.existsAsync(suite.execPath) + .then((exists: boolean) => { + return x( + exists, startTime, timeout, + Math.min(delay * 2, 2000)); + }); + }); + }; // change event can arrive during debug session on osx (why?) if (!this.isDebugging) { - waitAndThenTry(10, 128); + // TODO filter multiple events and dont mess with 'load' + x(false, Date.now(), + this.getDefaultExecWatchTimeout(this.getConfiguration()), 64); } }); } catch (e) { this.log.warn('watcher couldn\'t watch: ' + suite.execPath); } - return suite.reloadChildren().catch((e) => { + return suite.reloadChildren().catch((err: any) => { + this.log.warn(inspect(err)); this.allTests.removeChild(suite); }); } @@ -359,6 +382,11 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return config.get('defaultRngSeed', null); } + private getDefaultExecWatchTimeout(config: vscode.WorkspaceConfiguration): + number { + return config.get('defaultExecWatchTimeout', 10000); + } + private getWorkerMaxNumber(config: vscode.WorkspaceConfiguration): number { return config.get('workerMaxNumber', 4); } @@ -387,7 +415,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return config.get('enableSourceDecoration', true); } - private getExecutables(config: vscode.WorkspaceConfiguration): + private async getExecutables(config: vscode.WorkspaceConfiguration): Promise { const globalWorkingDirectory = this.getDefaultCwd(config); @@ -400,7 +428,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return path.isAbsolute(p) ? p : this.resolveRelPath(p); }; - const addObject = (o: Object): void => { + const addObject = async(o: Object): Promise => { const name: string = o.hasOwnProperty('name') ? (o)['name'] : '${dirname} : ${name}'; if (!o.hasOwnProperty('path') || (o)['path'] === null) { @@ -421,11 +449,12 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { false; if (regex.length > 0) { - const recursiveAdd = (directory: string): void => { + const recursiveAdd = async(directory: string): Promise => { const children = c2fs.readdirSync(directory); - children.forEach(child => { + for (let i = 0; i < children.length; ++i) { + const child = children[i]; const childPath = path.resolve(directory, child); - const childStat = c2fs.statSync(childPath); + const childStat = await c2fs.statAsync(childPath); if (childPath.match(regex) && childStat.isFile()) { let resolvedName = name + ' : ' + child; let resolvedCwd = cwd; @@ -451,14 +480,14 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { executables.push(new ExecutableConfig( resolvedName, childPath, regex, fullPath(resolvedCwd), env)); } else if (childStat.isDirectory() && regexRecursive) { - recursiveAdd(childPath); + await recursiveAdd(childPath); } - }); + } }; try { - const stat = c2fs.statSync(p); + const stat = await c2fs.statAsync(p); if (stat.isDirectory()) { - recursiveAdd(p); + await recursiveAdd(p); } else if (stat.isFile()) { executables.push(new ExecutableConfig(name, p, regex, cwd, env)); } else { @@ -492,16 +521,16 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { '', globalWorkingDirectory, [])); } } else { - addObject(configExecs[i]); + await addObject(configExecs[i]); } } } else if (configExecs instanceof Object) { - addObject(configExecs); + await addObject(configExecs); } else { throw 'Catch2 config error: wrong type: executables'; } - return this.filterVerifiedCatch2TestExecutables(executables); + return this.filterVerifiedCatch2TestExecutables(await executables); } verifyIsCatch2TestExecutable(path: string): Promise { diff --git a/src/C2TestInfo.ts b/src/C2TestInfo.ts index 0b4360b3..79a176cb 100644 --- a/src/C2TestInfo.ts +++ b/src/C2TestInfo.ts @@ -34,8 +34,11 @@ export class C2TestInfo implements TestInfo { } getEscapedTestName(): string { - let t = this.testNameFull.replace(',', '\\,').replace('[', '\\['); - if (t[0] == ' ') t = '*' + t.substr(1); + let t = this.testNameFull; + t = t.replace(/,/g, '\\,') + t = t.replace(/\[/g, '\\['); + t = t.replace(/\*/g, '\\*'); + if (t.startsWith(' ')) t = '*' + t.substr(1); return t; } diff --git a/src/C2TestSuiteInfo.ts b/src/C2TestSuiteInfo.ts index 22a7c7f1..a3535056 100644 --- a/src/C2TestSuiteInfo.ts +++ b/src/C2TestSuiteInfo.ts @@ -3,9 +3,7 @@ // public domain. The author hereby disclaims copyright to this source code. import {ChildProcess, spawn, SpawnOptions} from 'child_process'; -import * as fs from 'fs'; import * as path from 'path'; -import {promisify} from 'util'; import {TestEvent, TestSuiteEvent, TestSuiteInfo} from 'vscode-test-adapter-api'; import * as xml2js from 'xml2js'; @@ -82,17 +80,19 @@ export class C2TestSuiteInfo implements TestSuiteInfo { this.proc = undefined; if (tests.delete(this.id)) { - this.children.forEach(c => { + for (let i = 0; i < this.children.length; i++) { + const c = this.children[i]; tests.delete(c.id); - }); + } return this.runInner('all'); } else { let childrenToRun: C2TestInfo[] = []; - this.children.forEach(c => { + for (let i = 0; i < this.children.length; i++) { + const c = this.children[i]; if (tests.delete(c.id)) childrenToRun.push(c); - }); + } if (childrenToRun.length == 0) return Promise.resolve(); @@ -115,18 +115,20 @@ export class C2TestSuiteInfo implements TestSuiteInfo { const execParams: string[] = []; if (childrenToRun != 'all') { let testNames: string[] = []; - childrenToRun.forEach(c => { + for (let i = 0; i < childrenToRun.length; i++) { + const c = childrenToRun[i]; /*',' has special meaning */ testNames.push(c.getEscapedTestName()); - }); + } execParams.push(testNames.join(',')); } else { - this.children.forEach(c => { + 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()); } - }); + } } execParams.push('--reporter'); execParams.push('xml'); @@ -166,7 +168,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { const b = data.buffer.indexOf(' { - return promisify(fs.exists)(this.execPath).then((exists: boolean) => { - if (!exists) - throw Error('reloadSuiteChildren: Should exists: ' + this.execPath); - + return c2fs.existsAsync(this.execPath).then((exists: boolean) => { + if (!exists) throw new Error('Path not exists: ' + this.execPath); return c2fs .spawnAsync( this.execPath, diff --git a/src/FsWrapper.ts b/src/FsWrapper.ts index d4c8b66a..3ce59a66 100644 --- a/src/FsWrapper.ts +++ b/src/FsWrapper.ts @@ -5,23 +5,26 @@ import * as cp from 'child_process'; import * as fs from 'fs'; +export type SpawnReturns = cp.SpawnSyncReturns; + export function spawnAsync( cmd: string, args?: string[], - options?: cp.SpawnOptions): Promise> { + options?: cp.SpawnOptions): Promise { return new Promise((resolve) => { - const command = cp.spawn(cmd, args, options); - const ret: cp.SpawnSyncReturns = { - pid: command.pid, - output: [], + const ret: SpawnReturns = { + pid: 0, + output: ['', ''], stdout: '', stderr: '', status: 0, signal: '', error: new Error() }; + const command = cp.spawn(cmd, args, options); + ret.pid = command.pid; command.stdout.on('data', function(data) { ret.stdout += data; - ret.output.push(data); + ret.output[0] = ret.stdout; }); command.on('close', function(code) { ret.status = code; @@ -34,14 +37,41 @@ export function spawnAsync( }) } -export function statSync(path: string): fs.Stats { - return fs.statSync(path); +export type Stats = fs.Stats; + +export function statAsync(path: string): Promise { + return new Promise((resolve, reject) => { + fs.stat( + path, (err: NodeJS.ErrnoException|null, stats: fs.Stats|undefined) => { + if (stats) + resolve(stats); + else + reject(err); + }); + }); +} + +export function existsAsync(path: string): Promise { + return statAsync(path).then( + () => { + return true; + }, + () => { + return false; + }); } + export function existsSync(path: string): boolean { return fs.existsSync(path); } export function readdirSync(path: string): string[] { return fs.readdirSync(path, 'utf8'); +} + +export type FSWatcher = fs.FSWatcher; + +export function watch(path: string): FSWatcher { + return fs.watch(path); } \ No newline at end of file diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index dd4099d7..4bb99c2c 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -14,13 +14,13 @@ import * as sinon from 'sinon'; import {EventEmitter} from 'events'; import {TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, TestSuiteInfo, TestInfo, TestAdapter} from 'vscode-test-adapter-api'; import {Log} from 'vscode-test-adapter-util'; -import {inspect} from 'util'; +import {inspect, promisify} from 'util'; -import {C2AllTestSuiteInfo} from '../C2AllTestSuiteInfo'; import {C2TestAdapter} from '../C2TestAdapter'; import {example1} from './example1'; import {ChildProcessStub} from './Helpers'; import * as c2fs from '../FsWrapper'; +import * as Mocha from 'mocha'; assert.notEqual(vscode.workspace.workspaceFolders, undefined); assert.equal(vscode.workspace.workspaceFolders!.length, 1); @@ -30,10 +30,6 @@ const workspaceFolderUri = vscode.workspace.workspaceFolders![0].uri; const workspaceFolder = vscode.workspace.getWorkspaceFolder(workspaceFolderUri)!; -const workspaceFolderMatcher = - sinon.match(new RegExp('out(/|\\\\)test')) - .and(sinon.match(new RegExp('^((?!\\.vscode).)*$'))); - const logger = new Log('Catch2TestAdapter', workspaceFolder, 'Catch2TestAdapter'); @@ -62,9 +58,8 @@ describe('C2TestAdapter', function() { let spawnStub: sinon.SinonStub; let fsWatchStub: sinon.SinonStub; - let fsExistsStub: sinon.SinonStub; + let c2fsStatStub: sinon.SinonStub; let c2fsReaddirSyncStub: sinon.SinonStub; - let c2fsStatSyncStub: sinon.SinonStub; function resetConfig(): Thenable { const packageJson = fse.readJSONSync( @@ -75,7 +70,7 @@ describe('C2TestAdapter', function() { Object.keys(properties).forEach(key => { assert.ok(key.startsWith('catch2TestExplorer.')); const k = key.replace('catch2TestExplorer.', '') - t = t.then(() => { + t = t.then(function() { return getConfig().update(k, undefined); }); }); @@ -119,6 +114,21 @@ describe('C2TestAdapter', function() { return adapter!; } + async function doAndWaitForReloadEvent( + action: Function, timeout: number = 1000): Promise { + const origCount = testsEvents.length; + await action(); + const start = Date.now(); + while (testsEvents.length != origCount + 2 && + (Date.now() - start) < timeout) + await promisify(setTimeout)(10); + assert.equal(testsEvents.length, origCount + 2); + const e = testsEvents[testsEvents.length - 1]!; + assert.equal(e.type, 'finished'); + assert.ok(e.suite != undefined); + return e.suite!; + } + function disposeAdapterAndSubscribers() { adapter && adapter.dispose(); testsEventsConnection && testsEventsConnection.dispose(); @@ -127,46 +137,34 @@ describe('C2TestAdapter', function() { testStatesEvents = []; } - function stubsThrowByDefault() { - spawnStub.withArgs(workspaceFolderMatcher).callsFake((...args: any[]) => { - throw new Error(inspect(['spawnStub', args])); - }); - fsWatchStub.withArgs(workspaceFolderMatcher).callsFake((...args: any[]) => { - throw new Error(inspect(['fsWatchStub', args])); - }); - fsExistsStub.withArgs(workspaceFolderMatcher) - .callsFake((...args: any[]) => { - throw new Error(inspect(['fsExistsStub', args])); - }); - } - - function stubsResetToThrow() { + function stubsResetToMyDefault() { spawnStub.reset(); + spawnStub.callThrough(); fsWatchStub.reset(); - fsExistsStub.reset(); + fsWatchStub.callThrough(); + c2fsStatStub.reset(); + c2fsStatStub.callThrough(); c2fsReaddirSyncStub.reset(); - c2fsStatSyncStub.reset(); - stubsThrowByDefault(); - // TODO stub.callThrough(); + c2fsReaddirSyncStub.throws('Test isnt set properly error.'); } - before(() => { + before(function() { fse.removeSync(dotVscodePath); adapter = undefined; - spawnStub = sinonSandbox.stub(child_process, 'spawn'); - fsWatchStub = sinonSandbox.stub(fs, 'watch'); - fsExistsStub = sinonSandbox.stub(fs, 'exists'); - c2fsReaddirSyncStub = sinonSandbox.stub(c2fs, 'readdirSync'); - c2fsStatSyncStub = sinonSandbox.stub(c2fs, 'statSync'); + spawnStub = sinonSandbox.stub(child_process, 'spawn').named('spawnStub'); + fsWatchStub = sinonSandbox.stub(fs, 'watch').named('fsWatchStub'); + c2fsStatStub = sinonSandbox.stub(fs, 'stat').named('fsStat'); + c2fsReaddirSyncStub = + sinonSandbox.stub(c2fs, 'readdirSync').named('c2fsReaddirSyncStub'); - stubsResetToThrow(); + stubsResetToMyDefault(); // reset config can cause problem with fse.removeSync(dotVscodePath); return resetConfig(); }); - after(() => { + after(function() { disposeAdapterAndSubscribers(); sinonSandbox.restore(); }); @@ -174,616 +172,94 @@ describe('C2TestAdapter', function() { describe('detect config change', function() { this.slow(150); - const waitForReloadAndAssert = (): Promise => { - const waitForReloadAndAssertInner = (tryCount: number): Promise => { - if (testsEvents.length < 2) - return new Promise(r => setTimeout(r, 10)) - .then(() => {waitForReloadAndAssertInner(tryCount - 1)}); - else { - assert.equal(testsEvents.length, 2); - assert.equal(testsEvents[0].type, 'started'); - assert.equal(testsEvents[1].type, 'finished'); - const suite = (testsEvents[1]).suite; - assert.notEqual(suite, undefined); - assert.equal(suite!.children.length, 0); - return Promise.resolve(); - } - }; - return waitForReloadAndAssertInner(20); - }; - - afterEach(() => { - disposeAdapterAndSubscribers(); - return resetConfig(); - }); - - it('workerMaxNumber', () => { - createAdapterAndSubscribe(); - assert.deepStrictEqual(testsEvents, []); - return getConfig() - .update('workerMaxNumber', 42) - .then(waitForReloadAndAssert); - }); - - it('defaultEnv', () => { - createAdapterAndSubscribe(); - assert.deepStrictEqual(testsEvents, []); - return getConfig() - .update('defaultEnv', {'APPLE': 'apple'}) - .then(waitForReloadAndAssert); - }); - - it('defaultCwd', () => { - createAdapterAndSubscribe(); - assert.deepStrictEqual(testsEvents, []); - return getConfig() - .update('defaultCwd', 'apple/peach') - .then(waitForReloadAndAssert); - }); - - it('enableSourceDecoration', () => { - const adapter = createAdapterAndSubscribe(); - assert.deepStrictEqual(testsEvents, []); - return getConfig().update('enableSourceDecoration', false).then(() => { - assert.ok(!adapter.getIsEnabledSourceDecoration()); - }); - }); - - it('defaultRngSeed', () => { - const adapter = createAdapterAndSubscribe(); - assert.deepStrictEqual(testsEvents, []); - return getConfig().update('defaultRngSeed', 987).then(() => { - assert.equal(adapter.getRngSeed(), 987); - }); - }); - }); - // describe('detect config change' - - describe('adapter:', () => { let adapter: C2TestAdapter; - beforeEach(() => { + before(function() { adapter = createAdapterAndSubscribe(); - }); + assert.deepStrictEqual(testsEvents, []); + }) - afterEach(() => { + after(function() { disposeAdapterAndSubscribers(); - }); - - it('fill with empty config', function() { - return adapter.load().then(() => { - assert.equal(testsEvents.length, 2); - assert.equal(testsEvents[0].type, 'started'); - assert.equal(testsEvents[1].type, 'finished'); - const suite = (testsEvents[1]).suite; - assert.notEqual(suite, undefined); - assert.equal(suite!.children.length, 0); - }); - }); - - describe('example1', function() { - let tests: any; - - before(() => { - for (let suite of example1.outputs) { - for (let scenario of suite[1]) { - spawnStub.withArgs(suite[0], scenario[0]).callsFake(() => { - return new ChildProcessStub(scenario[1]); - }); - } - } - }) + return resetConfig(); + }) - after(() => { - stubsResetToThrow(); + it('workerMaxNumber', function() { + return doAndWaitForReloadEvent(() => { + return updateConfig('workerMaxNumber', 42); }); + }) - beforeEach(() => { - return adapter.load() - .then(() => { - const root = (testsEvents[1]).suite; - assert.notEqual(undefined, root); - return root!; - }) - .then((suite: TestSuiteInfo) => { - const root = suite; - const s1 = - root.createChildSuite('s1', example1.suite1.execPath, {}); - const s1t1 = s1.createChildTest( - 's1t1', 'd', ['tag1'], example1.suite1.execPath, 1); - const s1t2 = s1.createChildTest( - 's1t2', 'd', ['tag1'], example1.suite1.execPath, 2); - const s2 = - root.createChildSuite('s2', example1.suite2.execPath, {}); - const s2t1 = s2.createChildTest( - 's2t1', 'd', ['tag1'], example1.suite2.execPath, 1); - const s2t2 = s2.createChildTest( - 's2t2', 'd', ['[.]'], example1.suite2.execPath, 2); - const s2t3 = s2.createChildTest( - 's2t3', 'd', ['tag1'], example1.suite2.execPath, 3); - - tests = { - root: root, - s1: s1, - s1t1: s1t1, - s1t2: s1t2, - s2: s2, - s2t1: s2t1, - s2t2: s2t2, - s2t3: s2t3 - } - }); - }) - - it('run: 1 test (succ)', function() { - return adapter.run([tests.s1t1.id]).then(() => { - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s1t1.id]}, - {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'test', state: 'running', test: tests.s1t1}, - { - type: 'test', - state: 'passed', - test: tests.s1t1, - decorations: undefined, - message: 'Duration: 0.000112 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: tests.s1}, - {type: 'finished'}, - ]); - }); - }) - - it('run: 1 test (skipped)', function() { - return adapter.run([tests.s2t2.id]).then(() => { - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s2t2.id]}, - {type: 'suite', state: 'running', suite: tests.s2}, - {type: 'test', state: 'running', test: tests.s2t2}, - { - type: 'test', - state: 'passed', - test: tests.s2t2, - decorations: undefined, - message: 'Duration: 0.001294 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: tests.s2}, - {type: 'finished'}, - ]); - }); + it('defaultEnv', function() { + return doAndWaitForReloadEvent(() => { + return updateConfig('defaultEnv', {'APPLE': 'apple'}); }); - // it('run: 1 test (skipped)' + }) - it('run: 1 test (fails)', function() { - return adapter.run([tests.s2t3.id]).then(() => { - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s2t3.id]}, - {type: 'suite', state: 'running', suite: tests.s2}, - {type: 'test', state: 'running', test: tests.s2t3}, - { - type: 'test', - state: 'failed', - test: tests.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: tests.s2}, - {type: 'finished'}, - ]); - }); + it('defaultCwd', function() { + return doAndWaitForReloadEvent(() => { + return updateConfig('defaultCwd', 'apple/peach'); }); - // it('run: 1 test (fails)' - - it('run: s2t3 with chunks', function() { - const withArgs = spawnStub.withArgs( - tests.s2.execPath, example1.suite2.t3.outputs[0][0]); - withArgs.onCall(withArgs.callCount) - .returns(new ChildProcessStub(example1.suite2.t3.outputs[0][1])); + }) - return adapter.run([tests.s2t3.id]).then(() => { - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s2t3.id]}, - {type: 'suite', state: 'running', suite: tests.s2}, - {type: 'test', state: 'running', test: tests.s2t3}, - { - type: 'test', - state: 'failed', - test: tests.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: tests.s2}, - {type: 'finished'}, - ]); - }); + it('enableSourceDecoration', function() { + return updateConfig('enableSourceDecoration', false).then(function() { + assert.ok(!adapter.getIsEnabledSourceDecoration()); }); - // it('run: 1 test (fails) with chunks' + }) - it('run: suite1 (1 succ 1 fails)', function() { - return adapter.run([tests.s1.id]).then(() => { - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s1.id]}, - {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'test', state: 'running', test: tests.s1t1}, - { - type: 'test', - state: 'passed', - test: tests.s1t1, - decorations: undefined, - message: 'Duration: 0.000132 second(s)\n' - }, - {type: 'test', state: 'running', test: tests.s1t2}, - { - type: 'test', - state: 'failed', - test: tests.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: tests.s1}, - {type: 'finished'}, - ]); - }); + it('defaultRngSeed', function() { + return updateConfig('defaultRngSeed', 987).then(function() { + assert.equal(adapter.getRngSeed(), 987); }); - // it('run: suite1 (1 succ 1 fails)' - - it('run: root (at least 2 slots)', function() { - return adapter.run([tests.root.id]).then(() => { - assert.deepStrictEqual( - {type: 'started', tests: [tests.root.id]}, testStatesEvents[0]); - assert.deepStrictEqual( - {type: 'finished'}, - testStatesEvents[testStatesEvents.length - 1]); - - const s1running = {type: 'suite', state: 'running', suite: tests.s1}; - const s1finished = { - type: 'suite', - state: 'completed', - suite: tests.s1 - }; - assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); - - const s2running = {type: 'suite', state: 'running', suite: tests.s2}; - const s2finished = { - type: 'suite', - state: 'completed', - suite: tests.s2 - }; - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); - - const s1t1running = { - type: 'test', - state: 'running', - test: tests.s1t1 - }; - assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t1running)); - - const s1t1finished = { - type: 'test', - state: 'passed', - test: tests.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: tests.s1t2 - }; - assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t2running)); - - const s1t2finished = { - type: 'test', - state: 'failed', - test: tests.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: tests.s2t1 - }; - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t1running)); - - const s2t1finished = { - type: 'test', - state: 'passed', - test: tests.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: tests.s2t2 - }; - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t2running)); - - const s2t2finished = { - type: 'test', - state: 'skipped', - test: tests.s2t2 - }; - assert.ok(testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); - assert.ok(testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); - - const s2t3running = { - type: 'test', - state: 'running', - test: tests.s2t3 - }; - assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t3running)); - - const s2t3finished = { - type: 'test', - state: 'failed', - test: tests.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)); - - assert.equal(testStatesEvents.length, 16, inspect(testStatesEvents)); - }); - }) - - it('run: wrong xml 1', async function() { - const m = example1.suite1.t1.outputs[0][1].match(']+>'); - assert.notEqual(m, undefined); - assert.notEqual(m!.input, undefined); - assert.notEqual(m!.index, undefined); - const part = m!.input!.substr(0, m!.index! + m![0].length); - const withArgs = spawnStub.withArgs( - tests.s1.execPath, example1.suite1.t1.outputs[0][0]); - withArgs.onCall(withArgs.callCount).returns(new ChildProcessStub(part)); - - await adapter.run([tests.s1t1.id]); - - const expected = [ - {type: 'started', tests: [tests.s1t1.id]}, - {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'test', state: 'running', test: tests.s1t1}, - { - type: 'test', - state: 'failed', - test: tests.s1t1, - message: 'Unexpected test error. (Is Catch2 crashed?)\n' - }, - {type: 'suite', state: 'completed', suite: tests.s1}, - {type: 'finished'}, - ]; - assert.deepStrictEqual(testStatesEvents, expected); - - // this tests the sinon stubs too - await adapter.run([tests.s1t1.id]); - assert.deepStrictEqual(testStatesEvents, [ - ...expected, - {type: 'started', tests: [tests.s1t1.id]}, - {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'test', state: 'running', test: tests.s1t1}, - { - type: 'test', - state: 'passed', - test: tests.s1t1, - decorations: undefined, - message: 'Duration: 0.000112 second(s)\n' - }, - {type: 'suite', state: 'completed', suite: tests.s1}, - {type: 'finished'}, - ]); - }) - - it('cancel: empty', function() { - adapter.cancel(); - }) - - it('cancel', function() { - const suite1Kill = sinon.spy(); - const suite2Kill = sinon.spy(); - { - const spawnEvent = - new ChildProcessStub(example1.suite1.outputs[2][1]); - spawnEvent.kill = suite1Kill; - spawnStub - .withArgs( - tests.s1.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s1.execOptions) - .returns(spawnEvent); - } - { - const spawnEvent = - new ChildProcessStub(example1.suite2.outputs[2][1]); - spawnEvent.kill = suite2Kill; - spawnStub - .withArgs( - tests.s2.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s2.execOptions) - .returns(spawnEvent); - } - const run = adapter.run([tests.root.id]); - adapter.cancel(); - run.then(() => { - assert.deepStrictEqual( - testStatesEvents, - [{type: 'started', tests: [tests.root.id]}, {type: 'finished'}]); - assert.equal(suite1Kill.callCount, 1); - assert.equal(suite2Kill.callCount, 1); - }); - }) - - it('cancel: after run finished', function() { - const suite1Kill = sinon.spy(); - const suite2Kill = sinon.spy(); - { - const spawnEvent = - new ChildProcessStub(example1.suite1.outputs[2][1]); - spawnEvent.kill = suite1Kill; - spawnStub - .withArgs( - tests.s1.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s1.execOptions) - .returns(spawnEvent); - } - { - const spawnEvent = - new ChildProcessStub(example1.suite2.outputs[2][1]); - spawnEvent.kill = suite2Kill; - spawnStub - .withArgs( - tests.s2.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s2.execOptions) - .returns(spawnEvent); - } - const run = adapter.run([tests.root.id]); - run.then(() => { - adapter.cancel(); - assert.equal(suite1Kill.callCount, 0); - assert.equal(suite2Kill.callCount, 0); - }); - }) }) }) - describe('executables:', function() { - this.slow(150); - const cwd = path.join(process.cwd(), 'out', 'test'); - - afterEach(() => { - disposeAdapterAndSubscribers(); - stubsResetToThrow(); - return resetConfig(); - }); - - const updateAndVerify = (value: any, expected: any[]) => { - return getConfig() - .update('executables', value) - .then(() => { - const adapter = createAdapterAndSubscribe(); - - const verifyIsCatch2TestExecutable = - sinonSandbox.stub(adapter, 'verifyIsCatch2TestExecutable'); - verifyIsCatch2TestExecutable.returns(Promise.resolve(true)); - - const loadSuiteMock = sinon.expectation.create('loadSuiteMock'); - loadSuiteMock.exactly(expected.length).returns(Promise.resolve()) - sinonSandbox.replace(adapter, 'loadSuite', loadSuiteMock); - - return adapter.load().then(() => { - return loadSuiteMock; - }); - }) - .then((loadSuiteMock) => { - assert.equal(testsEvents.length, 2); - loadSuiteMock.verify(); - const calls = loadSuiteMock.getCalls(); - const args = calls.map((call: any) => { - const arg = call.args[0]; - const filteredKeys = - Object.keys(arg.env).filter(k => k.startsWith('C2TEST')); - const newEnv: {[prop: string]: string} = {}; - filteredKeys.forEach((k: string) => { - newEnv[k] = arg.env[k]; - }) - arg.env = newEnv; - return arg; - }); - assert.deepEqual(args, expected); - }); - }; - - it('"exe1.exe"', () => { - return updateAndVerify('exe1.exe', [{ - name: 'exe1.exe', - path: path.join(cwd, 'exe1.exe'), - regex: '', - cwd: cwd, - env: {} - }]); - }); - - it('["exe1.exe", "exe2.exe"]', () => { - return updateAndVerify(['exe1.exe', 'exe2.exe'], [ - { - name: 'exe1.exe', - path: path.join(cwd, 'exe1.exe'), - regex: '', - cwd: cwd, - env: {} - }, - { - name: 'exe2.exe', - path: path.join(cwd, 'exe2.exe'), - regex: '', - cwd: cwd, - env: {} - } - ]); - }); - - it('{path: "path1"}', () => { - return updateAndVerify({path: 'path1'}, [{ - name: '${dirname} : ${name}', - path: path.join(cwd, 'path1'), - regex: '', - cwd: cwd, - env: {} - }]); - }); + it('load with empty config', async function() { + const adapter = createAdapterAndSubscribe(); + await adapter.load(); + assert.equal(testsEvents.length, 2); + assert.equal(testsEvents[0].type, 'started'); + assert.equal(testsEvents[1].type, 'finished'); + const suite = (testsEvents[1]).suite; + assert.notEqual(suite, undefined); + assert.equal(suite!.children.length, 0); + disposeAdapterAndSubscribers(); }) - describe('load: example1', function() { - before(() => { + context('example1', function() { + const watchers: Map = new Map(); + + before(function() { for (let suite of example1.outputs) { for (let scenario of suite[1]) { - spawnStub.withArgs(suite[0], scenario[0]).callsFake(() => { + spawnStub.withArgs(suite[0], scenario[0]).callsFake(function() { return new ChildProcessStub(scenario[1]); }); } - } - - const exists = (path: string) => { - return example1.outputs.findIndex((v) => { - return v[0] == path; - }) != -1; - }; - fsExistsStub.withArgs(workspaceFolderMatcher) - .callsFake(function( - path: string, cb: (err: any, exists: boolean) => void) { - cb(undefined, exists(path)); - }); + c2fsStatStub.withArgs(suite[0]).callsFake( + (path: string, + cb: (err: NodeJS.ErrnoException|null, stats: fs.Stats|undefined) => + void) => { + cb(null, { + isFile() { + return true; + }, + isDirectory() { + return false; + } + }); + }); - fsWatchStub.withArgs(workspaceFolderMatcher).callsFake((path: string) => { - if (exists(path)) { - const ee = new class extends EventEmitter { + fsWatchStub.withArgs(suite[0]).callsFake((path: string) => { + const e = new class extends EventEmitter { close() {} }; - watchEvents.set(path, ee); - return ee; - } else { - throw Error('File not found?'); - } - }); + watchers.set(path, e); + return e; + }); + } const dirContent: Map = new Map(); for (let p of example1.outputs) { @@ -799,224 +275,184 @@ describe('C2TestAdapter', function() { dirContent.forEach((v: string[], k: string) => { c2fsReaddirSyncStub.withArgs(k).returns(v); }); - - c2fsStatSyncStub.callsFake((p: string) => { - if (dirContent.has(p)) - return { - isFile() { - return false; - }, - isDirectory() { - return true; - } - }; - const pa = dirContent.get(path.dirname(p)); - if (pa != undefined && pa.indexOf(path.basename(p)) != -1) - return { - isFile() { - return true; - }, - isDirectory() { - return false; - } - }; - throw Error(inspect(['c2fsStatSyncStub', p])); - }); }) - after(() => { - stubsResetToThrow(); + after(function() { + stubsResetToMyDefault(); }) - const uniqueIdC = new Set(); - const watchEvents: Map = new Map(); - let adapter: TestAdapter; - let root: TestSuiteInfo; - - beforeEach(async function() { - adapter = createAdapterAndSubscribe(); - await adapter.load(); - - assert.equal(testsEvents.length, 2, inspect(testsEvents)); - assert.equal(testsEvents[1].type, 'finished'); - assert.ok((testsEvents[1]).suite); - root = (testsEvents[1]).suite!; - testsEvents.pop(); - testsEvents.pop(); + afterEach(function() { + watchers.clear(); + }) - example1.assertWithoutChildren(root, uniqueIdC); - assert.deepStrictEqual(testStatesEvents, []); - }); + describe('load', function() { + const uniqueIdC = new Set(); + let adapter: TestAdapter; - afterEach(() => { - uniqueIdC.clear(); - watchEvents.clear(); - disposeAdapterAndSubscribers(); - }); + let root: TestSuiteInfo; + let suite1: TestSuiteInfo|any; + let s1t1: TestInfo|any; + let s1t2: TestInfo|any; + let suite2: TestSuiteInfo|any; + let s2t1: TestInfo|any; + let s2t2: TestInfo|any; + let s2t3: TestInfo|any; - context('executables="execPath1"', function() { - before(() => { - return updateConfig('executables', 'execPath1'); - }); + beforeEach(async function() { + adapter = createAdapterAndSubscribe(); + await adapter.load(); - after(() => { - return updateConfig('executables', undefined); + assert.equal(testsEvents.length, 2, inspect(testsEvents)); + assert.equal(testsEvents[1].type, 'finished'); + assert.ok((testsEvents[1]).suite); + root = (testsEvents[1]).suite!; + testsEvents.pop(); + testsEvents.pop(); + + suite1 = undefined; + s1t1 = undefined; + s1t2 = undefined; + suite2 = undefined; + s2t1 = undefined; + s2t2 = undefined; + s2t3 = undefined; + + example1.assertWithoutChildren(root, uniqueIdC); + assert.deepStrictEqual(testStatesEvents, []); }); - let suite1: TestSuiteInfo; - let s1t1: TestInfo; - let s1t2: TestInfo; - - beforeEach(async function() { - assert.deepStrictEqual( - getConfig().get('executables'), 'execPath1'); - assert.equal(root.children.length, 1); - assert.equal(root.children[0].type, 'suite'); - suite1 = root.children[0]; - example1.suite1.assert( - 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); - assert.equal(suite1.children.length, 2); - assert.equal(suite1.children[0].type, 'test'); - s1t1 = suite1.children[0]; - assert.equal(suite1.children[1].type, 'test'); - s1t2 = suite1.children[1]; + afterEach(function() { + uniqueIdC.clear(); + disposeAdapterAndSubscribers(); }); - it('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'}, - ]); - }); + context('executables="execPath1"', function() { + before(function() { + return updateConfig('executables', 'execPath1'); + }); - it('should run s1t1 with success', async function() { - assert.equal(getConfig().get('executables'), 'execPath1'); - 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); + after(function() { + return updateConfig('executables', undefined); + }); - await adapter.run([s1t1.id]); - assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); - }); + beforeEach(async function() { + assert.deepStrictEqual( + getConfig().get('executables'), 'execPath1'); + assert.equal(root.children.length, 1); + assert.equal(root.children[0].type, 'suite'); + suite1 = root.children[0]; + example1.suite1.assert( + 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); + assert.equal(suite1.children.length, 2); + assert.equal(suite1.children[0].type, 'test'); + s1t1 = suite1.children[0]; + assert.equal(suite1.children[1].type, 'test'); + s1t2 = suite1.children[1]; + }); - it('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); + it('should run with not existing test id', async function() { + await adapter.run(['not existing id']); - await adapter.run([suite1.id]); - assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); - }); + assert.deepStrictEqual(testStatesEvents, [ + {type: 'started', tests: ['not existing id']}, + {type: 'finished'}, + ]); + }); - it('should run all', async function() { - await adapter.run([root.id]); - const expected = [ - {type: 'started', tests: [root.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); + it('should run s1t1 with success', async function() { + assert.equal(getConfig().get('executables'), 'execPath1'); + 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([root.id]); - assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); - }); + await adapter.run([s1t1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }); - it('cancels without any problem', async function() { - adapter.cancel(); - assert.deepStrictEqual(testsEvents, []); - assert.deepStrictEqual(testStatesEvents, []); + it('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); - adapter.cancel(); - assert.deepStrictEqual(testsEvents, []); - assert.deepStrictEqual(testStatesEvents, []); + await adapter.run([suite1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }); - 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); + it('should run all', async function() { + await adapter.run([root.id]); + const expected = [ + {type: 'started', tests: [root.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); - adapter.cancel(); - assert.deepStrictEqual(testsEvents, []); - assert.deepStrictEqual(testStatesEvents, expected); - }); + await adapter.run([root.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }); - context('with config: defaultRngSeed=2', function() { - before(() => { - return updateConfig('defaultRngSeed', 2); - }) + it('cancels without any problem', async function() { + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, []); - after(() => { - return updateConfig('defaultRngSeed', undefined); - }) + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, []); - it('should run s1t1 with success', async function() { await adapter.run([s1t1.id]); const expected = [ {type: 'started', tests: [s1t1.id]}, @@ -1027,446 +463,953 @@ describe('C2TestAdapter', function() { state: 'passed', test: s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000327 second(s)\n' + 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]); - }) - }) - }) - - context('executables=["execPath1", "${workspaceFolder}/execPath2"]', () => { - before(() => { - return updateConfig( - 'executables', ['execPath1', '${workspaceFolder}/execPath2']); - }); + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, expected); + }); - after(() => { - return updateConfig('executables', undefined); - }); + context('with config: defaultRngSeed=2', function() { + before(function() { + return updateConfig('defaultRngSeed', 2); + }) - let suite1: TestSuiteInfo; - let s1t1: TestInfo; - let s1t2: TestInfo; - let suite2: TestSuiteInfo; - let s2t1: TestInfo; - let s2t2: TestInfo; - let s2t3: TestInfo; + after(function() { + return updateConfig('defaultRngSeed', undefined); + }) - beforeEach(async function() { - assert.deepStrictEqual( - getConfig().get('executables'), - ['execPath1', '${workspaceFolder}/execPath2']); - assert.equal(root.children.length, 2); - - assert.equal(root.children[0].type, 'suite'); - assert.equal(root.children[1].type, 'suite'); - suite1 = root.children[0]; - suite2 = root.children[1]; - if (suite2.label == 'execPath1') { - suite1 = root.children[1]; - suite2 = root.children[0]; - } + it('should run s1t1 with success', 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: + 'Randomness seeded to: 2\nDuration: 0.000327 second(s)\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'}, + ]; + assert.deepStrictEqual(testStatesEvents, expected); - example1.suite1.assert( - 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); - assert.equal(suite1.children.length, 2); - assert.equal(suite1.children[0].type, 'test'); - s1t1 = suite1.children[0]; - assert.equal(suite1.children[1].type, 'test'); - s1t2 = suite1.children[1]; - - example1.suite2.assert( - path.join(workspaceFolderUri.path, 'execPath2'), - ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); - assert.equal(suite2.children.length, 3); - assert.equal(suite2.children[0].type, 'test'); - s2t1 = suite2.children[0]; - assert.equal(suite2.children[1].type, 'test'); - s2t2 = suite2.children[1]; - assert.equal(suite2.children[2].type, 'test'); - s2t3 = suite2.children[2]; - }); + await adapter.run([s1t1.id]); + assert.deepStrictEqual( + testStatesEvents, [...expected, ...expected]); + }) + }) + }) - it('should run with not existing test id', async function() { - await adapter.run(['not existing id']); + context('suite1 and suite2 are used', function() { + let suite1Watcher: EventEmitter; + + beforeEach(function() { + assert.equal(root.children.length, 2); + + assert.equal(root.children[0].type, 'suite'); + assert.equal(root.children[1].type, 'suite'); + assert.equal(example1.suite1.outputs.length, 4 + 2 * 2); + assert.equal(example1.suite2.outputs.length, 4 + 2 * 3); + suite1 = root.children[0]; + suite2 = root.children[1]; + if (suite2.children.length == 2) { + suite1 = root.children[1]; + suite2 = root.children[0]; + } - assert.deepStrictEqual(testStatesEvents, [ - {type: 'started', tests: ['not existing id']}, - {type: 'finished'}, - ]); - }); + assert.equal(suite1.children.length, 2); + assert.equal(suite1.children[0].type, 'test'); + s1t1 = suite1.children[0]; + assert.equal(suite1.children[1].type, 'test'); + s1t2 = suite1.children[1]; + + assert.equal(suite2.children.length, 3); + assert.equal(suite2.children[0].type, 'test'); + s2t1 = suite2.children[0]; + assert.equal(suite2.children[1].type, 'test'); + s2t2 = suite2.children[1]; + assert.equal(suite2.children[2].type, 'test'); + s2t3 = suite2.children[2]; + + assert.equal(watchers.size, 2); + assert.ok(watchers.has(example1.suite1.execPath)); + suite1Watcher = watchers.get(example1.suite1.execPath)!; + }) - it('should run s1t1 with success', 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'}, + 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)); + + 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)); + }), + 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.notEqual(m, undefined); + assert.notEqual(m!.input, undefined); + assert.notEqual(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)); + + 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); + }); + }), + new Mocha.Test( + 'reload because of fswatcher event: touch', + async function(this: Mocha.Context) { + this.slow(200); + const newRoot = await doAndWaitForReloadEvent(async () => { + suite1Watcher.emit( + 'change', 'dummyEvent', example1.suite1.execPath); + }); + assert.deepStrictEqual(newRoot, root); + }), + new Mocha.Test( + 'reload because of fswatcher event: touch, retry 5 times', + async function(this: Mocha.Context) { + this.timeout(10000); + this.slow(6500); + const newRoot = await doAndWaitForReloadEvent(async () => { + const w = c2fsStatStub.withArgs(example1.suite1.execPath); + for (let cc = 0; cc < 5; cc++) { + w.onCall(w.callCount + cc) + .callsFake( + (path: string, + cb: ( + err: NodeJS.ErrnoException|null|any, + stats: fs.Stats|undefined) => void) => { + cb({ + code: 'ENOENT', + errno: -2, + message: 'ENOENT', + path: path, + syscall: 'stat' + }, + undefined); + }); + } + assert.ok(suite1Watcher.emit( + 'change', 'dummyEvent', example1.suite1.execPath)); + }, 10000); + assert.deepStrictEqual(newRoot, root); + }), + new Mocha.Test( + 'reload because of fswatcher event: test added', + async function(this: Mocha.Context) { + 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'); + 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(async () => { + suite1Watcher.emit( + 'change', 'dummyEvent', example1.suite1.execPath); + }); + + 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]); + } + }), + new Mocha.Test( + 'reload because of fswatcher event: test deleted', + async function(this: Mocha.Context) { + 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(async () => { + suite1Watcher.emit( + 'change', 'dummyEvent', example1.suite1.execPath); + }); + + 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]); + } + }), ]; - assert.deepStrictEqual(testStatesEvents, expected); - - await adapter.run([s1t1.id]); - assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); - }); - it('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); + context( + 'executables=["execPath1", "${workspaceFolder}/execPath2"]', + function() { + before(function() { + return updateConfig( + 'executables', + ['execPath1', '${workspaceFolder}/execPath2']); + }); + + after(function() { + return updateConfig('executables', undefined); + }); + + beforeEach(async function() { + example1.suite1.assert( + 'execPath1', ['s1t1', 's1t2'], suite1, uniqueIdC); + + example1.suite2.assert( + path.join(workspaceFolderUri.path, 'execPath2'), + ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); + }) - await adapter.run([suite1.id]); - assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); - }); + for (let t of testsForAdapterWithSuite1AndSuite2) this.addTest( + t.clone()); + }) - it('should run all', async function() { - 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)); - - 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=[{}] and env={...}', function() { + before(async function() { + await updateConfig('executables', [{ + name: '${dirname}: ${name} (${absDirname})', + path: '.', + regex: 'execPath(1|2)', + cwd: '${workspaceFolder}/cwd', + env: { + 'C2LOCALTESTENV': 'c2localtestenv', + 'C2OVERRIDETESTENV': 'c2overridetestenv-l', + } + }]); + await updateConfig('defaultEnv', { + 'C2GLOBALTESTENV': 'c2globaltestenv', + 'C2OVERRIDETESTENV': 'c2overridetestenv-g', + }); + }); - context('executables=[{...}] and env={...}', function() { - before(async () => { - await updateConfig('executables', [{ - name: '${dirname}: ${name} (${absDirname})', - path: '.', - regex: 'execPath(1|2)', - cwd: '${workspaceFolder}/cwd', - env: { - 'C2LOCALTESTENV': 'c2localtestenv', - 'C2OVERRIDETESTENV': 'c2overridetestenv-l', - } - }]); - await updateConfig('defaultEnv',{ - 'C2GLOBALTESTENV': 'c2globaltestenv', - 'C2OVERRIDETESTENV': 'c2overridetestenv-g', - }); - }); + after(async function() { + await updateConfig('executables', undefined); + await updateConfig('defaultEnv', undefined); + }); - after(async () => { - await updateConfig('executables', undefined); - await updateConfig('defaultEnv', undefined); - }); + beforeEach(async function() { + example1.suite1.assert( + ': execPath1 (' + workspaceFolderUri.path + ')', + ['s1t1', 's1t2'], suite1, uniqueIdC); - let suite1: TestSuiteInfo; - let s1t1: TestInfo; - let s1t2: TestInfo; - let suite2: TestSuiteInfo; - let s2t1: TestInfo; - let s2t2: TestInfo; - let s2t3: TestInfo; + example1.suite2.assert( + ': execPath2 (' + workspaceFolderUri.path + ')', + ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); + }) - beforeEach(async function() { - assert.equal(root.children.length, 2); - - assert.equal(root.children[0].type, 'suite'); - assert.equal(root.children[1].type, 'suite'); - suite1 = root.children[0]; - suite2 = root.children[1]; - - example1.suite1.assert( - ': execPath1 (' + workspaceFolderUri.path + ')', ['s1t1', 's1t2'], - suite1, uniqueIdC); - assert.equal(suite1.children.length, 2); - assert.equal(suite1.children[0].type, 'test'); - s1t1 = suite1.children[0]; - assert.equal(suite1.children[1].type, 'test'); - s1t2 = suite1.children[1]; - - example1.suite2.assert( - ': execPath2 (' + workspaceFolderUri.path + ')', - ['s2t1', 's2t2 [.]', 's2t3'], suite2, uniqueIdC); - assert.equal(suite2.children.length, 3); - assert.equal(suite2.children[0].type, 'test'); - s2t1 = suite2.children[0]; - assert.equal(suite2.children[1].type, 'test'); - s2t2 = suite2.children[1]; - assert.equal(suite2.children[2].type, 'test'); - s2t3 = suite2.children[2]; - }) + for (let t of testsForAdapterWithSuite1AndSuite2) this.addTest( + t.clone()); - it('should run all', async function() { - 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)); - - 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)); + 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.path, '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.path, '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) + } + }) + }) }) - it('should get execution options', async function() { - let called1 = false; - spawnStub - .withArgs( - example1.suite1.execPath, sinon.match.any, sinon.match.any) - .callsFake((p: string, args: string[], ops: any) => { - assert.equal(ops.cwd, path.join(workspaceFolderUri.path, 'cwd')); - assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); - assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); - assert.equal(ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); - called1 = true; - return new ChildProcessStub(example1.suite1.outputs[2][1]); + context( + 'executables=["execPath1", "execPath2", "execPath3"]', + async function() { + before(function() { + return updateConfig( + 'executables', ['execPath1', 'execPath2', 'execPath3']); }); - await adapter.run([suite1.id]); - assert.ok(called1); - - let called2 = false; - spawnStub - .withArgs( - example1.suite2.execPath, sinon.match.any, sinon.match.any) - .callsFake((p: string, args: string[], ops: any) => { - assert.equal(ops.cwd, path.join(workspaceFolderUri.path, 'cwd')); - assert.equal(ops.env.C2LOCALTESTENV, 'c2localtestenv'); - assert.ok(!ops.env.hasOwnProperty('C2GLOBALTESTENV')); - assert.equal(ops.env.C2OVERRIDETESTENV, 'c2overridetestenv-l'); - called2 = true; - return new ChildProcessStub(example1.suite2.outputs[2][1]); + + after(function() { + return updateConfig('executables', undefined); }); - await adapter.run([suite2.id]); - assert.ok(called2); - }); + + it('run suite3 one-by-one', async function() { + this.slow(200); + assert.equal(root.children.length, 3); + assert.equal(root.children[0].type, 'suite'); + const suite3 = root.children[2]; + assert.equal(suite3.children.length, 33); + + spawnStub.withArgs(example1.suite3.execPath).throwsArg(1); + + const runAndCheckEvents = async (test: TestInfo) => { + assert.equal(testStatesEvents.length, 0); + + await adapter.run([test.id]); + + assert.equal(testStatesEvents.length, 6, inspect(test)); + + assert.deepStrictEqual( + {type: 'started', tests: [test.id]}, testStatesEvents[0]); + assert.deepStrictEqual( + {type: 'suite', state: 'running', suite: suite3}, + testStatesEvents[1]); + + assert.equal(testStatesEvents[2].type, 'test'); + assert.equal((testStatesEvents[2]).state, 'running'); + assert.equal((testStatesEvents[2]).test, test); + + assert.equal(testStatesEvents[3].type, 'test'); + assert.ok( + (testStatesEvents[3]).state == 'passed' || + (testStatesEvents[3]).state == 'skipped' || + (testStatesEvents[3]).state == 'failed'); + assert.equal((testStatesEvents[3]).test, test); + + assert.deepStrictEqual( + {type: 'suite', state: 'completed', suite: suite3}, + testStatesEvents[4]); + assert.deepStrictEqual({type: 'finished'}, testStatesEvents[5]); + + while (testStatesEvents.length) testStatesEvents.pop(); + }; + + for (let test of suite3.children) { + assert.equal(test.type, 'test'); + await runAndCheckEvents(test); + } + }) + }) }) - }) -}) -describe.skip('a', function() { - this.timeout(99999); + specify('load executables=', async function() { + this.slow(300); + await updateConfig('executables', example1.suite1.execPath); + adapter = createAdapterAndSubscribe(); + await adapter.load(); + assert.equal(testsEvents.length, 2); + assert.equal( + (testsEvents[testsEvents.length - 1]) + .suite!.children.length, + 1); + await updateConfig('executables', undefined); + }) - before(() => { - debugger; - }); - beforeEach(() => { - debugger; - }); + specify( + 'load executables=["execPath1", "execPath2"] with error', + async function() { + this.slow(300); + await updateConfig('executables', ['execPath1', 'execPath2']) + adapter = createAdapterAndSubscribe(); - after(() => { - debugger; - }); - afterEach(() => { - debugger; - }); + const withArgs = spawnStub.withArgs( + example1.suite2.execPath, example1.suite2.outputs[1][0]); + withArgs.onCall(withArgs.callCount).throws( + 'dummy error for testing (should be handled)'); - it('a-it', () => { - debugger; - }); + await adapter.load(); - describe('b', () => { - before(() => { - debugger; - }); - beforeEach(() => { - debugger; - }); + await updateConfig('executables', undefined); + }) - after(() => { - debugger; - }); - afterEach(() => { - debugger; - }); + specify( + 'load executables=["execPath1", "execPath2Copy"] and delete second because of fswatcher event', + async function(this: Mocha.Context) { + const watchTimeout = 5000; + await updateConfig('defaultExecWatchTimeout', watchTimeout); + this.timeout(watchTimeout + 2500 /* because of 'delay' */); + this.slow(watchTimeout + 2500 /* because of 'delay' */); + const fullPath = path.join(workspaceFolderUri.path, 'execPath2Copy'); + + for (let scenario of example1.suite2.outputs) { + spawnStub.withArgs(fullPath, scenario[0]).callsFake(function() { + return new ChildProcessStub(scenario[1]); + }); + } - it('b-it1', () => { - debugger; - }); + c2fsStatStub.withArgs(fullPath).callsFake( + (path: string, + cb: ( + err: NodeJS.ErrnoException|null, + stats: fs.Stats|undefined) => void) => { + cb(null, { + isFile() { + return true; + }, + isDirectory() { + return false; + } + }); + }); + + fsWatchStub.withArgs(fullPath).callsFake((path: string) => { + const e = new class extends EventEmitter { + close() {} + }; + watchers.set(path, e); + return e; + }); - it('b-it2', () => { - debugger; - }); - }); -}); -// fswatcher test aztan atiras vscode workspace watcherre -// bonyolultabb teszteset parsoleasa de az mehet kulon fileba c2testinfo -// mock getExecutables regex meg sima minden test -// ExecutableConfig -// execOptions -// writing xml -// re-load soame object -// deepstrictequal \ No newline at end of file + await updateConfig('executables', ['execPath1', 'execPath2Copy']) + adapter = createAdapterAndSubscribe(); + + await adapter.load(); + + assert.equal( + (testsEvents[testsEvents.length - 1]) + .suite!.children.length, + 2); + assert.ok(watchers.has(fullPath)); + const watcher = watchers.get(fullPath)!; + const start = Date.now(); + + const newRoot = await doAndWaitForReloadEvent(async () => { + c2fsStatStub.withArgs(fullPath).callsFake( + (path: string, + cb: ( + err: NodeJS.ErrnoException|null|any, + stats: fs.Stats|undefined) => void) => { + cb({ + code: 'ENOENT', + errno: -2, + message: 'ENOENT', + path: path, + syscall: 'stat' + }, + undefined); + }); + assert.ok( + watcher.emit('change', 'dummyEvent', example1.suite1.execPath)); + }, 40000); + + const elapsed = Date.now() - start; + assert.equal(newRoot.children.length, 1); + assert.ok(watchTimeout < elapsed, inspect(elapsed)); + assert.ok(elapsed < watchTimeout + 2400, inspect(elapsed)); + + // restore + for (let scenario of example1.suite2.outputs) { + spawnStub.withArgs(fullPath, scenario[0]).throws(); + } + c2fsStatStub.withArgs(fullPath).throws(); + fsWatchStub.withArgs(fullPath).throws(); + disposeAdapterAndSubscribers(); + await updateConfig('executables', undefined); + await updateConfig('defaultExecWatchTimeout', undefined); + }) + }) +}) \ No newline at end of file diff --git a/src/test/FsWrapper.test.ts b/src/test/FsWrapper.test.ts index a6ea0b33..c4a9fa10 100644 --- a/src/test/FsWrapper.test.ts +++ b/src/test/FsWrapper.test.ts @@ -1,11 +1,19 @@ import * as assert from 'assert'; -import {spawnAsync} from '../FsWrapper'; +import * as path from 'path'; +import * as vscode from 'vscode'; + +import {spawnAsync, statAsync} from '../FsWrapper'; + +assert.notEqual(vscode.workspace.workspaceFolders, undefined); +assert.equal(vscode.workspace.workspaceFolders!.length, 1); + +const workspaceFolderUri = vscode.workspace.workspaceFolders![0].uri; describe('FsWrapper.spawnAsync', function() { it('echoes', async function() { const r = await spawnAsync('echo', ['apple']); assert.equal(r.stdout, 'apple\n'); - assert.equal(r.output.length, 1); + assert.equal(r.output.length, 2); assert.equal(r.output[0], 'apple\n'); assert.equal(r.status, 0); }) @@ -20,4 +28,23 @@ describe('FsWrapper.spawnAsync', function() { assert.equal(r.status, 0); } }) -}); \ No newline at end of file +}) + +describe('FsWrapper.statAsync', function() { + it('doesnt exists', async function() { + try { + await statAsync('notexists'); + assert.ok(false); + } catch (e) { + assert.equal(e.code, 'ENOENT'); + assert.equal(e.errno, -2); + } + }) + + it('exists', async function() { + const res = await statAsync( + path.join(workspaceFolderUri.path, 'FsWrapper.test.js')); + assert.ok(res.isFile()); + assert.ok(!res.isDirectory()); + }) +}) \ No newline at end of file diff --git a/src/test/Helpers.ts b/src/test/Helpers.ts index 5ca63c21..01600375 100644 --- a/src/test/Helpers.ts +++ b/src/test/Helpers.ts @@ -3,10 +3,12 @@ import {Stream} from 'stream'; export class ChildProcessStub extends EventEmitter { readonly stdout = new Stream.Readable(); + public closed: boolean = false; constructor(data?: string|Iterable) { super(); this.stdout.on('end', () => { + this.closed = true; this.emit('close', 1); }); if (data != undefined) { diff --git a/src/test/example1.ts b/src/test/example1.ts index 50783198..b20225c6 100644 --- a/src/test/example1.ts +++ b/src/test/example1.ts @@ -466,6 +466,1677 @@ export const example1 = new class { ]; }; + readonly suite3 = new class { + readonly execPath = path.join(workspaceFolderUri.path, 'execPath3'); + + readonly outputs: [string[], string][] = [ + + [ + ['--help'], ` +Catch v2.4.1 +usage: + suite3 [ ... ] options + +where options are: + -?, -h, --help display usage information + -l, --list-tests list all/matching test cases + -t, --list-tags list all/matching tags + -s, --success include successful tests in + output + -b, --break break into debugger on failure + -e, --nothrow skip exception tests + -i, --invisibles show invisibles (tabs, newlines) + -o, --out output filename + -r, --reporter reporter to use (defaults to + console) + -n, --name suite name + -a, --abort abort at first failure + -x, --abortx abort after x failures + -w, --warn enable warnings + -d, --durations show test durations + -f, --input-file load test names to run from a + file + -#, --filenames-as-tags adds a tag for the filename + -c, --section
specify section to run + -v, --verbosity set output verbosity + --list-test-names-only list all/matching test cases + names only + --list-reporters list all reporters + --order test case order (defaults to + decl) + --rng-seed <'time'|number> set a specific seed for random + numbers + --use-colour should output be colourised + --libidentify report name and version according + to libidentify standard + --wait-for-keypress waits for a keypress before + exiting + --benchmark-resolution-multiple multiple of clock resolution to + run benchmarks + +For more detailed usage please see the project docs + +` + ], + [ + ['[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', 'no'], + `Matching test cases: + test name,with,colon + ../vscode-catch2-test-adapter/src/test/suite3.cpp:8 + tag1 + test name with space + ../vscode-catch2-test-adapter/src/test/suite3.cpp:14 + (NO DESCRIPTION) + SECTION tree + ../vscode-catch2-test-adapter/src/test/suite3.cpp:20 + (NO DESCRIPTION) + spec ! char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:36 + (NO DESCRIPTION) + spec @ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:37 + (NO DESCRIPTION) + spec # char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:38 + (NO DESCRIPTION) + spec $ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:39 + (NO DESCRIPTION) + spec % char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:40 + (NO DESCRIPTION) + spec ^ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:41 + (NO DESCRIPTION) + spec & char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:42 + (NO DESCRIPTION) + spec * char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:43 + (NO DESCRIPTION) + spec (a) char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:44 + (NO DESCRIPTION) + spec {a} char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:45 + (NO DESCRIPTION) + spec [a] char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:46 + (NO DESCRIPTION) + spec ; char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:47 + (NO DESCRIPTION) + spec ' char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:48 + (NO DESCRIPTION) + spec \\ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:49 + (NO DESCRIPTION) + spec , char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:50 + (NO DESCRIPTION) + spec . char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:51 + (NO DESCRIPTION) + spec / char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:52 + (NO DESCRIPTION) + spec < char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:53 + (NO DESCRIPTION) + spec > char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:54 + (NO DESCRIPTION) + spec ? char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:55 + (NO DESCRIPTION) + spec - char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:56 + (NO DESCRIPTION) + spec = char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:57 + (NO DESCRIPTION) + spec _ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:58 + (NO DESCRIPTION) + spec + char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:59 + (NO DESCRIPTION) + spec ~ char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:60 + (NO DESCRIPTION) + spec \` char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:61 + (NO DESCRIPTION) + spec § char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:62 + (NO DESCRIPTION) + spec ± char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:63 + (NO DESCRIPTION) + spec " char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:64 + (NO DESCRIPTION) + spec | char + ../vscode-catch2-test-adapter/src/test/suite3.cpp:65 + (NO DESCRIPTION) +34 matching test cases + +` + ], + [ + ['--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+` + ], + [ + ['--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], + ` + + + + + + + + + + +
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+` + ], + [ + ['test name\\,with\\,colon', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['*test name with space ', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['SECTION tree', '--reporter', 'xml', '--durations', 'yes'], + ` + + + +
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+` + ], + [ + ['spec ! char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec @ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec # char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec $ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec % char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec ^ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec & char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \\* char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \(a\) char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec {a} char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \\[a] char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec ; char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \' char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \\ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \\, char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec . char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec / char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec < char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec > char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec ? char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec - char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec = char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec _ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec + char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec ~ char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec \` char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec § char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec ± char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec " char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + ['spec | char', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + +` + ], + [ + [ + 'test name\,with\,colon', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + '*test name with space ', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'SECTION tree', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + +
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+
+ + + std::false_type::value + + + false + + + +
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+` + ], + [ + [ + 'spec ! char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec @ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec # char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec $ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec % char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec ^ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec & char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \\* char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \(a\) char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec {a} char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \\[a] char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec ; char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \' char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \\\\ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \\, char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec . char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec / char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec < char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec > char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec ? char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec - char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec = char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec _ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec + char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec ~ char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec \` char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec § char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec ± char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec " char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + [ + [ + 'spec | char', '--reporter', 'xml', '--durations', 'yes', + '--rng-seed', '2' + ], + ` + + + + + + + + + + +` + ], + + ]; + }; + assertWithoutChildren(root: TestSuiteInfo, uniqeIdContainer?: Set) { assert.equal(root.type, 'suite'); assert.equal(root.label, 'AllTests'); @@ -479,6 +2150,7 @@ export const example1 = new class { readonly outputs: [string, [string[], string][]][] = [ [this.suite1.execPath, this.suite1.outputs], - [this.suite2.execPath, this.suite2.outputs] + [this.suite2.execPath, this.suite2.outputs], + [this.suite3.execPath, this.suite3.outputs], ]; }; \ No newline at end of file diff --git a/src/test/suite3.cpp b/src/test/suite3.cpp new file mode 100644 index 00000000..596136ef --- /dev/null +++ b/src/test/suite3.cpp @@ -0,0 +1,65 @@ +#define CATCH_CONFIG_MAIN +#include + +// 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 +// clang-format on + +TEST_CASE("test name,with,colon", "tag1") { + // + REQUIRE(std::true_type::value); + // +} + +TEST_CASE(" test name with space ") { + // + REQUIRE(std::true_type::value); + // +} + +TEST_CASE("SECTION tree") { + SECTION("1") { + SECTION("2") { + SECTION("3") { + SECTION("4") { REQUIRE(std::false_type::value); } + } + } + SECTION("2-2") { + SECTION("3") { + SECTION("4") { REQUIRE(std::false_type::value); } + } + } + } +} + +TEST_CASE("name with * character") {} +TEST_CASE("spec ! char") {} +TEST_CASE("spec @ char") {} +TEST_CASE("spec # char") {} +TEST_CASE("spec $ char") {} +TEST_CASE("spec % char") {} +TEST_CASE("spec ^ char") {} +TEST_CASE("spec & char") {} +TEST_CASE("spec * char") {} +TEST_CASE("spec (a) char") {} +TEST_CASE("spec {a} char") {} +TEST_CASE("spec [a] char") {} +TEST_CASE("spec ; char") {} +TEST_CASE("spec ' char") {} +TEST_CASE("spec \\ char") {} +TEST_CASE("spec , char") {} +TEST_CASE("spec . char") {} +TEST_CASE("spec / char") {} +TEST_CASE("spec < char") {} +TEST_CASE("spec > char") {} +TEST_CASE("spec ? char") {} +TEST_CASE("spec - char") {} +TEST_CASE("spec = char") {} +TEST_CASE("spec _ char") {} +TEST_CASE("spec + char") {} +TEST_CASE("spec ~ char") {} +TEST_CASE("spec ` char") {} +TEST_CASE("spec § char") {} +TEST_CASE("spec ± char") {} +TEST_CASE("spec \" char") {} +TEST_CASE("spec | char") {} diff --git a/src/test/suite3generator.sh b/src/test/suite3generator.sh new file mode 100755 index 00000000..5f80f6e5 --- /dev/null +++ b/src/test/suite3generator.sh @@ -0,0 +1,77 @@ + +echo "" >x.txt + +echo -n "[['--help'], \`" >>x.txt; ./suite3 "[.],*" --verbosity high --help >>x.txt; echo "\`]," >>x.txt; +echo -n "[['[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', 'no'], \`" >>x.txt; ./suite3 "[.],*" --verbosity high --list-tests --use-colour no >>x.txt; echo "\`]," >>x.txt ; + +echo -n "[['--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; + +echo -n "[['test name\,with\,colon', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "test name\,with\,colon" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['*test name with space ', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "*test name with space " --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['SECTION tree', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "SECTION tree" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ! char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ! char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec @ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec @ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec # char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec # char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec $ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec $ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec % char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec % char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ^ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ^ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec & char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec & char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec * char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec * char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \(a\) char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec (a) char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec {a} char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec {a} char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \[a] char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec \[a] char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ; char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ; char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\' char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ' char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\\\ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec \\\ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \, char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec \, char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec . char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec . char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec / char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec / char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec < char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec < char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec > char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec > char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ? char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ? char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec - char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec - char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec = char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec = char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec _ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec _ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec + char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec + char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ~ char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ~ char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\\` char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec \` char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec § char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec § char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ± char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec ± char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \" char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec \" char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec | char', '--reporter', 'xml', '--durations', 'yes'], \`" >>x.txt; ./suite3 "spec | char" --reporter xml --durations yes >>x.txt; echo "\`]," >>x.txt ; + +echo -n "[['test name\,with\,colon', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "test name\,with\,colon" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['*test name with space ', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "*test name with space " --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['SECTION tree', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "SECTION tree" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ! char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ! char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec @ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec @ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec # char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec # char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec $ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec $ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec % char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec % char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ^ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ^ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec & char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec & char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec * char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec * char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \(a\) char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec (a) char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec {a} char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec {a} char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \[a] char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec \[a] char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ; char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ; char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\' char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ' char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\\\ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec \\\ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \, char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec \, char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec . char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec . char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec / char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec / char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec < char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec < char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec > char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec > char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ? char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ? char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec - char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec - char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec = char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec = char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec _ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec _ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec + char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec + char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ~ char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ~ char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \\\` char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec \` char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec § char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec § char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec ± char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec ± char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec \" char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec \" char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +echo -n "[['spec | char', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], \`" >>x.txt; ./suite3 "spec | char" --reporter xml --durations yes --rng-seed 2 >>x.txt; echo "\`]," >>x.txt ; +