From 8ff09bd54216e3b8ffc69ada0adfc110adf55563 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Fri, 19 Oct 2018 16:22:25 +0200 Subject: [PATCH 1/7] shields.io --- README.md | 9 +++-- src/C2TestSuiteInfo.ts | 5 ++- src/test/C2TestAdapter.test.ts | 60 ++++++++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 10cc2f9f..88aba60b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # Catch2 Test Explorer for Visual Studio Code [![Build Status](https://travis-ci.org/matepek/vscode-catch2-test-adapter.svg?branch=master)](https://travis-ci.org/matepek/vscode-catch2-test-adapter) +[![GitHub issues](https://img.shields.io/github/issues/matepek/vscode-catch2-test-adapter.svg)](https://github.com/matepek/vscode-catch2-test-adapter/issues) +[![GitHub license](https://img.shields.io/github/license/matepek/vscode-catch2-test-adapter.svg)](https://github.com/matepek/vscode-catch2-test-adapter/blob/master/LICENSE) +[![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/d/matepek.vscode-catch2-test-adapter.svg)](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter) +[![Visual Studio Marketplace](https://img.shields.io/vscode-marketplace/v/matepek.vscode-catch2-test-adapter.svg)](https://marketplace.visualstudio.com/items?itemName=matepek.vscode-catch2-test-adapter) + This extension allows you to run your [Catch2 tests](https://github.com/catchorg/Catch2) using the [Test Explorer for VS Code](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer). @@ -19,6 +24,8 @@ This adapter doesn't support everything. | `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) | +| `testExplorer.onStart` | (This is part of the [dependency extension](https://github.com/hbenl/vscode-test-explorer#configuration)'s settings.) | +| `testExplorer.onReload` | (This is part of the [dependency extension](https://github.com/hbenl/vscode-test-explorer#configuration)'s settings.) | ### catch2TestExplorer.executables @@ -115,9 +122,7 @@ Example: ## TODOs - Implement more [Catch command line options](https://github.com/catchorg/Catch2/blob/master/docs/command-line.md#specifying-which-tests-to-run), such as: - - `--nothrow` - - Tests ## Contribution diff --git a/src/C2TestSuiteInfo.ts b/src/C2TestSuiteInfo.ts index 448e36e4..b6070a1b 100644 --- a/src/C2TestSuiteInfo.ts +++ b/src/C2TestSuiteInfo.ts @@ -256,8 +256,11 @@ export class C2TestSuiteInfo implements TestSuiteInfo { message: 'Unexpected test error. (Is Catch2 crashed?)\n' }); } - this.adapter.log.error(err.message); suiteFinally(); + try { + this.adapter.log.error(err.message); + } catch (e) { + } }); } diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index 3b421e11..cf114d5b 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -26,6 +26,7 @@ const logger = new Log('Catch2TestAdapter', workspaceFolder, 'Catch2TestAdapter'); const spawnStub = sinon.stub(child_process, 'spawn'); +const execFileStub = sinon.stub(child_process, 'execFile'); const dotVscodePath = path.join(workspaceFolderUri.path, '.vscode'); @@ -37,7 +38,7 @@ describe('C2TestAdapter', function() { const disposable: vscode.Disposable[] = []; - let adapter: myExtension.C2TestAdapter; + let adapter: myExtension.C2TestAdapter| undefined; let testsEvents: (TestLoadStartedEvent|TestLoadFinishedEvent)[]; let testStatesEvents: (TestRunStartedEvent|TestRunFinishedEvent| TestSuiteEvent|TestEvent)[]; @@ -64,6 +65,7 @@ describe('C2TestAdapter', function() { testStatesEvents = []; spawnStub.throws(); + execFileStub.throws(); disposable.push( adapter.tests((e: TestLoadStartedEvent|TestLoadFinishedEvent) => { @@ -74,9 +76,11 @@ describe('C2TestAdapter', function() { TestEvent) => { testStatesEvents.push(e); })); + return adapter!; }; beforeEach(() => { + adapter = undefined; fs.removeSync(dotVscodePath); return resetConfig(); }); @@ -126,7 +130,7 @@ describe('C2TestAdapter', function() { }); it('enableSourceDecoration', () => { - createAdapterAndSubscribe(); + const adapter = createAdapterAndSubscribe(); assert.deepEqual(testsEvents, []); return config.update('enableSourceDecoration', false).then(() => { assert.ok(!adapter.getIsEnabledSourceDecoration()); @@ -134,7 +138,7 @@ describe('C2TestAdapter', function() { }); it('defaultRngSeed', () => { - createAdapterAndSubscribe(); + const adapter = createAdapterAndSubscribe(); assert.deepEqual(testsEvents, []); return config.update('defaultRngSeed', 987).then(() => { assert.equal(adapter.getRngSeed(), 987); @@ -145,11 +149,13 @@ describe('C2TestAdapter', function() { // describe('example1' describe('adapter:', () => { + let adapter: myExtension.C2TestAdapter; + beforeEach(() => { - createAdapterAndSubscribe(); + adapter = createAdapterAndSubscribe(); }); - it('load: empty config', function() { + it('fill with empty config', function() { return adapter.load().then(() => { assert.equal(testsEvents.length, 2); assert.equal(testsEvents[0].type, 'started'); @@ -160,7 +166,7 @@ describe('C2TestAdapter', function() { }); }); - describe('load: example1', function() { + describe('fill with example1', function() { let tests: any; const randomnessXml = ``; @@ -697,8 +703,8 @@ describe('C2TestAdapter', function() { .withArgs( tests.s1.execPath, [ - tests.s1t1.testNameFull, '--reporter', 'xml', - '--durations', 'yes' + tests.s1t1.testNameFull, '--reporter', 'xml', '--durations', + 'yes' ], tests.s1.execOptions) .returns(spawnEvent); @@ -707,16 +713,14 @@ describe('C2TestAdapter', function() { assert.deepEqual(testStatesEvents, [ {type: 'started', tests: [tests.s1t1.id]}, {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'test', state:'running', test: tests.s1t1}, - + {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: 'suite', state: 'completed', suite: tests.s1}, {type: 'finished'}, ]); }); @@ -827,7 +831,36 @@ describe('C2TestAdapter', function() { }); // it('cancel: after run finished' }); - // describe('load: example1' + // describe('fill with example1' + + // describe('load', function() { + // it('1', () => { + // const stdout = new Stream.Readable(); + // const spawnEvent: any = new EventEmitter(); + // spawnEvent.stdout = stdout; + // stdout.on('end', () => { + // spawnEvent.emit('close', 1); + // }); + // stdout.push(''); + // stdout.push(null); + + // execFileStub + // .withArgs( + // 'exe.exe', + // [ + // ] + // ) + // .returns(spawnEvent); + // config.update('executables', 'exe.exe').then(()=>{ + // const adapter = createAdapterAndSubscribe(); + + // return adapter.load(); + // }).then(()=>{ + // assert.equal(testsEvents.length, 2); + // }); + // }); + // }); + // describe('fswatcher: suite1 deleted' }); // describe('adapter:' }); @@ -836,7 +869,6 @@ describe('C2TestAdapter', function() { // fswatcher test aztan atiras vscode workspace watcherre // bonyolultabb teszteset parsoleasa de az mehet kulon fileba c2testinfo // mock getExecutables regex meg sima minden test -// cancel test // ExecutableConfig // execOptions // writing xml \ No newline at end of file From 34de5f2a861bcb061fc47473e190dcd94b12c244 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Fri, 19 Oct 2018 20:03:32 +0200 Subject: [PATCH 2/7] executables --- src/C2TestAdapter.ts | 86 ++++++++------- src/test/C2TestAdapter.test.ts | 190 +++++++++++++++++++++++++-------- 2 files changed, 195 insertions(+), 81 deletions(-) diff --git a/src/C2TestAdapter.ts b/src/C2TestAdapter.ts index cd901b4c..9ad00363 100644 --- a/src/C2TestAdapter.ts +++ b/src/C2TestAdapter.ts @@ -97,7 +97,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return this.autorunEmitter.event; } - private loadSuite(exe: ExecutableConfig): Promise { + loadSuite(exe: ExecutableConfig): Promise { const suite = this.allTests.createChildSuite( exe.name, exe.path, {cwd: exe.cwd, env: exe.env}); @@ -106,39 +106,43 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { if (watcher != undefined) { watcher.close(); } - - watcher = fs.watch(suite.execPath); - this.watchers.set(suite.execPath, watcher); - const allTests = this.allTests; // alltest may has changed - - watcher.on('change', (eventType: string, filename: string) => { - // 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(() => { + try { + watcher = fs.watch(suite.execPath); + this.watchers.set(suite.execPath, watcher); + const allTests = this.allTests; // alltest may has changed + + watcher.on('change', (eventType: string, filename: string) => { + // 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}); + }); + } + }; - // change event can arrive during debug session on osx (why?) - if (!this.isDebugging) { - waitAndThenTry(10, 128); - } + // change event can arrive during debug session on osx (why?) + if (!this.isDebugging) { + waitAndThenTry(10, 128); + } + }); + } catch (e) { + this.log.warn('watcher couldn\'t watch: ' + suite.execPath); + } + return suite.reloadChildren().catch((e) => { + this.allTests.removeChild(suite); }); - - return suite.reloadChildren(); } load(): Promise { @@ -172,13 +176,17 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return testListReaders; }) - .then(() => { - this.testsEmitter.fire({type: 'finished', suite: this.allTests}); - }) - .catch((err: Error) => { - this.testsEmitter.fire( - {type: 'finished', suite: undefined, errorMessage: err.message}); - }); + .then( + () => { + this.testsEmitter.fire({type: 'finished', suite: this.allTests}); + }, + (err: Error) => { + this.testsEmitter.fire({ + type: 'finished', + suite: undefined, + errorMessage: err.message + }); + }); } cancel(): void { @@ -498,7 +506,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { return this.filterVerifiedCatch2TestExecutables(executables); } - private verifyIsCatch2TestExecutable(path: string): Promise { + verifyIsCatch2TestExecutable(path: string): Promise { return new Promise((resolve, reject) => { try { execFile( diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index cf114d5b..48ea38c8 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -3,7 +3,8 @@ const child_process = require('child_process'); const deepEqual = require('deep-equal'); import * as path from 'path'; -import * as fs from 'fs-extra'; +import * as fs from 'fs'; +import * as fse from 'fs-extra'; import * as assert from 'assert'; import {EventEmitter} from 'events'; import * as vscode from 'vscode'; @@ -25,11 +26,10 @@ const workspaceFolder = const logger = new Log('Catch2TestAdapter', workspaceFolder, 'Catch2TestAdapter'); -const spawnStub = sinon.stub(child_process, 'spawn'); -const execFileStub = sinon.stub(child_process, 'execFile'); - const dotVscodePath = path.join(workspaceFolderUri.path, '.vscode'); +const sinonSandbox = sinon.createSandbox(); + /// describe('C2TestAdapter', function() { @@ -38,13 +38,17 @@ describe('C2TestAdapter', function() { const disposable: vscode.Disposable[] = []; - let adapter: myExtension.C2TestAdapter| undefined; + let adapter: myExtension.C2TestAdapter|undefined; let testsEvents: (TestLoadStartedEvent|TestLoadFinishedEvent)[]; let testStatesEvents: (TestRunStartedEvent|TestRunFinishedEvent| TestSuiteEvent|TestEvent)[]; + let spawnStub: any; + let fsWatchStub: any; + let fsExistsStub: any; + const resetConfig = function(): Thenable { - const packageJson = fs.readJSONSync( + const packageJson = fse.readJSONSync( path.join(workspaceFolderUri.path, '../..', 'package.json')); const properties: {[prop: string]: any}[] = packageJson['contributes']['configuration']['properties']; @@ -64,9 +68,6 @@ describe('C2TestAdapter', function() { testsEvents = []; testStatesEvents = []; - spawnStub.throws(); - execFileStub.throws(); - disposable.push( adapter.tests((e: TestLoadStartedEvent|TestLoadFinishedEvent) => { testsEvents.push(e); @@ -81,20 +82,32 @@ describe('C2TestAdapter', function() { beforeEach(() => { adapter = undefined; - fs.removeSync(dotVscodePath); + fse.removeSync(dotVscodePath); + + spawnStub = sinonSandbox.stub(child_process, 'spawn'); + spawnStub.throws(); + + fsWatchStub = sinonSandbox.stub(fs, 'watch'); + fsWatchStub.throws(); + + fsExistsStub = sinonSandbox.stub(fs, 'exists'); + fsExistsStub.throws(); + return resetConfig(); }); afterEach(() => { while (disposable.length > 0) disposable.pop()!.dispose(); + sinonSandbox.restore(); }); describe('detect config change', function() { + this.timeout(1000); const waitForReloadAndAssert = () => { const waitForReloadAndAssertInner = (tryCount: number = 20): Promise => { if (testsEvents.length < 2) - return new Promise(r => setTimeout(r, 20)) + return new Promise(r => setTimeout(r, 10)) .then(() => {waitForReloadAndAssertInner(tryCount - 1)}); else { assert.equal(testsEvents.length, 2); @@ -145,8 +158,7 @@ describe('C2TestAdapter', function() { }); }); }); - - // describe('example1' + // describe('detect config change' describe('adapter:', () => { let adapter: myExtension.C2TestAdapter; @@ -832,37 +844,131 @@ describe('C2TestAdapter', function() { // it('cancel: after run finished' }); // describe('fill with example1' - - // describe('load', function() { - // it('1', () => { - // const stdout = new Stream.Readable(); - // const spawnEvent: any = new EventEmitter(); - // spawnEvent.stdout = stdout; - // stdout.on('end', () => { - // spawnEvent.emit('close', 1); - // }); - // stdout.push(''); - // stdout.push(null); - - // execFileStub - // .withArgs( - // 'exe.exe', - // [ - // ] - // ) - // .returns(spawnEvent); - // config.update('executables', 'exe.exe').then(()=>{ - // const adapter = createAdapterAndSubscribe(); - - // return adapter.load(); - // }).then(()=>{ - // assert.equal(testsEvents.length, 2); - // }); - // }); - // }); - // describe('fswatcher: suite1 deleted' }); // describe('adapter:' + + describe('executables:', function() { + const cwd = path.join(process.cwd(), 'out', 'test'); + + const updateAndVerify = (value: any, expected: any[]) => { + return config.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.returns(Promise.resolve()).exactly(expected.length); + 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] = args.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: [] + }]); + }); + }); + // describe('executables:' + + describe('load:', function() { + // const cwd = path.join(process.cwd(), 'out', 'test'); + + const updateAndLoad = (value: any) => { + return config.update('executables', value) + .then(() => { + const adapter = createAdapterAndSubscribe(); + + const watchEvents: Map = new Map(); + fsWatchStub.reset(); + fsWatchStub.callsFake((path: string) => { + const ee = new EventEmitter(); + watchEvents.set(path, ee); + return ee; + }); + + const verifyIsCatch2TestExecutable = + sinonSandbox.stub(adapter, 'verifyIsCatch2TestExecutable'); + verifyIsCatch2TestExecutable.returns(Promise.resolve(true)); + + return adapter.load().then(() => { + return watchEvents; + }); + }) + .then((watchEvents) => { + assert.equal(testsEvents.length, 2); + assert.equal(testsEvents[1].type, 'finished'); + assert.notEqual( + (testsEvents[1]).suite, undefined); + const root = (testsEvents[1]).suite!; + return {root: root, watchEvents: watchEvents}; + }); + }; + + it('a', () => { + return updateAndLoad('path1').then( + (param) => { + + }); + }); + }); }); // describe('C2TestAdapter' From 1858c93264a9bcc8120c6353e33a9ed6ffcd44ec Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 20 Oct 2018 01:46:23 +0200 Subject: [PATCH 3/7] load --- src/test/C2TestAdapter.test.ts | 56 +++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index 48ea38c8..61b156b4 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -44,6 +44,7 @@ describe('C2TestAdapter', function() { TestSuiteEvent|TestEvent)[]; let spawnStub: any; + let execFileStub: any; let fsWatchStub: any; let fsExistsStub: any; @@ -87,6 +88,9 @@ describe('C2TestAdapter', function() { spawnStub = sinonSandbox.stub(child_process, 'spawn'); spawnStub.throws(); + execFileStub = sinonSandbox.stub(child_process, 'execFile'); + execFileStub.throws(); + fsWatchStub = sinonSandbox.stub(fs, 'watch'); fsWatchStub.throws(); @@ -859,8 +863,7 @@ describe('C2TestAdapter', function() { sinonSandbox.stub(adapter, 'verifyIsCatch2TestExecutable'); verifyIsCatch2TestExecutable.returns(Promise.resolve(true)); - const loadSuiteMock = - sinon.expectation.create('loadSuiteMock'); + const loadSuiteMock = sinon.expectation.create('loadSuiteMock'); loadSuiteMock.returns(Promise.resolve()).exactly(expected.length); sinonSandbox.replace(adapter, 'loadSuite', loadSuiteMock); @@ -937,7 +940,6 @@ describe('C2TestAdapter', function() { const adapter = createAdapterAndSubscribe(); const watchEvents: Map = new Map(); - fsWatchStub.reset(); fsWatchStub.callsFake((path: string) => { const ee = new EventEmitter(); watchEvents.set(path, ee); @@ -962,7 +964,53 @@ describe('C2TestAdapter', function() { }); }; - it('a', () => { + const fakeExecFileFunc = + (pathAndContent: Map) => { + return (path: string, args: string[], + cb: (err: any, stout: string, stderr: string) => void) => { + const res = pathAndContent.get(path); + if (res === undefined) { + cb(new Error('fake file not exists.'), '', ''); + } else if (args.length == 1 && args[0] === '--help') { + cb(null, 'Catch v2.', ''); + } else if (!deepEqual(args, [ + '[.],*', '--verbosity', 'high', '--list-tests', + '--use-colour', 'no' + ])) { + assert.ok(false, inspect([path, args])); + } else { + cb(null, res!, ''); + }; + }; + }; + + const fakeExistsFunc = (pathAndContent: Map) => { + return (path: string, cb: (err: any, exists: boolean) => void) => { + cb(undefined, pathAndContent.has(path)); + }; + }; + + it('"path1"', () => { + execFileStub.callsFake( + (path: string, args: string[], + cb: (err: any, stout: string, stderr: string) => void) => { + if (args.length == 1 && args[0] === '--help') { + cb(null, 'Catch v2.', ''); + } else if (!deepEqual(args, [ + '[.],*', '--verbosity', 'high', '--list-tests', + '--use-colour', 'no' + ])) { + assert.ok(false, inspect([path, args])); + } else { + cb(null, 'alma', ''); + } + }); + + fsExistsStub.callsFake( + (path: string, cb: (err: any, exists: boolean) => void) => { + cb(undefined, true); + }); + return updateAndLoad('path1').then( (param) => { From 1da2fc5f3060a1b1b09f36cffd2aab023424c39b Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 20 Oct 2018 01:52:35 +0200 Subject: [PATCH 4/7] load with fake --- src/C2TestSuiteInfo.ts | 2 +- src/test/C2TestAdapter.test.ts | 104 +++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/C2TestSuiteInfo.ts b/src/C2TestSuiteInfo.ts index b6070a1b..9ffb9be2 100644 --- a/src/C2TestSuiteInfo.ts +++ b/src/C2TestSuiteInfo.ts @@ -284,7 +284,7 @@ export class C2TestSuiteInfo implements TestSuiteInfo { if (lines.length == 0) this.adapter.log.error('Empty test list.'); - while (lines[lines.length - 1].length == 0) lines.pop(); + while (lines[lines.length - 1].trim().length == 0) lines.pop(); let i = 1; while (i < lines.length - 1) { diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index 61b156b4..26b84ee5 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -932,7 +932,32 @@ describe('C2TestAdapter', function() { // describe('executables:' describe('load:', function() { - // const cwd = path.join(process.cwd(), 'out', 'test'); + const cwd = path.join(process.cwd(), 'out', 'test'); + + const suite1TestList = `Matching test cases: + s1t1 + ../vscode-catch2-test-adapter/src/test/suite1.cpp:7 + tag1 + s1t2 + ../vscode-catch2-test-adapter/src/test/suite1.cpp:13 + tag1 +2 matching test cases + + `; + + const suite2TestList = `Matching test cases: + s2t1 + ../vscode-catch2-test-adapter/src/test/suite2.cpp:7 + tag1 + s2t2 + ../vscode-catch2-test-adapter/src/test/suite2.cpp:13 + tag1 + [.] + s2t3 + ../vscode-catch2-test-adapter/src/test/suite2.cpp:19 + tag1 +3 matching test cases + `; const updateAndLoad = (value: any) => { return config.update('executables', value) @@ -940,6 +965,7 @@ describe('C2TestAdapter', function() { const adapter = createAdapterAndSubscribe(); const watchEvents: Map = new Map(); + fsWatchStub.reset(); fsWatchStub.callsFake((path: string) => { const ee = new EventEmitter(); watchEvents.set(path, ee); @@ -964,57 +990,47 @@ describe('C2TestAdapter', function() { }); }; - const fakeExecFileFunc = - (pathAndContent: Map) => { - return (path: string, args: string[], - cb: (err: any, stout: string, stderr: string) => void) => { - const res = pathAndContent.get(path); - if (res === undefined) { - cb(new Error('fake file not exists.'), '', ''); - } else if (args.length == 1 && args[0] === '--help') { - cb(null, 'Catch v2.', ''); - } else if (!deepEqual(args, [ - '[.],*', '--verbosity', 'high', '--list-tests', - '--use-colour', 'no' - ])) { - assert.ok(false, inspect([path, args])); - } else { - cb(null, res!, ''); - }; - }; + function fakeExecFileFunc(pathAndContent: Map) { + return function( + path: string, args: string[], + cb: (err: any, stout: string, stderr: string) => void) { + const res = pathAndContent.get(path); + if (res === undefined) { + cb(new Error('fake file not exists.'), '', ''); + } else if (args.length == 1 && args[0] === '--help') { + cb(null, 'Catch v2.', ''); + } else if (!deepEqual(args, [ + '[.],*', '--verbosity', 'high', '--list-tests', + '--use-colour', 'no' + ])) { + assert.ok(false, inspect([path, args])); + } else { + cb(null, res!, ''); }; + }; + }; - const fakeExistsFunc = (pathAndContent: Map) => { - return (path: string, cb: (err: any, exists: boolean) => void) => { + function fakeExistsFunc(pathAndContent: Map) { + return function(path: string, cb: (err: any, exists: boolean) => void) { cb(undefined, pathAndContent.has(path)); }; }; - it('"path1"', () => { - execFileStub.callsFake( - (path: string, args: string[], - cb: (err: any, stout: string, stderr: string) => void) => { - if (args.length == 1 && args[0] === '--help') { - cb(null, 'Catch v2.', ''); - } else if (!deepEqual(args, [ - '[.],*', '--verbosity', 'high', '--list-tests', - '--use-colour', 'no' - ])) { - assert.ok(false, inspect([path, args])); - } else { - cb(null, 'alma', ''); - } - }); - - fsExistsStub.callsFake( - (path: string, cb: (err: any, exists: boolean) => void) => { - cb(undefined, true); - }); + const fakeFs = (pathAndContent: Iterable<[string, string]>) => { + const map = new Map(pathAndContent); + execFileStub.reset(); + execFileStub.callsFake(fakeExecFileFunc(map)); + fsExistsStub.reset(); + fsExistsStub.callsFake(fakeExistsFunc(map)); + }; - return updateAndLoad('path1').then( - (param) => { + it('"path1"', () => { + this.timeout(99999); + fakeFs([[path.join(cwd, 'path1'), suite1TestList]]); - }); + return updateAndLoad('path1').then((param) => { + assert.equal(param.root.children.length, 1); + }); }); }); }); From d94aa6fd34b3ffc362cda927ae696a80c270037b Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 20 Oct 2018 10:21:12 +0200 Subject: [PATCH 5/7] test refactor --- package-lock.json | 2 +- package.json | 2 +- src/test/C2TestAdapter.test.ts | 786 ++++++++++++++++++++++++--------- 3 files changed, 587 insertions(+), 203 deletions(-) diff --git a/package-lock.json b/package-lock.json index ec851d2c..9a2fa10b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-catch2-test-adapter", - "version": "1.1.1", + "version": "1.1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5f1f0e91..4d71aaa8 100644 --- a/package.json +++ b/package.json @@ -150,4 +150,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index 26b84ee5..d903f0c1 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -8,11 +8,11 @@ import * as fse from 'fs-extra'; import * as assert from 'assert'; import {EventEmitter} from 'events'; import * as vscode from 'vscode'; -import {TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, TestSuiteInfo} from 'vscode-test-adapter-api'; +import {TestEvent, TestLoadFinishedEvent, TestLoadStartedEvent, TestRunFinishedEvent, TestRunStartedEvent, TestSuiteEvent, TestSuiteInfo, TestInfo, TestAdapter} from 'vscode-test-adapter-api'; import {Log} from 'vscode-test-adapter-util'; import {C2AllTestSuiteInfo} from '../C2AllTestSuiteInfo'; -import * as myExtension from '../C2TestAdapter'; +import {C2TestAdapter} from '../C2TestAdapter'; import {Stream} from 'stream'; import {inspect} from 'util'; @@ -30,25 +30,268 @@ const dotVscodePath = path.join(workspaceFolderUri.path, '.vscode'); const sinonSandbox = sinon.createSandbox(); +const example1 = new class { + readonly suite1 = new class { + readonly execPath = path.join(workspaceFolderUri.path, 'execPath1'); + readonly testList = 'Matching test cases:\n' + + ' s1t1\n' + + ' suite1.cpp:7\n' + + ' tag1\n' + + ' s1t2\n' + + ' suite1.cpp:13\n' + + ' tag1\n' + + '2 matching test cases\n' + + '\n'; + + readonly t1 = new class { + readonly fullTestName = 's1t1'; + assert(test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, 's1t1'); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(test.line, 7 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly xml = ` + + + `; + }; + + readonly t2 = new class { + readonly fullTestName = 's1t2'; + assert(test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, 's1t2'); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(test.line, 13 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly xml = ` + + + + std::false_type::value + + + false + + + + `; + }; + + assert(suite: TestSuiteInfo, uniqeIdContainer?: Set) { + assert.equal(suite.type, 'suite'); + assert.equal(suite.label, 'execPath1'); + assert.equal( + suite.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(suite.line, 0); + assert.equal(suite.children.length, 2); + this.t1.assert(suite.children[0], uniqeIdContainer); + this.t2.assert(suite.children[1], uniqeIdContainer); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(suite.id)); + uniqeIdContainer.add(suite.id); + } + } + + readonly xmlHeader = ` + + + `; + + readonly xmlFull = this.xmlHeader + this.t1.xml + this.t2.xml + ` + + + + `; + }; + + readonly suite2 = new class { + readonly execPath = path.join(workspaceFolderUri.path, 'execPath2'); + readonly testList = 'Matching test cases:\n' + + ' s2t1\n' + + ' suite2.cpp:7\n' + + ' tag1\n' + + ' s2t2\n' + + ' suite2.cpp:13\n' + + ' tag1\n' + + ' [.]\n' + + ' s2t3\n' + + ' suite2.cpp:19\n' + + ' tag1\n' + + '3 matching test cases\n' + + '\n'; + + readonly t1 = new class { + readonly fullTestName = 's2t1'; + assert(test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, 's2t1'); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 7 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly xml = ` + + + `; + }; + + readonly t2 = new class { + readonly fullTestName = 's2t2'; + assert(test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, 's2t2 [.]'); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 13 - 1); + assert.ok(test.skipped === true); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly xml = ` + + + `; + }; + + readonly t3 = new class { + readonly fullTestName = 's2t3'; + assert(test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, 's2t3'); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 19 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly xml = ` + + + + std::false_type::value + + + false + + + + `; + }; + + assert(suite: TestSuiteInfo, uniqeIdContainer?: Set) { + assert.equal(suite.type, 'suite'); + assert.equal(suite.label, 'execPath2'); + assert.equal( + suite.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(suite.line, 0); + assert.equal(suite.children.length, 3); + this.t1.assert(suite.children[0], uniqeIdContainer); + this.t2.assert(suite.children[1], uniqeIdContainer); + this.t3.assert(suite.children[2], uniqeIdContainer); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(suite.id)); + uniqeIdContainer.add(suite.id); + } + } + + readonly xmlHeader = ` + + + `; + + readonly xmlFull = this.xmlHeader + this.t1.xml + + /* this.t2.xml is skipped */ this.t3.xml + ` + + + + `; + }; + + assertWithoutChildren(root: TestSuiteInfo, uniqeIdContainer?: Set) { + assert.equal(root.type, 'suite'); + assert.equal(root.label, 'AllTests'); + assert.equal(root.file, undefined); + assert.equal(root.line, undefined); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(root.id)); + uniqeIdContainer.add(root.id); + } + }; +}; + +class ChildProcessStub extends EventEmitter { + readonly stdout = new Stream.Readable(); + + constructor(data?: string) { + super(); + this.stdout.on('end', () => { + this.emit('close', 1); + }); + if (data != undefined) this.writeAndClose(data); + } + + writeAndClose(data: string): void { + this.stdout.push(data); + this.stdout.push(null); + } + + writeLineByLineAndClose(data: string): void { + const lines = data.split('\n'); + lines.forEach((l) => { + this.stdout.push(l); + }); + this.stdout.push(null); + } +}; + /// describe('C2TestAdapter', function() { const config = vscode.workspace.getConfiguration( 'catch2TestExplorer', workspaceFolderUri); - const disposable: vscode.Disposable[] = []; - - let adapter: myExtension.C2TestAdapter|undefined; + let adapter: C2TestAdapter|undefined; let testsEvents: (TestLoadStartedEvent|TestLoadFinishedEvent)[]; + let testsEventsConnection: vscode.Disposable; let testStatesEvents: (TestRunStartedEvent|TestRunFinishedEvent| TestSuiteEvent|TestEvent)[]; + let testStatesEventsConnection: vscode.Disposable; let spawnStub: any; let execFileStub: any; let fsWatchStub: any; let fsExistsStub: any; - const resetConfig = function(): Thenable { + function resetConfig(): Thenable { const packageJson = fse.readJSONSync( path.join(workspaceFolderUri.path, '../..', 'package.json')); const properties: {[prop: string]: any}[] = @@ -62,70 +305,89 @@ describe('C2TestAdapter', function() { }); }); return t; - }; + } - const createAdapterAndSubscribe = function() { - adapter = new myExtension.C2TestAdapter(workspaceFolder, logger); - testsEvents = []; - testStatesEvents = []; + function createAdapterAndSubscribe() { + adapter = new C2TestAdapter(workspaceFolder, logger); - disposable.push( + testsEvents = []; + testsEventsConnection = adapter.tests((e: TestLoadStartedEvent|TestLoadFinishedEvent) => { testsEvents.push(e); - })); - disposable.push(adapter.testStates( + }); + + testStatesEvents = []; + testStatesEventsConnection = adapter.testStates( (e: TestRunStartedEvent|TestRunFinishedEvent|TestSuiteEvent| TestEvent) => { testStatesEvents.push(e); - })); + }); + return adapter!; - }; + } - beforeEach(() => { - adapter = undefined; + function disposeAdapterAndSubscribers() { + testsEventsConnection.dispose(); + testStatesEventsConnection.dispose(); + testsEvents = []; + testStatesEvents = []; + } + + before(() => { fse.removeSync(dotVscodePath); + adapter = undefined; spawnStub = sinonSandbox.stub(child_process, 'spawn'); - spawnStub.throws(); - execFileStub = sinonSandbox.stub(child_process, 'execFile'); - execFileStub.throws(); - fsWatchStub = sinonSandbox.stub(fs, 'watch'); - fsWatchStub.throws(); - fsExistsStub = sinonSandbox.stub(fs, 'exists'); - fsExistsStub.throws(); + // reset config can cause problem with fse.removeSync(dotVscodePath); return resetConfig(); }); - afterEach(() => { - while (disposable.length > 0) disposable.pop()!.dispose(); + function resetStubs() { + spawnStub.reset(); + spawnStub.throws(); + execFileStub.reset(); + execFileStub.throws(); + fsWatchStub.reset(); + fsWatchStub.throws(); + fsExistsStub.reset(); + fsExistsStub.throws(); + } + + after(() => { + disposeAdapterAndSubscribers(); sinonSandbox.restore(); }); describe('detect config change', function() { - this.timeout(1000); - const waitForReloadAndAssert = () => { - const waitForReloadAndAssertInner = - (tryCount: number = 20): 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(); + 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.deepEqual(testsEvents, []); @@ -165,12 +427,17 @@ describe('C2TestAdapter', function() { // describe('detect config change' describe('adapter:', () => { - let adapter: myExtension.C2TestAdapter; + let adapter: C2TestAdapter; beforeEach(() => { adapter = createAdapterAndSubscribe(); }); + afterEach(() => { + disposeAdapterAndSubscribers(); + resetStubs(); + }); + it('fill with empty config', function() { return adapter.load().then(() => { assert.equal(testsEvents.length, 2); @@ -182,75 +449,9 @@ describe('C2TestAdapter', function() { }); }); - describe('fill with example1', function() { + describe('example1', function() { let tests: any; - const randomnessXml = ``; - - const s1t1Xml = ` - - - `; - - const s1t2Xml = ` - - - - std::false_type::value - - - false - - - - ;`; - - const s1HeaderXml = ` - - - `; - - const s1Xml = s1HeaderXml + s1t1Xml + s1t2Xml + ` - - - - `; - - const s2t1Xml = ` - - - `; - - const s2t2Xml = ` - - - `; - - const s2t3Xml = ` - - - - std::false_type::value - - - false - - - - `; - - const s2HeaderXml = ` - - ` + - randomnessXml + ` - `; - - const s2Xml = s2HeaderXml + s2t1Xml + /* s2t2 is skipped */ s2t3Xml + ` - - - - `; - beforeEach(() => { return adapter.load() .then(() => { @@ -294,7 +495,7 @@ describe('C2TestAdapter', function() { spawnEvent.emit('close', 1); }); stdout.push( - s1HeaderXml + s1t1Xml + + example1.suite1.xmlHeader + example1.suite1.t1.xml + ` @@ -338,7 +539,7 @@ describe('C2TestAdapter', function() { spawnEvent.emit('close', 1); }); stdout.push( - s1HeaderXml + + example1.suite1.xmlHeader + ` @@ -373,7 +574,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s2HeaderXml + s2t2Xml + ` + stdout.push(example1.suite1.xmlHeader + example1.suite2.t2.xml + ` @@ -417,7 +618,7 @@ describe('C2TestAdapter', function() { spawnEvent.emit('close', 1); }); stdout.push( - s2HeaderXml + s2t3Xml + + example1.suite1.xmlHeader + example1.suite2.t3.xml + ` @@ -472,8 +673,10 @@ describe('C2TestAdapter', function() { tests.s1.execOptions) .returns(spawnEvent); - s2HeaderXml.split('\n').forEach((l: string) => {stdout.push(l)}); - s2t3Xml.split('\n').forEach((l: string) => {stdout.push(l)}); + example1.suite1.xmlHeader.split('\n').forEach( + (l: string) => {stdout.push(l)}); + example1.suite2.t3.xml.split('\n').forEach( + (l: string) => {stdout.push(l)}); stdout.push( ' \n'); stdout.push(' \n'); @@ -509,7 +712,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s1Xml); + stdout.push(example1.suite1.xmlFull); stdout.push(null); spawnStub @@ -554,7 +757,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s1Xml); + stdout.push(example1.suite1.xmlFull); stdout.push(null); spawnStub @@ -571,7 +774,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s2Xml); + stdout.push(example1.suite2.xmlFull); stdout.push(null); spawnStub @@ -710,9 +913,9 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - const testCaseBegin = s1t1Xml.split('\n')[1]; + const testCaseBegin = example1.suite1.t1.xml.split('\n')[1]; assert.ok(testCaseBegin.indexOf(' { spawnEvent.emit('close', 1); }); - stdout.push(s1Xml); + stdout.push(example1.suite1.xmlFull); stdout.push(null); spawnStub @@ -777,7 +980,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s2Xml); + stdout.push(example1.suite2.xmlFull); stdout.push(null); spawnStub @@ -810,7 +1013,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s1Xml); + stdout.push(example1.suite1.xmlFull); stdout.push(null); spawnStub @@ -828,7 +1031,7 @@ describe('C2TestAdapter', function() { stdout.on('end', () => { spawnEvent.emit('close', 1); }); - stdout.push(s2Xml); + stdout.push(example1.suite2.xmlFull); stdout.push(null); spawnStub @@ -847,13 +1050,20 @@ describe('C2TestAdapter', function() { }); // it('cancel: after run finished' }); - // describe('fill with example1' + // describe('example1' }); // describe('adapter:' describe('executables:', function() { + this.slow(150); const cwd = path.join(process.cwd(), 'out', 'test'); + afterEach(() => { + disposeAdapterAndSubscribers(); + resetStubs(); + return resetConfig(); + }); + const updateAndVerify = (value: any, expected: any[]) => { return config.update('executables', value) .then(() => { @@ -931,66 +1141,8 @@ describe('C2TestAdapter', function() { }); // describe('executables:' - describe('load:', function() { - const cwd = path.join(process.cwd(), 'out', 'test'); - - const suite1TestList = `Matching test cases: - s1t1 - ../vscode-catch2-test-adapter/src/test/suite1.cpp:7 - tag1 - s1t2 - ../vscode-catch2-test-adapter/src/test/suite1.cpp:13 - tag1 -2 matching test cases - - `; - - const suite2TestList = `Matching test cases: - s2t1 - ../vscode-catch2-test-adapter/src/test/suite2.cpp:7 - tag1 - s2t2 - ../vscode-catch2-test-adapter/src/test/suite2.cpp:13 - tag1 - [.] - s2t3 - ../vscode-catch2-test-adapter/src/test/suite2.cpp:19 - tag1 -3 matching test cases - `; - - const updateAndLoad = (value: any) => { - return config.update('executables', value) - .then(() => { - const adapter = createAdapterAndSubscribe(); - - const watchEvents: Map = new Map(); - fsWatchStub.reset(); - fsWatchStub.callsFake((path: string) => { - const ee = new EventEmitter(); - watchEvents.set(path, ee); - return ee; - }); - - const verifyIsCatch2TestExecutable = - sinonSandbox.stub(adapter, 'verifyIsCatch2TestExecutable'); - verifyIsCatch2TestExecutable.returns(Promise.resolve(true)); - - return adapter.load().then(() => { - return watchEvents; - }); - }) - .then((watchEvents) => { - assert.equal(testsEvents.length, 2); - assert.equal(testsEvents[1].type, 'finished'); - assert.notEqual( - (testsEvents[1]).suite, undefined); - const root = (testsEvents[1]).suite!; - return {root: root, watchEvents: watchEvents}; - }); - }; - - function fakeExecFileFunc(pathAndContent: Map) { + context('example1', function() { + function fakeExecFileFunc(pathAndContent: Map) { return function( path: string, args: string[], cb: (err: any, stout: string, stderr: string) => void) { @@ -999,46 +1151,278 @@ describe('C2TestAdapter', function() { cb(new Error('fake file not exists.'), '', ''); } else if (args.length == 1 && args[0] === '--help') { cb(null, 'Catch v2.', ''); - } else if (!deepEqual(args, [ + } else if (deepEqual(args, [ '[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', 'no' ])) { - assert.ok(false, inspect([path, args])); - } else { cb(null, res!, ''); + } else { + assert.ok(false, inspect([path, args])); }; }; }; - function fakeExistsFunc(pathAndContent: Map) { + function fakeExistsFunc(pathAndContent: Map) { return function(path: string, cb: (err: any, exists: boolean) => void) { cb(undefined, pathAndContent.has(path)); }; }; - const fakeFs = (pathAndContent: Iterable<[string, string]>) => { + function fakeSpawn() { + const testTestParams = ['--reporter', 'xml', '--durations', 'yes']; + + spawnStub + .withArgs( + example1.suite1.execPath, + [example1.suite1.t1.fullTestName, ...testTestParams]) + .callsFake(() => { + return new ChildProcessStub( + example1.suite1.xmlHeader + example1.suite1.t1.xml + + ''); + }); + spawnStub + .withArgs( + example1.suite1.execPath, + [example1.suite1.t2.fullTestName, ...testTestParams]) + .callsFake(() => { + return new ChildProcessStub( + example1.suite1.xmlHeader + example1.suite1.t2.xml + + ''); + }); + spawnStub.withArgs(example1.suite1.execPath, testTestParams) + .callsFake(() => { + return new ChildProcessStub(example1.suite1.xmlFull); + }); + + spawnStub + .withArgs( + example1.suite2.execPath, + [example1.suite2.t1.fullTestName, ...testTestParams]) + .callsFake(() => { + return new ChildProcessStub( + example1.suite2.xmlHeader + example1.suite2.t1.xml + + ''); + }); + spawnStub + .withArgs( + example1.suite2.execPath, + [example1.suite2.t2.fullTestName, ...testTestParams]) + .callsFake(() => { + return new ChildProcessStub( + example1.suite2.xmlHeader + example1.suite2.t2.xml + + ''); + }); + spawnStub + .withArgs( + example1.suite2.execPath, + [example1.suite2.t3.fullTestName, ...testTestParams]) + .callsFake(() => { + return new ChildProcessStub( + example1.suite2.xmlHeader + example1.suite2.t3.xml + + ''); + }); + spawnStub.withArgs(example1.suite2.execPath, testTestParams) + .callsFake(() => { + return new ChildProcessStub(example1.suite2.xmlFull); + }); + } + + function fakeFs(pathAndContent: Iterable<[string, string]>) { const map = new Map(pathAndContent); execFileStub.reset(); execFileStub.callsFake(fakeExecFileFunc(map)); + fsExistsStub.reset(); fsExistsStub.callsFake(fakeExistsFunc(map)); + + fsWatchStub.reset(); + fsWatchStub.callsFake((path: string) => { + if (map.has(path)) { + const ee = new class extends EventEmitter { + close() {} + }; + watchEvents.set(path, ee); + return ee; + } else { + throw Error('File not found?'); + } + }); + + fakeSpawn(); }; - it('"path1"', () => { - this.timeout(99999); - fakeFs([[path.join(cwd, 'path1'), suite1TestList]]); + const uniqueIdC = new Set(); + const watchEvents: Map = new Map(); + + before(() => { + fakeFs([ + [example1.suite1.execPath, example1.suite1.testList], + [example1.suite2.execPath, example1.suite2.testList] + ]); + }); + + beforeEach(() => { + watchEvents.clear(); + uniqueIdC.clear(); + }) + + after(() => { + resetStubs(); + }); + + context.only('load with config: executables="execPath1"', function() { + let adapter: TestAdapter; + let root: TestSuiteInfo; + + before(() => { + return config.update('executables', 'execPath1'); + }); + + 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!; + + example1.assertWithoutChildren(root, uniqueIdC); + assert.equal(root.children.length, 1); + example1.suite1.assert(root.children[0], uniqueIdC); + }); + + afterEach(() => { + disposeAdapterAndSubscribers(); + }); + + after(() => { + return resetConfig(); + }); + + it('should run with not existing test id', async function() { + await adapter.run(['not existing id']); + + assert.deepEqual(testStatesEvents, [ + {type: 'started', tests: ['not existing id']}, + {type: 'finished'}, + ]); + }); - return updateAndLoad('path1').then((param) => { - assert.equal(param.root.children.length, 1); + it('should run s1t1 with success', async function() { + const suite1 = root.children[0]; + const s1t1 = suite1.children[0]; + + 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.000174 second(s)\n' + }, + {type: 'suite', state: 'completed', suite: suite1}, + {type: 'finished'}, + ]; + assert.deepEqual(testStatesEvents, expected); + + await adapter.run([s1t1.id]); + assert.deepEqual(testStatesEvents, [...expected, ...expected]); + }); + + it('should run suite1', async function() { + const suite1 = root.children[0]; + const s1t1 = suite1.children[0]; + const s1t2 = suite1.children[1]; + + 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: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + }, + {type: 'test', state: 'running', test: s1t2}, + { + type: 'test', + state: 'failed', + test: s1t2, + decorations: [{line: 14, message: 'Expanded: false'}], + message: + 'Randomness seeded to: 2\nDuration: 0.000255 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.deepEqual(testStatesEvents, expected); + + await adapter.run([suite1.id]); + assert.deepEqual(testStatesEvents, [...expected, ...expected]); }); }); - }); + }); // descibe('example1' }); // describe('C2TestAdapter' +describe.skip('a', function() { + this.timeout(99999); + + before(() => { + debugger; + }); + beforeEach(() => { + debugger; + }); + + after(() => { + debugger; + }); + afterEach(() => { + debugger; + }); + + it('a-it', () => { + debugger; + }); + + describe('b', () => { + before(() => { + debugger; + }); + beforeEach(() => { + debugger; + }); + + after(() => { + debugger; + }); + afterEach(() => { + debugger; + }); + + it('b-it1', () => { + debugger; + }); + + 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 \ No newline at end of file +// writing xml +// re-load soame object +// deepstrictequal \ No newline at end of file From 4065368e53f725ce4d1ec0cfa150969d2f58b810 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sun, 21 Oct 2018 09:44:57 +0200 Subject: [PATCH 6/7] more tests --- CHANGELOG.md | 4 + package-lock.json | 18 +- package.json | 4 +- src/C2TestAdapter.ts | 26 +- src/C2TestSuiteInfo.ts | 128 +-- src/FsWrapper.ts | 47 + src/test/C2TestAdapter.test.ts | 1570 ++++++++++++++++---------------- src/test/FsWrapper.test.ts | 23 + src/test/Helpers.ts | 40 + src/test/example1.ts | 484 ++++++++++ 10 files changed, 1494 insertions(+), 850 deletions(-) create mode 100644 src/FsWrapper.ts create mode 100644 src/test/FsWrapper.test.ts create mode 100644 src/test/Helpers.ts create mode 100644 src/test/example1.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7533ecba..1532ee5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.2] + +Bugfix release. + ## [1.1.1] ### Added diff --git a/package-lock.json b/package-lock.json index 9a2fa10b..f21a8291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,12 @@ "integrity": "sha512-ZwTHAlC9akprWDinwEPD4kOuwaYZlyMwVJIANsKNC3QVp0AHB04m7RnB4eqeWfgmxw8MGTzS9uMaw93Z3QcZbw==", "dev": true }, + "@types/chai": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.6.tgz", + "integrity": "sha512-CBk7KTZt3FhPsEkYioG6kuCIpWISw+YI8o+3op4+NXwTpvAPxE1ES8+PY8zfaK2L98b1z5oq03UHa4VYpeUxnw==", + "dev": true + }, "@types/deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.1.tgz", @@ -78,6 +84,12 @@ "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ==", "dev": true }, + "@types/sinon": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-5.0.5.tgz", + "integrity": "sha512-Wnuv66VhvAD2LEJfZkq8jowXGxe+gjVibeLCYcVBp7QLdw0BFx2sRkKzoiiDkYEPGg5VyqO805Rcj0stVjQwCQ==", + "dev": true + }, "@types/xml-parser": { "version": "1.2.29", "resolved": "https://registry.npmjs.org/@types/xml-parser/-/xml-parser-1.2.29.tgz", @@ -549,9 +561,9 @@ } }, "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "escape-string-regexp": { "version": "1.0.5", diff --git a/package.json b/package.json index 4d71aaa8..a172578f 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "hbenl.vscode-test-explorer" ], "dependencies": { - "entities": "^1.1.1", + "entities": "^1.1.2", "tslib": "^1.9.3", "vscode-test-adapter-api": "^1.1.0", "vscode-test-adapter-util": "^0.5.0", @@ -52,10 +52,12 @@ "@types/xml-parser": "^1.2.29", "@types/xml2js": "^0.4.3", "@types/mocha": "^5.2.5", + "@types/chai": "^4.1.6", "@types/fs-extra": "^5.0.4", "fs-extra": "^7.0.0", "@types/deep-equal": "^1.0.1", "deep-equal": "^1.0.1", + "@types/sinon": "^5.0.5", "sinon": "^7.0.0", "typescript": "^2.9.2", "vsce": "^1.51.1", diff --git a/src/C2TestAdapter.ts b/src/C2TestAdapter.ts index 9ad00363..6e3a7692 100644 --- a/src/C2TestAdapter.ts +++ b/src/C2TestAdapter.ts @@ -2,7 +2,6 @@ // 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 {execFile} from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; @@ -11,6 +10,7 @@ import * as util from 'vscode-test-adapter-util'; import {C2AllTestSuiteInfo} from './C2AllTestSuiteInfo'; import {C2TestInfo} from './C2TestInfo'; +import * as c2fs from './FsWrapper'; export class C2TestAdapter implements TestAdapter, vscode.Disposable { private readonly testsEmitter = @@ -80,8 +80,6 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { this.disposables.forEach(d => { d.dispose(); }); - while (this.disposables.shift() !== undefined) - ; } get testStates(): vscode.Event 0) { const recursiveAdd = (directory: string): void => { - const children = fs.readdirSync(directory, 'utf8'); + const children = c2fs.readdirSync(directory); children.forEach(child => { const childPath = path.resolve(directory, child); - const childStat = fs.statSync(childPath); + const childStat = c2fs.statSync(childPath); if (childPath.match(regex) && childStat.isFile()) { let resolvedName = name + ' : ' + child; let resolvedCwd = cwd; @@ -458,7 +456,7 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { }); }; try { - const stat = fs.statSync(p); + const stat = c2fs.statSync(p); if (stat.isDirectory()) { recursiveAdd(p); } else if (stat.isFile()) { @@ -507,20 +505,8 @@ export class C2TestAdapter implements TestAdapter, vscode.Disposable { } verifyIsCatch2TestExecutable(path: string): Promise { - return new Promise((resolve, reject) => { - try { - execFile( - path, ['--help'], - (error: Error|null, stdout: string, stderr: string) => { - if (stdout.indexOf('Catch v2.') != -1) { - resolve(true); - } else { - resolve(false); - } - }); - } catch (e) { - resolve(false); - } + return c2fs.spawnAsync(path, ['--help']).then((res) => { + return res.stdout.indexOf('Catch v2.') != -1; }); } diff --git a/src/C2TestSuiteInfo.ts b/src/C2TestSuiteInfo.ts index 9ffb9be2..22a7c7f1 100644 --- a/src/C2TestSuiteInfo.ts +++ b/src/C2TestSuiteInfo.ts @@ -2,7 +2,7 @@ // 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 {ChildProcess, execFile, spawn, SpawnOptions} from 'child_process'; +import {ChildProcess, spawn, SpawnOptions} from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import {promisify} from 'util'; @@ -11,6 +11,7 @@ import * as xml2js from 'xml2js'; import {C2TestAdapter} from './C2TestAdapter'; import {C2TestInfo} from './C2TestInfo'; +import * as c2fs from './FsWrapper'; import {generateUniqueId} from './IdGenerator'; import {TaskPool} from './TaskPool'; @@ -269,74 +270,75 @@ export class C2TestSuiteInfo implements TestSuiteInfo { if (!exists) throw Error('reloadSuiteChildren: Should exists: ' + this.execPath); - return new Promise((resolve, reject) => { - execFile( - this.execPath, - [ - '[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', - 'no' - ], - (error: Error|null, stdout: string, stderr: string) => { - const oldChildren = this.children; - this.children = []; - - let lines = stdout.split(/\r?\n/); - - if (lines.length == 0) this.adapter.log.error('Empty test list.'); - - while (lines[lines.length - 1].trim().length == 0) lines.pop(); - - let i = 1; - while (i < lines.length - 1) { - if (lines[i][0] != ' ') - this.adapter.log.error( - 'Wrong test list output format: ' + lines.toString()); - - const testNameFull = lines[i++].substr(2); - - let filePath = ''; - let line = 0; - { - const fileLine = lines[i++].substr(4); - const match = - fileLine.match(/(?:(.+):([0-9]+)|(.+)\(([0-9]+)\))/); - if (match && match.length == 5) { - filePath = match[1] ? match[1] : match[3]; - if (this.execOptions.cwd) - filePath = path.resolve(this.execOptions.cwd, filePath); - line = Number(match[2] ? match[2] : match[4]); + return c2fs + .spawnAsync( + this.execPath, + [ + '[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', + 'no' + ], + this.execOptions) + .then((r) => { + const oldChildren = this.children; + this.children = []; + + let lines = r.stdout.split(/\r?\n/); + + if (lines.length == 0) this.adapter.log.error('Empty test list.'); + + while (lines[lines.length - 1].trim().length == 0) lines.pop(); + + let i = 1; + while (i < lines.length - 1) { + if (lines[i][0] != ' ') + this.adapter.log.error( + 'Wrong test list output format: ' + lines.toString()); + + const testNameFull = lines[i++].substr(2); + + let filePath = ''; + let line = 0; + { + const fileLine = lines[i++].substr(4); + const match = + fileLine.match(/(?:(.+):([0-9]+)|(.+)\(([0-9]+)\))/); + if (match && match.length == 5) { + filePath = match[1] ? match[1] : match[3]; + filePath = + path.resolve(path.dirname(this.execPath), filePath); + if (!c2fs.existsSync(filePath) && this.execOptions.cwd) { + const r = path.resolve(this.execOptions.cwd, filePath); + if (c2fs.existsSync(r)) filePath = r; } + line = Number(match[2] ? match[2] : match[4]); } + } - let description = lines[i++].substr(4); - if (description.startsWith('(NO DESCRIPTION)')) - description = ''; - - let tags: string[] = []; - if (lines[i].length > 6 && lines[i][6] === '[') { - tags = lines[i].trim().split(']'); - tags.pop(); - for (let j = 0; j < tags.length; ++j) tags[j] += ']'; - ++i; - } + let description = lines[i++].substr(4); + if (description.startsWith('(NO DESCRIPTION)')) description = ''; - const index = oldChildren.findIndex( - (c: C2TestInfo): boolean => {return c.testNameFull == - testNameFull}); - if (index != -1 && - oldChildren[index].label == - C2TestInfo.generateLabel( - testNameFull, description, tags)) { - this.children.push(oldChildren[index]); - } else { - this.createChildTest( - testNameFull, description, tags, filePath, line); - } + let tags: string[] = []; + if (lines[i].length > 6 && lines[i][6] === '[') { + tags = lines[i].trim().split(']'); + tags.pop(); + for (let j = 0; j < tags.length; ++j) tags[j] += ']'; + ++i; } - resolve(); - }); - }); + const index = oldChildren.findIndex( + (c: C2TestInfo): boolean => {return c.testNameFull == + testNameFull}); + if (index != -1 && + oldChildren[index].label == + C2TestInfo.generateLabel( + testNameFull, description, tags)) { + this.children.push(oldChildren[index]); + } else { + this.createChildTest( + testNameFull, description, tags, filePath, line); + } + } + }); }); } } diff --git a/src/FsWrapper.ts b/src/FsWrapper.ts new file mode 100644 index 00000000..d4c8b66a --- /dev/null +++ b/src/FsWrapper.ts @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// 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 cp from 'child_process'; +import * as fs from 'fs'; + +export function spawnAsync( + cmd: string, args?: string[], + options?: cp.SpawnOptions): Promise> { + return new Promise((resolve) => { + const command = cp.spawn(cmd, args, options); + const ret: cp.SpawnSyncReturns = { + pid: command.pid, + output: [], + stdout: '', + stderr: '', + status: 0, + signal: '', + error: new Error() + }; + command.stdout.on('data', function(data) { + ret.stdout += data; + ret.output.push(data); + }); + command.on('close', function(code) { + ret.status = code; + resolve(ret) + }); + command.on('error', function(err) { + ret.error = err; + resolve(ret); + }); + }) +} + +export function statSync(path: string): fs.Stats { + return fs.statSync(path); +} + +export function existsSync(path: string): boolean { + return fs.existsSync(path); +} + +export function readdirSync(path: string): string[] { + return fs.readdirSync(path, 'utf8'); +} \ No newline at end of file diff --git a/src/test/C2TestAdapter.test.ts b/src/test/C2TestAdapter.test.ts index d903f0c1..dd4099d7 100644 --- a/src/test/C2TestAdapter.test.ts +++ b/src/test/C2TestAdapter.test.ts @@ -1,20 +1,26 @@ -const sinon = require('sinon'); +//----------------------------------------------------------------------------- +// 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. + const child_process = require('child_process'); -const deepEqual = require('deep-equal'); +const deepStrictEqual = require('deep-equal'); import * as path from 'path'; import * as fs from 'fs'; import * as fse from 'fs-extra'; import * as assert from 'assert'; -import {EventEmitter} from 'events'; import * as vscode from 'vscode'; +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 {C2AllTestSuiteInfo} from '../C2AllTestSuiteInfo'; import {C2TestAdapter} from '../C2TestAdapter'; -import {Stream} from 'stream'; -import {inspect} from 'util'; +import {example1} from './example1'; +import {ChildProcessStub} from './Helpers'; +import * as c2fs from '../FsWrapper'; assert.notEqual(vscode.workspace.workspaceFolders, undefined); assert.equal(vscode.workspace.workspaceFolders!.length, 1); @@ -23,6 +29,11 @@ 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'); @@ -30,266 +41,30 @@ const dotVscodePath = path.join(workspaceFolderUri.path, '.vscode'); const sinonSandbox = sinon.createSandbox(); -const example1 = new class { - readonly suite1 = new class { - readonly execPath = path.join(workspaceFolderUri.path, 'execPath1'); - readonly testList = 'Matching test cases:\n' + - ' s1t1\n' + - ' suite1.cpp:7\n' + - ' tag1\n' + - ' s1t2\n' + - ' suite1.cpp:13\n' + - ' tag1\n' + - '2 matching test cases\n' + - '\n'; - - readonly t1 = new class { - readonly fullTestName = 's1t1'; - assert(test: TestInfo, uniqeIdContainer?: Set) { - assert.equal(test.type, 'test'); - assert.equal(test.label, 's1t1'); - assert.equal( - test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); - assert.equal(test.line, 7 - 1); - assert.ok(test.skipped == undefined || test.skipped === false); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(test.id)); - uniqeIdContainer.add(test.id); - } - }; - - readonly xml = ` - - - `; - }; - - readonly t2 = new class { - readonly fullTestName = 's1t2'; - assert(test: TestInfo, uniqeIdContainer?: Set) { - assert.equal(test.type, 'test'); - assert.equal(test.label, 's1t2'); - assert.equal( - test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); - assert.equal(test.line, 13 - 1); - assert.ok(test.skipped == undefined || test.skipped === false); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(test.id)); - uniqeIdContainer.add(test.id); - } - }; - - readonly xml = ` - - - - std::false_type::value - - - false - - - - `; - }; - - assert(suite: TestSuiteInfo, uniqeIdContainer?: Set) { - assert.equal(suite.type, 'suite'); - assert.equal(suite.label, 'execPath1'); - assert.equal( - suite.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); - assert.equal(suite.line, 0); - assert.equal(suite.children.length, 2); - this.t1.assert(suite.children[0], uniqeIdContainer); - this.t2.assert(suite.children[1], uniqeIdContainer); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(suite.id)); - uniqeIdContainer.add(suite.id); - } - } - - readonly xmlHeader = ` - - - `; - - readonly xmlFull = this.xmlHeader + this.t1.xml + this.t2.xml + ` - - - - `; - }; - - readonly suite2 = new class { - readonly execPath = path.join(workspaceFolderUri.path, 'execPath2'); - readonly testList = 'Matching test cases:\n' + - ' s2t1\n' + - ' suite2.cpp:7\n' + - ' tag1\n' + - ' s2t2\n' + - ' suite2.cpp:13\n' + - ' tag1\n' + - ' [.]\n' + - ' s2t3\n' + - ' suite2.cpp:19\n' + - ' tag1\n' + - '3 matching test cases\n' + - '\n'; - - readonly t1 = new class { - readonly fullTestName = 's2t1'; - assert(test: TestInfo, uniqeIdContainer?: Set) { - assert.equal(test.type, 'test'); - assert.equal(test.label, 's2t1'); - assert.equal( - test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); - assert.equal(test.line, 7 - 1); - assert.ok(test.skipped == undefined || test.skipped === false); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(test.id)); - uniqeIdContainer.add(test.id); - } - }; - - readonly xml = ` - - - `; - }; - - readonly t2 = new class { - readonly fullTestName = 's2t2'; - assert(test: TestInfo, uniqeIdContainer?: Set) { - assert.equal(test.type, 'test'); - assert.equal(test.label, 's2t2 [.]'); - assert.equal( - test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); - assert.equal(test.line, 13 - 1); - assert.ok(test.skipped === true); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(test.id)); - uniqeIdContainer.add(test.id); - } - }; - - readonly xml = ` - - - `; - }; - - readonly t3 = new class { - readonly fullTestName = 's2t3'; - assert(test: TestInfo, uniqeIdContainer?: Set) { - assert.equal(test.type, 'test'); - assert.equal(test.label, 's2t3'); - assert.equal( - test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); - assert.equal(test.line, 19 - 1); - assert.ok(test.skipped == undefined || test.skipped === false); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(test.id)); - uniqeIdContainer.add(test.id); - } - }; - - readonly xml = ` - - - - std::false_type::value - - - false - - - - `; - }; - - assert(suite: TestSuiteInfo, uniqeIdContainer?: Set) { - assert.equal(suite.type, 'suite'); - assert.equal(suite.label, 'execPath2'); - assert.equal( - suite.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); - assert.equal(suite.line, 0); - assert.equal(suite.children.length, 3); - this.t1.assert(suite.children[0], uniqeIdContainer); - this.t2.assert(suite.children[1], uniqeIdContainer); - this.t3.assert(suite.children[2], uniqeIdContainer); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(suite.id)); - uniqeIdContainer.add(suite.id); - } - } - - readonly xmlHeader = ` - - - `; - - readonly xmlFull = this.xmlHeader + this.t1.xml + - /* this.t2.xml is skipped */ this.t3.xml + ` - - - - `; - }; +/// - assertWithoutChildren(root: TestSuiteInfo, uniqeIdContainer?: Set) { - assert.equal(root.type, 'suite'); - assert.equal(root.label, 'AllTests'); - assert.equal(root.file, undefined); - assert.equal(root.line, undefined); - if (uniqeIdContainer != undefined) { - assert.ok(!uniqeIdContainer.has(root.id)); - uniqeIdContainer.add(root.id); - } +describe('C2TestAdapter', function() { + function getConfig() { + return vscode.workspace.getConfiguration( + 'catch2TestExplorer', workspaceFolderUri) }; -}; - -class ChildProcessStub extends EventEmitter { - readonly stdout = new Stream.Readable(); - - constructor(data?: string) { - super(); - this.stdout.on('end', () => { - this.emit('close', 1); - }); - if (data != undefined) this.writeAndClose(data); - } - - writeAndClose(data: string): void { - this.stdout.push(data); - this.stdout.push(null); - } - writeLineByLineAndClose(data: string): void { - const lines = data.split('\n'); - lines.forEach((l) => { - this.stdout.push(l); - }); - this.stdout.push(null); + function updateConfig(key: string, value: any) { + return getConfig().update(key, value) } -}; - -/// - -describe('C2TestAdapter', function() { - const config = vscode.workspace.getConfiguration( - 'catch2TestExplorer', workspaceFolderUri); let adapter: C2TestAdapter|undefined; let testsEvents: (TestLoadStartedEvent|TestLoadFinishedEvent)[]; - let testsEventsConnection: vscode.Disposable; + let testsEventsConnection: vscode.Disposable|undefined; let testStatesEvents: (TestRunStartedEvent|TestRunFinishedEvent| TestSuiteEvent|TestEvent)[]; - let testStatesEventsConnection: vscode.Disposable; + let testStatesEventsConnection: vscode.Disposable|undefined; - let spawnStub: any; - let execFileStub: any; - let fsWatchStub: any; - let fsExistsStub: any; + let spawnStub: sinon.SinonStub; + let fsWatchStub: sinon.SinonStub; + let fsExistsStub: sinon.SinonStub; + let c2fsReaddirSyncStub: sinon.SinonStub; + let c2fsStatSyncStub: sinon.SinonStub; function resetConfig(): Thenable { const packageJson = fse.readJSONSync( @@ -301,12 +76,30 @@ describe('C2TestAdapter', function() { assert.ok(key.startsWith('catch2TestExplorer.')); const k = key.replace('catch2TestExplorer.', '') t = t.then(() => { - return config.update(k, undefined); + return getConfig().update(k, undefined); }); }); return t; } + function testStatesEvI(o: any) { + const i = testStatesEvents.findIndex( + (v: TestRunStartedEvent|TestRunFinishedEvent|TestSuiteEvent| + TestEvent) => { + if (o.type == v.type) + if (o.type == 'suite' || o.type == 'test') + return o.state === (v).state && + o[o.type] === (v)[v.type]; + return deepStrictEqual(o, v); + }); + assert.notEqual( + i, -1, + 'testStatesEvI failed to find: ' + inspect(o) + '\n\nin\n\n' + + inspect(testStatesEvents)); + assert.deepStrictEqual(testStatesEvents[i], o); + return i; + }; + function createAdapterAndSubscribe() { adapter = new C2TestAdapter(workspaceFolder, logger); @@ -327,36 +120,52 @@ describe('C2TestAdapter', function() { } function disposeAdapterAndSubscribers() { - testsEventsConnection.dispose(); - testStatesEventsConnection.dispose(); + adapter && adapter.dispose(); + testsEventsConnection && testsEventsConnection.dispose(); + testStatesEventsConnection && testStatesEventsConnection.dispose(); testsEvents = []; 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() { + spawnStub.reset(); + fsWatchStub.reset(); + fsExistsStub.reset(); + c2fsReaddirSyncStub.reset(); + c2fsStatSyncStub.reset(); + stubsThrowByDefault(); + // TODO stub.callThrough(); + } + before(() => { fse.removeSync(dotVscodePath); adapter = undefined; spawnStub = sinonSandbox.stub(child_process, 'spawn'); - execFileStub = sinonSandbox.stub(child_process, 'execFile'); fsWatchStub = sinonSandbox.stub(fs, 'watch'); fsExistsStub = sinonSandbox.stub(fs, 'exists'); + c2fsReaddirSyncStub = sinonSandbox.stub(c2fs, 'readdirSync'); + c2fsStatSyncStub = sinonSandbox.stub(c2fs, 'statSync'); + + stubsResetToThrow(); // reset config can cause problem with fse.removeSync(dotVscodePath); return resetConfig(); }); - function resetStubs() { - spawnStub.reset(); - spawnStub.throws(); - execFileStub.reset(); - execFileStub.throws(); - fsWatchStub.reset(); - fsWatchStub.throws(); - fsExistsStub.reset(); - fsExistsStub.throws(); - } - after(() => { disposeAdapterAndSubscribers(); sinonSandbox.restore(); @@ -390,36 +199,40 @@ describe('C2TestAdapter', function() { it('workerMaxNumber', () => { createAdapterAndSubscribe(); - assert.deepEqual(testsEvents, []); - return config.update('workerMaxNumber', 42).then(waitForReloadAndAssert); + assert.deepStrictEqual(testsEvents, []); + return getConfig() + .update('workerMaxNumber', 42) + .then(waitForReloadAndAssert); }); it('defaultEnv', () => { createAdapterAndSubscribe(); - assert.deepEqual(testsEvents, []); - return config.update('defaultEnv', {'APPLE': 'apple'}) + assert.deepStrictEqual(testsEvents, []); + return getConfig() + .update('defaultEnv', {'APPLE': 'apple'}) .then(waitForReloadAndAssert); }); it('defaultCwd', () => { createAdapterAndSubscribe(); - assert.deepEqual(testsEvents, []); - return config.update('defaultCwd', 'apple/peach') + assert.deepStrictEqual(testsEvents, []); + return getConfig() + .update('defaultCwd', 'apple/peach') .then(waitForReloadAndAssert); }); it('enableSourceDecoration', () => { const adapter = createAdapterAndSubscribe(); - assert.deepEqual(testsEvents, []); - return config.update('enableSourceDecoration', false).then(() => { + assert.deepStrictEqual(testsEvents, []); + return getConfig().update('enableSourceDecoration', false).then(() => { assert.ok(!adapter.getIsEnabledSourceDecoration()); }); }); it('defaultRngSeed', () => { const adapter = createAdapterAndSubscribe(); - assert.deepEqual(testsEvents, []); - return config.update('defaultRngSeed', 987).then(() => { + assert.deepStrictEqual(testsEvents, []); + return getConfig().update('defaultRngSeed', 987).then(() => { assert.equal(adapter.getRngSeed(), 987); }); }); @@ -435,7 +248,6 @@ describe('C2TestAdapter', function() { afterEach(() => { disposeAdapterAndSubscribers(); - resetStubs(); }); it('fill with empty config', function() { @@ -452,6 +264,20 @@ describe('C2TestAdapter', function() { 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]); + }); + } + } + }) + + after(() => { + stubsResetToThrow(); + }); + beforeEach(() => { return adapter.load() .then(() => { @@ -461,18 +287,20 @@ describe('C2TestAdapter', function() { }) .then((suite: TestSuiteInfo) => { const root = suite; - const s1 = root.createChildSuite('s1', 'execPath1', {}); - const s1t1 = - s1.createChildTest('s1t1', 'd', ['tag1'], 'suite1.cpp', 1); - const s1t2 = - s1.createChildTest('s1t2', 'd', ['tag1'], 'suite1.cpp', 2); - const s2 = root.createChildSuite('s2', 'execPath2', {}); - const s2t1 = - s2.createChildTest('s2t1', 'd', ['tag1'], 'suite2.cpp', 1); - const s2t2 = - s2.createChildTest('s2t2', 'd', ['[.]'], 'suite2.cpp', 2); - const s2t3 = - s2.createChildTest('s2t3', 'd', ['tag1'], 'suite2.cpp', 3); + 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, @@ -485,35 +313,11 @@ describe('C2TestAdapter', function() { s2t3: s2t3 } }); - }); + }) it('run: 1 test (succ)', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push( - example1.suite1.xmlHeader + example1.suite1.t1.xml + - ` - - - `); - stdout.push(null); - - spawnStub - .withArgs( - tests.s1.execPath, - [ - tests.s1t1.testNameFull, '--reporter', 'xml', '--durations', - 'yes' - ], - tests.s1.execOptions) - .returns(spawnEvent); - return adapter.run([tests.s1t1.id]).then(() => { - assert.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: [tests.s1t1.id]}, {type: 'suite', state: 'running', suite: tests.s1}, {type: 'test', state: 'running', test: tests.s1t1}, @@ -522,77 +326,17 @@ describe('C2TestAdapter', function() { state: 'passed', test: tests.s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + message: 'Duration: 0.000112 second(s)\n' }, {type: 'suite', state: 'completed', suite: tests.s1}, {type: 'finished'}, ]); }); - }); - // it('run: 1 test (succ)' - - it('run: 1 test (missing)', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push( - example1.suite1.xmlHeader + - ` - - - `); - stdout.push(null); - - spawnStub - .withArgs( - tests.s1.execPath, - [ - tests.s1t1.testNameFull, '--reporter', 'xml', '--durations', - 'yes' - ], - tests.s1.execOptions) - .returns(spawnEvent); - - return adapter.run([tests.s1t1.id]).then(() => { - assert.deepEqual(testStatesEvents, [ - {type: 'started', tests: [tests.s1t1.id]}, - {type: 'suite', state: 'running', suite: tests.s1}, - {type: 'suite', state: 'completed', suite: tests.s1}, - {type: 'finished'}, - ]); - }); - }); - // it('run: 1 test (missing)' + }) it('run: 1 test (skipped)', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite1.xmlHeader + example1.suite2.t2.xml + ` - - - - `); - stdout.push(null); - - spawnStub - .withArgs( - tests.s2.execPath, - [ - tests.s2t2.testNameFull, '--reporter', 'xml', '--durations', - 'yes' - ], - tests.s2.execOptions) - .returns(spawnEvent); - return adapter.run([tests.s2t2.id]).then(() => { - assert.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: [tests.s2t2.id]}, {type: 'suite', state: 'running', suite: tests.s2}, {type: 'test', state: 'running', test: tests.s2t2}, @@ -601,7 +345,7 @@ describe('C2TestAdapter', function() { state: 'passed', test: tests.s2t2, decorations: undefined, - message: 'Randomness seeded to: 2\n' + message: 'Duration: 0.001294 second(s)\n' }, {type: 'suite', state: 'completed', suite: tests.s2}, {type: 'finished'}, @@ -611,32 +355,8 @@ describe('C2TestAdapter', function() { // it('run: 1 test (skipped)' it('run: 1 test (fails)', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push( - example1.suite1.xmlHeader + example1.suite2.t3.xml + - ` - - - `); - stdout.push(null); - - spawnStub - .withArgs( - tests.s2.execPath, - [ - tests.s2t3.testNameFull, '--reporter', 'xml', '--durations', - 'yes' - ], - tests.s2.execOptions) - .returns(spawnEvent); - return adapter.run([tests.s2t3.id]).then(() => { - assert.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: [tests.s2t3.id]}, {type: 'suite', state: 'running', suite: tests.s2}, {type: 'test', state: 'running', test: tests.s2t3}, @@ -646,7 +366,7 @@ describe('C2TestAdapter', function() { test: tests.s2t3, decorations: [{line: 20, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000199 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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'}, @@ -655,38 +375,14 @@ describe('C2TestAdapter', function() { }); // it('run: 1 test (fails)' - it('run: 1 test (fails) with chunks', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - - spawnStub - .withArgs( - tests.s2.execPath, - [ - tests.s2t3.testNameFull, '--reporter', 'xml', '--durations', - 'yes' - ], - tests.s1.execOptions) - .returns(spawnEvent); - - example1.suite1.xmlHeader.split('\n').forEach( - (l: string) => {stdout.push(l)}); - example1.suite2.t3.xml.split('\n').forEach( - (l: string) => {stdout.push(l)}); - stdout.push( - ' \n'); - stdout.push(' \n'); - stdout.push( - ' \n'); - stdout.push('\n'); - stdout.push(null); + 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.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: [tests.s2t3.id]}, {type: 'suite', state: 'running', suite: tests.s2}, {type: 'test', state: 'running', test: tests.s2t3}, @@ -696,7 +392,7 @@ describe('C2TestAdapter', function() { test: tests.s2t3, decorations: [{line: 20, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000199 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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'}, @@ -706,23 +402,8 @@ describe('C2TestAdapter', function() { // it('run: 1 test (fails) with chunks' it('run: suite1 (1 succ 1 fails)', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite1.xmlFull); - stdout.push(null); - - spawnStub - .withArgs( - tests.s1.execPath, ['--reporter', 'xml', '--durations', 'yes'], - tests.s1.execOptions) - .returns(spawnEvent); - return adapter.run([tests.s1.id]).then(() => { - assert.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: [tests.s1.id]}, {type: 'suite', state: 'running', suite: tests.s1}, {type: 'test', state: 'running', test: tests.s1t1}, @@ -731,7 +412,7 @@ describe('C2TestAdapter', function() { state: 'passed', test: tests.s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + message: 'Duration: 0.000132 second(s)\n' }, {type: 'test', state: 'running', test: tests.s1t2}, { @@ -740,7 +421,7 @@ describe('C2TestAdapter', function() { test: tests.s1t2, decorations: [{line: 14, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000255 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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'}, @@ -750,63 +431,20 @@ describe('C2TestAdapter', function() { // it('run: suite1 (1 succ 1 fails)' it('run: root (at least 2 slots)', function() { - { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite1.xmlFull); - stdout.push(null); - - spawnStub - .withArgs( - tests.s1.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s1.execOptions) - .returns(spawnEvent); - } - { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite2.xmlFull); - stdout.push(null); - - spawnStub - .withArgs( - tests.s2.execPath, - ['--reporter', 'xml', '--durations', 'yes'], - tests.s2.execOptions) - .returns(spawnEvent); - } - return adapter.run([tests.root.id]).then(() => { - assert.deepEqual( + assert.deepStrictEqual( {type: 'started', tests: [tests.root.id]}, testStatesEvents[0]); - assert.deepEqual( + assert.deepStrictEqual( {type: 'finished'}, testStatesEvents[testStatesEvents.length - 1]); - const findIndex = function(o: any) { - const i = testStatesEvents.findIndex((v) => { - return deepEqual(o, v); - }); - assert.notEqual(i, -1, 'findIndex failed to find: ' + inspect(o)); - return i; - }; - const s1running = {type: 'suite', state: 'running', suite: tests.s1}; const s1finished = { type: 'suite', state: 'completed', suite: tests.s1 }; - assert.ok(findIndex(s1running) < findIndex(s1finished)); + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1finished)); const s2running = {type: 'suite', state: 'running', suite: tests.s2}; const s2finished = { @@ -814,31 +452,31 @@ describe('C2TestAdapter', function() { state: 'completed', suite: tests.s2 }; - assert.ok(findIndex(s2running) < findIndex(s2finished)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2finished)); const s1t1running = { type: 'test', state: 'running', test: tests.s1t1 }; - assert.ok(findIndex(s1running) < findIndex(s1t1running)); + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t1running)); const s1t1finished = { type: 'test', state: 'passed', test: tests.s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + message: 'Duration: 0.000132 second(s)\n' }; - assert.ok(findIndex(s1t1running) < findIndex(s1t1finished)); - assert.ok(findIndex(s1t1finished) < findIndex(s1finished)); + assert.ok(testStatesEvI(s1t1running) < testStatesEvI(s1t1finished)); + assert.ok(testStatesEvI(s1t1finished) < testStatesEvI(s1finished)); const s1t2running = { type: 'test', state: 'running', test: tests.s1t2 }; - assert.ok(findIndex(s1running) < findIndex(s1t2running)); + assert.ok(testStatesEvI(s1running) < testStatesEvI(s1t2running)); const s1t2finished = { type: 'test', @@ -846,49 +484,49 @@ describe('C2TestAdapter', function() { test: tests.s1t2, decorations: [{line: 14, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000255 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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(findIndex(s1t2running) < findIndex(s1t2finished)); - assert.ok(findIndex(s1t2finished) < findIndex(s1finished)); + assert.ok(testStatesEvI(s1t2running) < testStatesEvI(s1t2finished)); + assert.ok(testStatesEvI(s1t2finished) < testStatesEvI(s1finished)); const s2t1running = { type: 'test', state: 'running', test: tests.s2t1 }; - assert.ok(findIndex(s2running) < findIndex(s2t1running)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t1running)); const s2t1finished = { type: 'test', state: 'passed', test: tests.s2t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000165 second(s)\n' + message: 'Duration: 0.00037 second(s)\n' }; - assert.ok(findIndex(s2t1running) < findIndex(s2t1finished)); - assert.ok(findIndex(s2t1finished) < findIndex(s2finished)); + assert.ok(testStatesEvI(s2t1running) < testStatesEvI(s2t1finished)); + assert.ok(testStatesEvI(s2t1finished) < testStatesEvI(s2finished)); const s2t2running = { type: 'test', state: 'running', test: tests.s2t2 }; - assert.ok(findIndex(s2running) < findIndex(s2t2running)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t2running)); const s2t2finished = { type: 'test', state: 'skipped', test: tests.s2t2 }; - assert.ok(findIndex(s2t2running) < findIndex(s2t2finished)); - assert.ok(findIndex(s2t2finished) < findIndex(s2finished)); + assert.ok(testStatesEvI(s2t2running) < testStatesEvI(s2t2finished)); + assert.ok(testStatesEvI(s2t2finished) < testStatesEvI(s2finished)); const s2t3running = { type: 'test', state: 'running', test: tests.s2t3 }; - assert.ok(findIndex(s2running) < findIndex(s2t3running)); + assert.ok(testStatesEvI(s2running) < testStatesEvI(s2t3running)); const s2t3finished = { type: 'test', @@ -896,75 +534,72 @@ describe('C2TestAdapter', function() { test: tests.s2t3, decorations: [{line: 20, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000199 second(s)\n>>> s2t3(line: 19) REQUIRE (line: 21) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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(findIndex(s2t3running) < findIndex(s2t3finished)); - assert.ok(findIndex(s2t3finished) < findIndex(s2finished)); + assert.ok(testStatesEvI(s2t3running) < testStatesEvI(s2t3finished)); + assert.ok(testStatesEvI(s2t3finished) < testStatesEvI(s2finished)); assert.equal(testStatesEvents.length, 16, inspect(testStatesEvents)); }); - }); - // it('run: root (at least 2 slots)' - - it('run: wrong xml 1', function() { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - const testCaseBegin = example1.suite1.t1.xml.split('\n')[1]; - assert.ok(testCaseBegin.indexOf(']+>'); + 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)); - return adapter.run([tests.s1t1.id]).then(() => { - assert.deepEqual(testStatesEvents, [ - {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'}, - ]); - }); - }); - // it('run: wrong xml 1' + 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: empty' + }) it('cancel', function() { const suite1Kill = sinon.spy(); const suite2Kill = sinon.spy(); { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); + const spawnEvent = + new ChildProcessStub(example1.suite1.outputs[2][1]); spawnEvent.kill = suite1Kill; - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite1.xmlFull); - stdout.push(null); - spawnStub .withArgs( tests.s1.execPath, @@ -973,16 +608,9 @@ describe('C2TestAdapter', function() { .returns(spawnEvent); } { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); + const spawnEvent = + new ChildProcessStub(example1.suite2.outputs[2][1]); spawnEvent.kill = suite2Kill; - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite2.xmlFull); - stdout.push(null); - spawnStub .withArgs( tests.s2.execPath, @@ -993,29 +621,21 @@ describe('C2TestAdapter', function() { const run = adapter.run([tests.root.id]); adapter.cancel(); run.then(() => { - assert.deepEqual( + assert.deepStrictEqual( testStatesEvents, [{type: 'started', tests: [tests.root.id]}, {type: 'finished'}]); assert.equal(suite1Kill.callCount, 1); assert.equal(suite2Kill.callCount, 1); }); - }); - // it('cancel' + }) it('cancel: after run finished', function() { const suite1Kill = sinon.spy(); const suite2Kill = sinon.spy(); { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); + const spawnEvent = + new ChildProcessStub(example1.suite1.outputs[2][1]); spawnEvent.kill = suite1Kill; - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite1.xmlFull); - stdout.push(null); - spawnStub .withArgs( tests.s1.execPath, @@ -1024,16 +644,9 @@ describe('C2TestAdapter', function() { .returns(spawnEvent); } { - const stdout = new Stream.Readable(); - const spawnEvent: any = new EventEmitter(); + const spawnEvent = + new ChildProcessStub(example1.suite2.outputs[2][1]); spawnEvent.kill = suite2Kill; - spawnEvent.stdout = stdout; - stdout.on('end', () => { - spawnEvent.emit('close', 1); - }); - stdout.push(example1.suite2.xmlFull); - stdout.push(null); - spawnStub .withArgs( tests.s2.execPath, @@ -1047,12 +660,9 @@ describe('C2TestAdapter', function() { assert.equal(suite1Kill.callCount, 0); assert.equal(suite2Kill.callCount, 0); }); - }); - // it('cancel: after run finished' - }); - // describe('example1' - }); - // describe('adapter:' + }) + }) + }) describe('executables:', function() { this.slow(150); @@ -1060,12 +670,13 @@ describe('C2TestAdapter', function() { afterEach(() => { disposeAdapterAndSubscribers(); - resetStubs(); + stubsResetToThrow(); return resetConfig(); }); const updateAndVerify = (value: any, expected: any[]) => { - return config.update('executables', value) + return getConfig() + .update('executables', value) .then(() => { const adapter = createAdapterAndSubscribe(); @@ -1074,7 +685,7 @@ describe('C2TestAdapter', function() { verifyIsCatch2TestExecutable.returns(Promise.resolve(true)); const loadSuiteMock = sinon.expectation.create('loadSuiteMock'); - loadSuiteMock.returns(Promise.resolve()).exactly(expected.length); + loadSuiteMock.exactly(expected.length).returns(Promise.resolve()) sinonSandbox.replace(adapter, 'loadSuite', loadSuiteMock); return adapter.load().then(() => { @@ -1091,7 +702,7 @@ describe('C2TestAdapter', function() { Object.keys(arg.env).filter(k => k.startsWith('C2TEST')); const newEnv: {[prop: string]: string} = {}; filteredKeys.forEach((k: string) => { - newEnv[k] = args.env[k]; + newEnv[k] = arg.env[k]; }) arg.env = newEnv; return arg; @@ -1106,7 +717,7 @@ describe('C2TestAdapter', function() { path: path.join(cwd, 'exe1.exe'), regex: '', cwd: cwd, - env: [] + env: {} }]); }); @@ -1117,14 +728,14 @@ describe('C2TestAdapter', function() { path: path.join(cwd, 'exe1.exe'), regex: '', cwd: cwd, - env: [] + env: {} }, { name: 'exe2.exe', path: path.join(cwd, 'exe2.exe'), regex: '', cwd: cwd, - env: [] + env: {} } ]); }); @@ -1135,109 +746,35 @@ describe('C2TestAdapter', function() { path: path.join(cwd, 'path1'), regex: '', cwd: cwd, - env: [] + env: {} }]); }); - }); - // describe('executables:' - - context('example1', function() { - function fakeExecFileFunc(pathAndContent: Map) { - return function( - path: string, args: string[], - cb: (err: any, stout: string, stderr: string) => void) { - const res = pathAndContent.get(path); - if (res === undefined) { - cb(new Error('fake file not exists.'), '', ''); - } else if (args.length == 1 && args[0] === '--help') { - cb(null, 'Catch v2.', ''); - } else if (deepEqual(args, [ - '[.],*', '--verbosity', 'high', '--list-tests', - '--use-colour', 'no' - ])) { - cb(null, res!, ''); - } else { - assert.ok(false, inspect([path, args])); - }; - }; - }; - - function fakeExistsFunc(pathAndContent: Map) { - return function(path: string, cb: (err: any, exists: boolean) => void) { - cb(undefined, pathAndContent.has(path)); - }; - }; + }) - function fakeSpawn() { - const testTestParams = ['--reporter', 'xml', '--durations', 'yes']; - - spawnStub - .withArgs( - example1.suite1.execPath, - [example1.suite1.t1.fullTestName, ...testTestParams]) - .callsFake(() => { - return new ChildProcessStub( - example1.suite1.xmlHeader + example1.suite1.t1.xml + - ''); - }); - spawnStub - .withArgs( - example1.suite1.execPath, - [example1.suite1.t2.fullTestName, ...testTestParams]) - .callsFake(() => { - return new ChildProcessStub( - example1.suite1.xmlHeader + example1.suite1.t2.xml + - ''); - }); - spawnStub.withArgs(example1.suite1.execPath, testTestParams) - .callsFake(() => { - return new ChildProcessStub(example1.suite1.xmlFull); - }); - - spawnStub - .withArgs( - example1.suite2.execPath, - [example1.suite2.t1.fullTestName, ...testTestParams]) - .callsFake(() => { - return new ChildProcessStub( - example1.suite2.xmlHeader + example1.suite2.t1.xml + - ''); - }); - spawnStub - .withArgs( - example1.suite2.execPath, - [example1.suite2.t2.fullTestName, ...testTestParams]) - .callsFake(() => { - return new ChildProcessStub( - example1.suite2.xmlHeader + example1.suite2.t2.xml + - ''); - }); - spawnStub - .withArgs( - example1.suite2.execPath, - [example1.suite2.t3.fullTestName, ...testTestParams]) - .callsFake(() => { - return new ChildProcessStub( - example1.suite2.xmlHeader + example1.suite2.t3.xml + - ''); - }); - spawnStub.withArgs(example1.suite2.execPath, testTestParams) - .callsFake(() => { - return new ChildProcessStub(example1.suite2.xmlFull); + describe('load: example1', function() { + 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]); }); - } + } + } - function fakeFs(pathAndContent: Iterable<[string, string]>) { - const map = new Map(pathAndContent); - execFileStub.reset(); - execFileStub.callsFake(fakeExecFileFunc(map)); + const exists = (path: string) => { + return example1.outputs.findIndex((v) => { + return v[0] == path; + }) != -1; + }; - fsExistsStub.reset(); - fsExistsStub.callsFake(fakeExistsFunc(map)); + fsExistsStub.withArgs(workspaceFolderMatcher) + .callsFake(function( + path: string, cb: (err: any, exists: boolean) => void) { + cb(undefined, exists(path)); + }); - fsWatchStub.reset(); - fsWatchStub.callsFake((path: string) => { - if (map.has(path)) { + fsWatchStub.withArgs(workspaceFolderMatcher).callsFake((path: string) => { + if (exists(path)) { const ee = new class extends EventEmitter { close() {} }; @@ -1248,71 +785,324 @@ describe('C2TestAdapter', function() { } }); - fakeSpawn(); - }; + const dirContent: Map = new Map(); + for (let p of example1.outputs) { + const parent = path.dirname(p[0]); + let children: string[] = []; + if (dirContent.has(parent)) + children = dirContent.get(parent)!; + else + dirContent.set(parent, children); + children.push(path.basename(p[0])); + } + + 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(); + }) const uniqueIdC = new Set(); const watchEvents: Map = new Map(); + let adapter: TestAdapter; + let root: TestSuiteInfo; - before(() => { - fakeFs([ - [example1.suite1.execPath, example1.suite1.testList], - [example1.suite2.execPath, example1.suite2.testList] - ]); - }); + beforeEach(async function() { + adapter = createAdapterAndSubscribe(); + await adapter.load(); - beforeEach(() => { - watchEvents.clear(); - uniqueIdC.clear(); - }) + 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(); - after(() => { - resetStubs(); + example1.assertWithoutChildren(root, uniqueIdC); + assert.deepStrictEqual(testStatesEvents, []); }); - context.only('load with config: executables="execPath1"', function() { - let adapter: TestAdapter; - let root: TestSuiteInfo; + afterEach(() => { + uniqueIdC.clear(); + watchEvents.clear(); + disposeAdapterAndSubscribers(); + }); + context('executables="execPath1"', function() { before(() => { - return config.update('executables', 'execPath1'); + 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!; + let suite1: TestSuiteInfo; + let s1t1: TestInfo; + let s1t2: TestInfo; - example1.assertWithoutChildren(root, uniqueIdC); + beforeEach(async function() { + assert.deepStrictEqual( + getConfig().get('executables'), 'execPath1'); assert.equal(root.children.length, 1); - example1.suite1.assert(root.children[0], uniqueIdC); + 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 with not existing test id', async function() { + await adapter.run(['not existing id']); + + assert.deepStrictEqual(testStatesEvents, [ + {type: 'started', tests: ['not existing id']}, + {type: 'finished'}, + ]); }); - afterEach(() => { - disposeAdapterAndSubscribers(); + 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([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); + + await adapter.run([suite1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...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); + + await adapter.run([root.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }); + + it('cancels without any problem', async function() { + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, []); + + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, []); + + 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); + + adapter.cancel(); + assert.deepStrictEqual(testsEvents, []); + assert.deepStrictEqual(testStatesEvents, expected); + }); + + context('with config: defaultRngSeed=2', function() { + before(() => { + return updateConfig('defaultRngSeed', 2); + }) + + after(() => { + return updateConfig('defaultRngSeed', undefined); + }) + + 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); + + await adapter.run([s1t1.id]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); + }) + }) + }) + + context('executables=["execPath1", "${workspaceFolder}/execPath2"]', () => { + before(() => { + return updateConfig( + 'executables', ['execPath1', '${workspaceFolder}/execPath2']); }); after(() => { - return resetConfig(); + return updateConfig('executables', undefined); + }); + + let suite1: TestSuiteInfo; + let s1t1: TestInfo; + let s1t2: TestInfo; + let suite2: TestSuiteInfo; + let s2t1: TestInfo; + let s2t2: TestInfo; + let s2t3: TestInfo; + + 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]; + } + + 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]; }); it('should run with not existing test id', async function() { await adapter.run(['not existing id']); - assert.deepEqual(testStatesEvents, [ + assert.deepStrictEqual(testStatesEvents, [ {type: 'started', tests: ['not existing id']}, {type: 'finished'}, ]); }); it('should run s1t1 with success', async function() { - const suite1 = root.children[0]; - const s1t1 = suite1.children[0]; - await adapter.run([s1t1.id]); const expected = [ {type: 'started', tests: [s1t1.id]}, @@ -1323,22 +1113,18 @@ describe('C2TestAdapter', function() { state: 'passed', test: s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + message: 'Duration: 0.000112 second(s)\n' }, {type: 'suite', state: 'completed', suite: suite1}, {type: 'finished'}, ]; - assert.deepEqual(testStatesEvents, expected); + assert.deepStrictEqual(testStatesEvents, expected); await adapter.run([s1t1.id]); - assert.deepEqual(testStatesEvents, [...expected, ...expected]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); }); it('should run suite1', async function() { - const suite1 = root.children[0]; - const s1t1 = suite1.children[0]; - const s1t2 = suite1.children[1]; - await adapter.run([suite1.id]); const expected = [ {type: 'started', tests: [suite1.id]}, @@ -1349,7 +1135,7 @@ describe('C2TestAdapter', function() { state: 'passed', test: s1t1, decorations: undefined, - message: 'Randomness seeded to: 2\nDuration: 0.000174 second(s)\n' + message: 'Duration: 0.000132 second(s)\n' }, {type: 'test', state: 'running', test: s1t2}, { @@ -1358,20 +1144,278 @@ describe('C2TestAdapter', function() { test: s1t2, decorations: [{line: 14, message: 'Expanded: false'}], message: - 'Randomness seeded to: 2\nDuration: 0.000255 second(s)\n>>> s1t2(line: 13) REQUIRE (line: 15) \n Original:\n std::false_type::value\n Expanded:\n false\n<<<\n' + '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.deepEqual(testStatesEvents, expected); + assert.deepStrictEqual(testStatesEvents, expected); await adapter.run([suite1.id]); - assert.deepEqual(testStatesEvents, [...expected, ...expected]); + assert.deepStrictEqual(testStatesEvents, [...expected, ...expected]); }); - }); - }); // descibe('example1' -}); -// describe('C2TestAdapter' + + 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 () => { + 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 () => { + await updateConfig('executables', undefined); + await updateConfig('defaultEnv', undefined); + }); + + let suite1: TestSuiteInfo; + let s1t1: TestInfo; + let s1t2: TestInfo; + let suite2: TestSuiteInfo; + let s2t1: TestInfo; + let s2t2: TestInfo; + let s2t3: TestInfo; + + 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]; + }) + + 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() { + 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]); + }); + 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]); + }); + await adapter.run([suite2.id]); + assert.ok(called2); + }); + }) + }) +}) describe.skip('a', function() { this.timeout(99999); diff --git a/src/test/FsWrapper.test.ts b/src/test/FsWrapper.test.ts new file mode 100644 index 00000000..a6ea0b33 --- /dev/null +++ b/src/test/FsWrapper.test.ts @@ -0,0 +1,23 @@ +import * as assert from 'assert'; +import {spawnAsync} from '../FsWrapper'; + +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[0], 'apple\n'); + assert.equal(r.status, 0); + }) + + it.skip('sleeps', async function() { + this.timeout(1100); + this.slow(1050); + if (process.platform === 'darwin') { + const r = await spawnAsync('sleep', ['1']); + assert.equal(r.stdout, ''); + assert.equal(r.output.length, 0); + assert.equal(r.status, 0); + } + }) +}); \ No newline at end of file diff --git a/src/test/Helpers.ts b/src/test/Helpers.ts new file mode 100644 index 00000000..5ca63c21 --- /dev/null +++ b/src/test/Helpers.ts @@ -0,0 +1,40 @@ +import {EventEmitter} from 'events'; +import {Stream} from 'stream'; + +export class ChildProcessStub extends EventEmitter { + readonly stdout = new Stream.Readable(); + + constructor(data?: string|Iterable) { + super(); + this.stdout.on('end', () => { + this.emit('close', 1); + }); + if (data != undefined) { + if (typeof data != 'string') { + for (let line of data) { + this.stdout.push(line); + } + this.stdout.push(null); + } else { + this.writeAndClose(data); + } + } + } + + kill() { + this.stdout.emit('end'); + } + + writeAndClose(data: string): void { + this.stdout.push(data); + this.stdout.push(null); + } + + writeLineByLineAndClose(data: string): void { + const lines = data.split('\n'); + lines.forEach((l) => { + this.stdout.push(l); + }); + this.stdout.push(null); + } +}; \ No newline at end of file diff --git a/src/test/example1.ts b/src/test/example1.ts new file mode 100644 index 00000000..50783198 --- /dev/null +++ b/src/test/example1.ts @@ -0,0 +1,484 @@ +import * as assert from 'assert'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import {TestInfo, TestSuiteInfo} from 'vscode-test-adapter-api'; + +assert.notEqual(vscode.workspace.workspaceFolders, undefined); +assert.equal(vscode.workspace.workspaceFolders!.length, 1); + +const workspaceFolderUri = vscode.workspace.workspaceFolders![0].uri; + +export const example1 = new class { + readonly suite1 = new class { + readonly execPath = path.join(workspaceFolderUri.path, 'execPath1'); + + readonly t1 = new class { + readonly fullTestName = 's1t1'; + assert(label: string, test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, label); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(test.line, 7 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly outputs: [string[], string][] = [ + [ + ['s1t1', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + ` + ], + [ + [ + 's1t1', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2' + ], + ` + + + + + + + + + + ` + ], + ]; + }; + + readonly t2 = new class { + readonly fullTestName = 's1t2'; + assert(label: string, test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, label); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(test.line, 13 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + }; + + readonly outputs: [string[], string][] = [ + [ + ['s1t2', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + [ + [ + 's1t2', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2' + ], + ` + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ] + ]; + }; + + readonly outputs: [string[], string][] = [ + [['--help'], 'Catch v2.'], + [ + ['[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', 'no'], + 'Matching test cases:\n' + + ' s1t1\n' + + ' suite1.cpp:7\n' + + ' tag1\n' + + ' s1t2\n' + + ' suite1.cpp:13\n' + + ' tag1\n' + + '2 matching test cases\n\n' + ], + [ + ['--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + [ + ['--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], + ` + + + + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + ...this.t1.outputs, + ...this.t2.outputs, + ]; + + assert( + label: string, childLabels: string[], suite: TestSuiteInfo, + uniqeIdContainer?: Set) { + assert.equal(suite.type, 'suite'); + assert.equal(suite.label, label); + assert.equal( + suite.file, path.join(workspaceFolderUri.path, 'suite1.cpp')); + assert.equal(suite.line, 0); + assert.equal(suite.children.length, 2); + assert.equal(childLabels.length, suite.children.length); + this.t1.assert( + childLabels[0], suite.children[0], uniqeIdContainer); + this.t2.assert( + childLabels[1], suite.children[1], uniqeIdContainer); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(suite.id)); + uniqeIdContainer.add(suite.id); + } + } + }; + + readonly suite2 = new class { + readonly execPath = path.join(workspaceFolderUri.path, 'execPath2'); + + readonly t1 = new class { + readonly fullTestName = 's2t1'; + assert(label: string, test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, label); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 7 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + } + + readonly outputs: [string[], string][] = [ + [ + ['s2t1', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + ` + ], + [ + [ + 's2t1', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2' + ], + ` + + + + + + + + + + ` + ] + ]; + }; + + readonly t2 = new class { + readonly fullTestName = 's2t2'; + assert(label: string, test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, label); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 13 - 1); + assert.ok(test.skipped === true); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + } + + readonly outputs: [string[], string][] = [ + [ + ['s2t2', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + ` + ], + [ + [ + 's2t2', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2' + ], + ` + + + + + + + + + + ` + ] + ]; + }; + + readonly t3 = new class { + readonly fullTestName = 's2t3'; + assert(label: string, test: TestInfo, uniqeIdContainer?: Set) { + assert.equal(test.type, 'test'); + assert.equal(test.label, label); + assert.equal( + test.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(test.line, 19 - 1); + assert.ok(test.skipped == undefined || test.skipped === false); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(test.id)); + uniqeIdContainer.add(test.id); + } + } + + readonly outputs: [string[], string][] = [ + [ + ['s2t3', '--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + [ + [ + 's2t3', '--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2' + ], + ` + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ] + ]; + }; + + assert( + label: string, childLabels: string[], suite: TestSuiteInfo, + uniqeIdContainer?: Set) { + assert.equal(suite.type, 'suite'); + assert.equal(suite.label, label); + assert.equal( + suite.file, path.join(workspaceFolderUri.path, 'suite2.cpp')); + assert.equal(suite.line, 0); + assert.equal(suite.children.length, 3); + assert.equal(childLabels.length, suite.children.length); + this.t1.assert( + childLabels[0], suite.children[0], uniqeIdContainer); + this.t2.assert( + childLabels[1], suite.children[1], uniqeIdContainer); + this.t3.assert( + childLabels[2], suite.children[2], uniqeIdContainer); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(suite.id)); + uniqeIdContainer.add(suite.id); + } + } + + readonly outputs: [string[], string][] = [ + [['--help'], 'Catch v2.'], + [ + ['[.],*', '--verbosity', 'high', '--list-tests', '--use-colour', 'no'], + 'Matching test cases:\n' + + ' s2t1\n' + + ' suite2.cpp:7\n' + + ' tag1\n' + + ' s2t2\n' + + ' suite2.cpp:13\n' + + ' tag1\n' + + ' [.]\n' + + ' s2t3\n' + + ' suite2.cpp:19\n' + + ' tag1\n' + + '3 matching test cases\n\n' + ], + [ + ['--reporter', 'xml', '--durations', 'yes'], + ` + + + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + [ + ['--reporter', 'xml', '--durations', 'yes', '--rng-seed', '2'], + ` + + + + + + + + + + std::false_type::value + + + false + + + + + + + + ` + ], + ...this.t1.outputs, ...this.t2.outputs, ...this.t3.outputs + ]; + }; + + assertWithoutChildren(root: TestSuiteInfo, uniqeIdContainer?: Set) { + assert.equal(root.type, 'suite'); + assert.equal(root.label, 'AllTests'); + assert.equal(root.file, undefined); + assert.equal(root.line, undefined); + if (uniqeIdContainer != undefined) { + assert.ok(!uniqeIdContainer.has(root.id)); + uniqeIdContainer.add(root.id); + } + }; + + readonly outputs: [string, [string[], string][]][] = [ + [this.suite1.execPath, this.suite1.outputs], + [this.suite2.execPath, this.suite2.outputs] + ]; +}; \ No newline at end of file From db664638f4e48e933d31f7d7631475df6b825fa6 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Mon, 22 Oct 2018 03:34:59 +0200 Subject: [PATCH 7/7] readme.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 88aba60b..9c077ca9 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ Example: - Implement more [Catch command line options](https://github.com/catchorg/Catch2/blob/master/docs/command-line.md#specifying-which-tests-to-run), such as: - `--nothrow` -- Tests ## Contribution