From 56ebdd008157d9f9262818dd298cc8c2491a2244 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Fri, 4 Jan 2019 09:17:06 +0100 Subject: [PATCH 1/6] refactoring --- src/TestAdapter.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/TestAdapter.ts b/src/TestAdapter.ts index cb7c3f24..75194323 100644 --- a/src/TestAdapter.ts +++ b/src/TestAdapter.ts @@ -219,8 +219,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { const getDebugConfiguration = (): vscode.DebugConfiguration => { const config = this._getConfiguration(); - let template = - this._getDebugConfigurationTemplate(config); + let template = this._getDebugConfigurationTemplate(config); if (template !== null) { //skip @@ -244,16 +243,14 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { "cwd": "${cwd}", "env": "${envObj}" }; - } else { - throw 'C2: For debugging \'debugConfigTemplate\' should be set.'; } - if (template !== null && !template.hasOwnProperty('name')) - template = Object.assign({ 'name': "${label} (${suiteLabel})" }, template); - if (template !== null && !template.hasOwnProperty('request')) - template = Object.assign({ 'request': "launch" }, template); + if (!template) { + throw 'C2: For debugging \'debugConfigTemplate\' should be set.'; + } - const args = testInfo.getDebugParams(this._getDebugBreakOnFailure(config)); + template = Object.assign({ 'name': "${label} (${suiteLabel})" }, template); + template = Object.assign({ 'request': "launch" }, template); return resolveVariables(template, [ ...this._variableToValue, @@ -261,7 +258,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { ["${suiteLabel}", testInfo.parent.label], ["${label}", testInfo.label], ["${exec}", testInfo.parent.execPath], - ["${args}", args], + ["${args}", testInfo.getDebugParams(this._getDebugBreakOnFailure(config))], ["${cwd}", testInfo.parent.execOptions.cwd!], ["${envObj}", testInfo.parent.execOptions.env!], ]); From 44467041576c36dc1715b336d78b075c5013d652 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Fri, 4 Jan 2019 20:57:26 +0100 Subject: [PATCH 2/6] small refactor --- README.md | 2 +- src/RootTestSuiteInfo.ts | 1 - src/TestAdapter.ts | 4 ++-- src/TestSuiteInfoBase.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 03f16146..5707f56d 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Otherwise it only can point to an executable (No _search-pattern_!). "pattern": "{build,Build,BUILD,out,Out,OUT}/**/*{test,Test,TEST}*", "cwd": "${absDirpath}", "env": { - "ExampleENV1": "You can use variables here too, like ${absName}" + "ExampleENV1": "You can use variables here too, like ${absPath}" } } ``` diff --git a/src/RootTestSuiteInfo.ts b/src/RootTestSuiteInfo.ts index c58e8e5b..879a096b 100644 --- a/src/RootTestSuiteInfo.ts +++ b/src/RootTestSuiteInfo.ts @@ -32,7 +32,6 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { public readonly testStatesEmitter: vscode.EventEmitter, - public readonly autorunEmitter: vscode.EventEmitter, public readonly variableToValue: [string, string][], public isEnabledSourceDecoration: boolean, public rngSeed: string | number | null, diff --git a/src/TestAdapter.ts b/src/TestAdapter.ts index 75194323..8f35e8c3 100644 --- a/src/TestAdapter.ts +++ b/src/TestAdapter.ts @@ -112,7 +112,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._allTests = new RootTestSuiteInfo( this._allTasks, this._log, this.workspaceFolder, this._loadFinishedEmitter, this._testsEmitter, this._testStatesEmitter, - this._autorunEmitter, this._variableToValue, + this._variableToValue, this._getEnableSourceDecoration(config), this._getDefaultRngSeed(config), this._getDefaultExecWatchTimeout(config), @@ -149,7 +149,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._allTests = new RootTestSuiteInfo( this._allTasks, this._log, this.workspaceFolder, this._loadFinishedEmitter, this._testsEmitter, this._testStatesEmitter, - this._autorunEmitter, this._variableToValue, + this._variableToValue, this._getEnableSourceDecoration(config), this._getDefaultRngSeed(config), this._getDefaultExecWatchTimeout(config), diff --git a/src/TestSuiteInfoBase.ts b/src/TestSuiteInfoBase.ts index 0ceefcf8..735f3692 100644 --- a/src/TestSuiteInfoBase.ts +++ b/src/TestSuiteInfoBase.ts @@ -97,7 +97,7 @@ export abstract class TestSuiteInfoBase implements TestSuiteInfo { if (this._isKill) return Promise.reject(Error('Test was killed.')); if (!taskPool.acquire()) { - return new Promise(resolve => setTimeout(resolve, 64)).then(() => { + return new Promise(resolve => setTimeout(resolve, 128)).then(() => { return this._runInner(childrenToRun, taskPool); }); } From f708df411632c1571f25502918f6a324b7acde63 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Fri, 4 Jan 2019 23:31:04 +0100 Subject: [PATCH 3/6] better TaskPool --- src/QueueGraph.ts | 4 +-- src/RootTestSuiteInfo.ts | 13 ++++++--- src/TaskPool.ts | 60 ++++++++++++++++++++++++++++++++-------- src/TestAdapter.ts | 18 +++++++++--- src/TestSuiteInfoBase.ts | 11 ++------ 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/QueueGraph.ts b/src/QueueGraph.ts index afd995a3..8bd6510d 100644 --- a/src/QueueGraph.ts +++ b/src/QueueGraph.ts @@ -25,7 +25,7 @@ export class QueueGraphNode { then( task: (() => TResult1 | PromiseLike), taskErrorHandler?: ((reason: any) => TResult2 | PromiseLike) | - undefined | null): Promise { + undefined | null): Promise { this._count++; const previous = this._queue; @@ -35,7 +35,7 @@ export class QueueGraphNode { }); this._queue = current .then( - (value: TResult1 | PromiseLike) => { + (value: TResult1) => { this._count--; }, (reason: any) => { diff --git a/src/RootTestSuiteInfo.ts b/src/RootTestSuiteInfo.ts index 879a096b..011dfd0d 100644 --- a/src/RootTestSuiteInfo.ts +++ b/src/RootTestSuiteInfo.ts @@ -21,6 +21,7 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { readonly children: TestSuiteInfoBase[] = []; private readonly _executables: TestExecutableInfo[] = []; private _isDisposed = false; + private readonly _taskPool: TaskPool; constructor( private readonly _allTasks: QueueGraphNode, @@ -38,9 +39,15 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { public execWatchTimeout: number, public execRunningTimeout: null | number, public isNoThrow: boolean, + workerMaxNumber: number, ) { this.label = workspaceFolder.name + ' (Catch2 and Google Test Explorer)'; this.id = generateUniqueId(); + this._taskPool = new TaskPool(workerMaxNumber); + } + + set workerMaxNumber(workerMaxNumber: number) { + this._taskPool.maxTaskCount = workerMaxNumber; } dispose() { @@ -143,11 +150,9 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { } } - run(tests: string[], workerMaxNumber: number): Promise { + run(tests: string[]): Promise { this.testStatesEmitter.fire({ type: 'started', tests: tests }); - const taskPool = new TaskPool(workerMaxNumber); - // everybody should remove what they use from it. // and put their children into if they are in it const testSet = new Set(tests); @@ -161,7 +166,7 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { const ps: Promise[] = []; for (let i = 0; i < this.children.length; i++) { const child = this.children[i]; - ps.push(child.run(testSet, taskPool)); + ps.push(child.run(testSet, this._taskPool)); } if (testSet.size > 0) { diff --git a/src/TaskPool.ts b/src/TaskPool.ts index 524f9779..1f7a449f 100644 --- a/src/TaskPool.ts +++ b/src/TaskPool.ts @@ -1,19 +1,57 @@ export class TaskPool { /** - * - * @param availableSlot The available slot number. If -1 (negative) means no limit, acquire will always return true. + * @param maxTaskCount Has to be bigger than 0 or `undefined`. */ - constructor(private availableSlot: number) {} + constructor(private _maxTaskCount: number | undefined) { + if (_maxTaskCount != undefined && _maxTaskCount < 1) throw Error('invalid argument'); + } + + get maxTaskCount(): number | undefined { + return this._maxTaskCount; + } + + set maxTaskCount(maxTaskCount: number | undefined) { + if (maxTaskCount != undefined && maxTaskCount < 1) throw Error('invalid argument'); + this._maxTaskCount = maxTaskCount; + + while (this._waitingTasks.length > 0 && this._acquire()) + this._waitingTasks.pop()!(); + } - acquire(): boolean { - if (this.availableSlot < 0) return true; - if (this.availableSlot == 0) return false; - this.availableSlot -= 1; - return true; + scheduleTask(task: () => TResult | PromiseLike): Promise { + return new Promise(resolve => { + if (this._acquire()) + resolve(); + else + this._waitingTasks.unshift(resolve); + }).then(task) + .then( + (v: TResult) => { + this._release(); + return v; + }, + (reason?: any) => { + this._release(); + throw reason; + }); } - release(): void { - if (this.availableSlot < 0) return; - this.availableSlot += 1; + private _runningTaskCount: number = 0; + private readonly _waitingTasks: (() => void)[] = []; + + private _acquire(): boolean { + if (this._maxTaskCount === undefined || this._runningTaskCount < this._maxTaskCount) { + this._runningTaskCount += 1; + return true; + } else { + return false; + } + } + + private _release(): void { + this._runningTaskCount -= 1; + + while (this._waitingTasks.length > 0 && this._acquire()) + this._waitingTasks.pop()!(); } } diff --git a/src/TestAdapter.ts b/src/TestAdapter.ts index 8f35e8c3..bd949cf7 100644 --- a/src/TestAdapter.ts +++ b/src/TestAdapter.ts @@ -106,6 +106,12 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._allTests.isNoThrow = this._getDefaultNoThrow(this._getConfiguration()); } + if (configChange.affectsConfiguration( + 'catch2TestExplorer.workerMaxNumber', + this.workspaceFolder.uri)) { + this._allTests.workerMaxNumber = + this._getWorkerMaxNumber(this._getConfiguration()); + } })); const config = this._getConfiguration(); @@ -117,7 +123,9 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._getDefaultRngSeed(config), this._getDefaultExecWatchTimeout(config), this._getDefaultExecRunningTimeout(config), - this._getDefaultNoThrow(config)); + this._getDefaultNoThrow(config), + this._getWorkerMaxNumber(config) + ); } dispose() { @@ -154,7 +162,9 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { this._getDefaultRngSeed(config), this._getDefaultExecWatchTimeout(config), this._getDefaultExecRunningTimeout(config), - this._getDefaultNoThrow(config)); + this._getDefaultNoThrow(config), + this._getWorkerMaxNumber(config) + ); this._testsEmitter.fire({ type: 'started' }); @@ -186,7 +196,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { return this._allTasks.then(() => { return this._allTests - .run(tests, this._getWorkerMaxNumber(this._getConfiguration())) + .run(tests) .catch((reason: any) => { this._log.error(inspect(reason)); }); @@ -332,7 +342,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { } private _getWorkerMaxNumber(config: vscode.WorkspaceConfiguration): number { - return config.get('workerMaxNumber', 4); + return Math.max(1, config.get('workerMaxNumber', 1)); } private _getDefaultExecWatchTimeout(config: vscode.WorkspaceConfiguration): diff --git a/src/TestSuiteInfoBase.ts b/src/TestSuiteInfoBase.ts index 735f3692..02d90074 100644 --- a/src/TestSuiteInfoBase.ts +++ b/src/TestSuiteInfoBase.ts @@ -89,19 +89,13 @@ export abstract class TestSuiteInfoBase implements TestSuiteInfo { if (childrenToRun.length == 0) return Promise.resolve(); } - return this._runInner(childrenToRun, taskPool); + return taskPool.scheduleTask(() => { return this._runInner(childrenToRun); }); } - private _runInner(childrenToRun: TestInfoBase[] | 'all', taskPool: TaskPool): + private _runInner(childrenToRun: TestInfoBase[] | 'all'): Promise { if (this._isKill) return Promise.reject(Error('Test was killed.')); - if (!taskPool.acquire()) { - return new Promise(resolve => setTimeout(resolve, 128)).then(() => { - return this._runInner(childrenToRun, taskPool); - }); - } - this.allTests.testStatesEmitter.fire( { type: 'suite', suite: this, state: 'running' }); @@ -151,7 +145,6 @@ export abstract class TestSuiteInfoBase implements TestSuiteInfo { this.allTests.log.info('proc finished: ' + inspect(this.execPath)); this.allTests.testStatesEmitter.fire({ type: 'suite', suite: this, state: 'completed' }); - taskPool.release(); this._proc = undefined; runInfo.process = undefined; }); From 9d51456ab41f7cae9ffa90b8890960224d0c1870 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 5 Jan 2019 15:14:38 +0100 Subject: [PATCH 4/6] refactoring queuegraphnode -> TaskQueue --- src/RootTestSuiteInfo.ts | 4 +-- src/{QueueGraph.ts => TaskQueue.ts} | 25 +++++++------------ src/TestAdapter.ts | 10 +++----- .../{QueueGraph.test.ts => TaskQueue.test.ts} | 22 ++++++++-------- 4 files changed, 26 insertions(+), 35 deletions(-) rename src/{QueueGraph.ts => TaskQueue.ts} (59%) rename src/test/{QueueGraph.test.ts => TaskQueue.test.ts} (87%) diff --git a/src/RootTestSuiteInfo.ts b/src/RootTestSuiteInfo.ts index 011dfd0d..1d0ab7c5 100644 --- a/src/RootTestSuiteInfo.ts +++ b/src/RootTestSuiteInfo.ts @@ -11,7 +11,7 @@ import { TestExecutableInfo } from './TestExecutableInfo' import { TestInfoBase } from './TestInfoBase'; import { TestSuiteInfoBase } from './TestSuiteInfoBase'; import { generateUniqueId } from './IdGenerator'; -import { QueueGraphNode } from './QueueGraph'; +import { TaskQueue } from './TaskQueue'; import { TaskPool } from './TaskPool'; export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { @@ -24,7 +24,7 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { private readonly _taskPool: TaskPool; constructor( - private readonly _allTasks: QueueGraphNode, + private readonly _allTasks: TaskQueue, public readonly log: util.Log, public readonly workspaceFolder: vscode.WorkspaceFolder, private readonly _loadFinishedEmitter: vscode.EventEmitter, diff --git a/src/QueueGraph.ts b/src/TaskQueue.ts similarity index 59% rename from src/QueueGraph.ts rename to src/TaskQueue.ts index 8bd6510d..f0b0bf83 100644 --- a/src/QueueGraph.ts +++ b/src/TaskQueue.ts @@ -2,10 +2,9 @@ // 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. -export class QueueGraphNode { - constructor( - public readonly name?: string, depends: Iterable = [], - private readonly _handleError?: ((reason: any) => any)) { +export class TaskQueue { + constructor(depends: Iterable = [], + public readonly name?: string) { this._depends = [...depends]; // TODO check circular dependency } @@ -22,17 +21,17 @@ export class QueueGraphNode { return this._count; } - then( - task: (() => TResult1 | PromiseLike), - taskErrorHandler?: ((reason: any) => TResult2 | PromiseLike) | - undefined | null): Promise { + then( + task: (() => TResult1 | PromiseLike)): Promise { this._count++; const previous = this._queue; + const current = Promise.all(this._depends.map(v => v._queue)) .then(() => { return previous.then(task); }); + this._queue = current .then( (value: TResult1) => { @@ -40,18 +39,12 @@ export class QueueGraphNode { }, (reason: any) => { this._count--; - if (taskErrorHandler) - return taskErrorHandler(reason); - else if (this._handleError) - return this._handleError(reason); - else - throw reason; // fatal: the queue is broken }); return current; } - dependsOn(depends: Iterable): void { + dependsOn(depends: Iterable): void { for (const dep of depends) { this._depends.push(dep); } @@ -60,5 +53,5 @@ export class QueueGraphNode { private _count: number = 0; private _queue: Promise = Promise.resolve(); - private readonly _depends: Array; + private readonly _depends: Array; } diff --git a/src/TestAdapter.ts b/src/TestAdapter.ts index bd949cf7..0c43779b 100644 --- a/src/TestAdapter.ts +++ b/src/TestAdapter.ts @@ -11,7 +11,7 @@ import * as util from 'vscode-test-adapter-util'; import { RootTestSuiteInfo } from './RootTestSuiteInfo'; import { resolveVariables } from './Helpers'; -import { QueueGraphNode } from './QueueGraph'; +import { TaskQueue } from './TaskQueue'; import { TestExecutableInfo } from './TestExecutableInfo'; export class TestAdapter implements api.TestAdapter, vscode.Disposable { @@ -31,7 +31,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { // because we always want to return with the current allTests suite private readonly _loadFinishedEmitter = new vscode.EventEmitter(); - private _allTasks: QueueGraphNode; + private _allTasks: TaskQueue; private _allTests: RootTestSuiteInfo; private readonly _disposables: vscode.Disposable[] = []; @@ -45,9 +45,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { process.platform, process.version, process.versions, vscode.version ])); - this._allTasks = new QueueGraphNode('TestAdapter', [], (reason: any) => { - this._log.error('fatal error: QueueGraphNode(TestAdapter): ' + inspect(this)); - }); + this._allTasks = new TaskQueue([], 'TestAdapter'); this._disposables.push(this._testsEmitter); this._disposables.push(this._testStatesEmitter); @@ -152,7 +150,7 @@ export class TestAdapter implements api.TestAdapter, vscode.Disposable { const config = this._getConfiguration(); this._allTests.dispose(); - this._allTasks = new QueueGraphNode(); + this._allTasks = new TaskQueue([], 'TestAdapter'); this._allTests = new RootTestSuiteInfo( this._allTasks, this._log, this.workspaceFolder, diff --git a/src/test/QueueGraph.test.ts b/src/test/TaskQueue.test.ts similarity index 87% rename from src/test/QueueGraph.test.ts rename to src/test/TaskQueue.test.ts index 9b83b580..28409878 100644 --- a/src/test/QueueGraph.test.ts +++ b/src/test/TaskQueue.test.ts @@ -3,13 +3,13 @@ // public domain. The author hereby disclaims copyright to this source code. import * as assert from 'assert'; -import {promisify} from 'util'; +import { promisify } from 'util'; -import {QueueGraphNode} from '../QueueGraph'; +import { TaskQueue } from '../TaskQueue'; -describe('QueueGraphNode', function() { +describe('TaskQueue', function () { async function waitFor( - test: Mocha.Context, condition: Function, timeout: number = 1000) { + test: Mocha.Context, condition: Function, timeout: number = 1000) { const start = Date.now(); let c = await condition(); while (!c && (Date.now() - start < timeout || !test.enableTimeouts())) { @@ -19,7 +19,7 @@ describe('QueueGraphNode', function() { return c; } - it('promise practice 1', async function() { + it('promise practice 1', async function () { let resolve: Function; let second = false; new Promise(r => { @@ -36,7 +36,7 @@ describe('QueueGraphNode', function() { assert.ok(second); }); - it('promise practice 2', async function() { + it('promise practice 2', async function () { let resolve: Function; let second = false; const p = new Promise(r => { @@ -56,16 +56,16 @@ describe('QueueGraphNode', function() { assert.ok(second); }); - context('example 1', function() { + context('example 1', function () { /** * node1 <___ node * node2 <___/ */ - const node1 = new QueueGraphNode('node1'); - const node2 = new QueueGraphNode('node2'); - const nodeD = new QueueGraphNode('nodeD', [node1, node2]); + const node1 = new TaskQueue([], 'node1'); + const node2 = new TaskQueue([], 'node2'); + const nodeD = new TaskQueue([node1, node2], 'nodeD'); - it('add:depends before', async function() { + it('add:depends before', async function () { this.slow(150); let startD: Function; let hasRunDatOnce = false; From f49d10ea985d3876e951f91ffd19902a87ee94d3 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 5 Jan 2019 16:50:48 +0100 Subject: [PATCH 5/6] no more mindless delay --- README.md | 6 ++--- src/RootTestSuiteInfo.ts | 13 +++++++++- src/TestSuiteInfoBase.ts | 55 ++++++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 5707f56d..8a30bf06 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ and [Google Test](https://github.com/google/googletest) tests using the | `catch2TestExplorer.debugBreakOnFailure` | Debugger breaks on failure while debugging the test. Catch2: --break; Google Test: --gtest_break_on_failure; | | `catch2TestExplorer.defaultNoThrow` | Skips all assertions that test that an exception is thrown, e.g. REQUIRE_THROWS. This is a Catch2 parameter: --nothrow | | `catch2TestExplorer.defaultRngSeed` | Catch2: `--rng-seed or "time"`; Google Test: `--gtest_random_seed=`; | -| `catch2TestExplorer.defaultWatchTimeoutSec` | Test executables are being watched. In case of one compiles too much this variable can help with it. Unit: second. | -| `catch2TestExplorer.defaultRunningTimeoutSec` | Test executable is running in a process. In case of an inifinite loop, it will run forever, unless this parameter is set. | -| `catch2TestExplorer.workerMaxNumber` | The variable maximize the number of the parallel test execution. | +| `catch2TestExplorer.defaultWatchTimeoutSec` | Test executables are being watched. In case of one compiles too much this variable can help with it. Unit: second. It applies instantly. | +| `catch2TestExplorer.defaultRunningTimeoutSec` | Test executable is running in a process. In case of an inifinite loop, it will run forever, unless this parameter is set. It applies instantly. | +| `catch2TestExplorer.workerMaxNumber` | The variable maximize the number of the parallel test execution. It applies instantly. | | `testExplorer.errorDecoration` | Show error messages from test failures as decorations in the editor. [Details](https://github.com/hbenl/vscode-test-explorer#configuration) | | `testExplorer.gutterDecoration` | Show the state of each test in the editor using Gutter Decorations. [Details](https://github.com/hbenl/vscode-test-explorer#configuration) | | `testExplorer.codeLens` | Show a CodeLens above each test or suite for running or debugging the tests. [Details](https://github.com/hbenl/vscode-test-explorer#configuration) | diff --git a/src/RootTestSuiteInfo.ts b/src/RootTestSuiteInfo.ts index 1d0ab7c5..61683929 100644 --- a/src/RootTestSuiteInfo.ts +++ b/src/RootTestSuiteInfo.ts @@ -37,7 +37,7 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { public isEnabledSourceDecoration: boolean, public rngSeed: string | number | null, public execWatchTimeout: number, - public execRunningTimeout: null | number, + private _execRunningTimeout: null | number, public isNoThrow: boolean, workerMaxNumber: number, ) { @@ -50,8 +50,19 @@ export class RootTestSuiteInfo implements TestSuiteInfo, vscode.Disposable { this._taskPool.maxTaskCount = workerMaxNumber; } + get execRunningTimeout() { return this._execRunningTimeout; } + + set execRunningTimeout(value: null | number) { + this._execRunningTimeout = value; + this._execRunningTimeoutChangeEmitter.fire(); + } + + private readonly _execRunningTimeoutChangeEmitter = new vscode.EventEmitter(); + readonly onDidExecRunningTimeoutChanged = this._execRunningTimeoutChangeEmitter.event; + dispose() { this._isDisposed = true; + this._execRunningTimeoutChangeEmitter.dispose(); for (let i = 0; i < this._executables.length; i++) { this._executables[i].dispose(); } diff --git a/src/TestSuiteInfoBase.ts b/src/TestSuiteInfoBase.ts index 02d90074..d9e2655b 100644 --- a/src/TestSuiteInfoBase.ts +++ b/src/TestSuiteInfoBase.ts @@ -4,7 +4,8 @@ import { ChildProcess, spawn, SpawnOptions } from 'child_process'; import * as path from 'path'; -import { promisify, inspect } from 'util'; +import { inspect } from 'util'; +import * as vscode from 'vscode'; import { TestEvent, TestSuiteInfo } from 'vscode-test-adapter-api'; import { RootTestSuiteInfo } from './RootTestSuiteInfo'; @@ -118,24 +119,44 @@ export abstract class TestSuiteInfoBase implements TestSuiteInfo { const runInfo: TestSuiteInfoBaseRunInfo = { process: this._proc, childrenToRun: childrenToRun, - timeout: undefined + timeout: undefined, + timeoutWatcherTrigger: () => { }, }; this.allTests.log.info('proc started'); - - const startTime = Date.now(); - const killIfTimeouts = (): Promise => { - if (runInfo.process === undefined) { return Promise.resolve(); } - else if (this.allTests.execRunningTimeout !== null - && Date.now() - startTime > this.allTests.execRunningTimeout) { - runInfo.process.kill(); - runInfo.timeout = this.allTests.execRunningTimeout; - return Promise.resolve(); - } else { - return promisify(setTimeout)(1000).then(killIfTimeouts); - } - }; - promisify(setTimeout)(1000).then(killIfTimeouts); + { + const startTime = Date.now(); + + const killIfTimeouts = (): Promise => { + return new Promise(resolve => { + const conn = this.allTests.onDidExecRunningTimeoutChanged(() => { + resolve(conn); + }); + + runInfo.timeoutWatcherTrigger = () => { resolve(conn); }; + + if (this.allTests.execRunningTimeout !== null) { + const elapsed = Date.now() - startTime; + const left = this.allTests.execRunningTimeout - elapsed; + if (left <= 0) resolve(conn); + else setTimeout(resolve, left, conn); + } + }).then((conn: vscode.Disposable) => { + conn.dispose(); + if (runInfo.process === undefined) { + return Promise.resolve(); + } else if (this.allTests.execRunningTimeout !== null + && Date.now() - startTime > this.allTests.execRunningTimeout) { + runInfo.process.kill(); + runInfo.timeout = this.allTests.execRunningTimeout; + return Promise.resolve(); + } else { + return killIfTimeouts(); + } + }); + }; + killIfTimeouts(); + } return this._handleProcess(runInfo) .catch((reason: any) => { @@ -147,6 +168,7 @@ export abstract class TestSuiteInfoBase implements TestSuiteInfo { this._proc = undefined; runInfo.process = undefined; + runInfo.timeoutWatcherTrigger(); }); } @@ -227,4 +249,5 @@ export interface TestSuiteInfoBaseRunInfo { process: ChildProcess | undefined; childrenToRun: TestInfoBase[] | 'all'; timeout: number | undefined; + timeoutWatcherTrigger: () => void; } \ No newline at end of file From b1287549f4374b1a7cb33b941c3e9a9a18e6acf9 Mon Sep 17 00:00:00 2001 From: Mate Pek Date: Sat, 5 Jan 2019 17:36:26 +0100 Subject: [PATCH 6/6] no reload in case of timeout --- CHANGELOG.md | 4 ++++ package-lock.json | 2 +- package.json | 2 +- src/GoogleTestSuiteInfo.ts | 10 +++++----- src/TaskQueue.ts | 11 +++-------- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c3a42a..a3725422 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] +## [2.3.10] + +Performance and stability impovements. + ## [2.3.9] - 2019-01-03 ### Fixed diff --git a/package-lock.json b/package-lock.json index 81d21f15..2bbb3f5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vscode-catch2-test-adapter", - "version": "2.3.9", + "version": "2.3.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0c983a63..618bd8b0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "icon": "resources/icon.png", "author": "Mate Pek", "publisher": "matepek", - "version": "2.3.9", + "version": "2.3.10", "license": "Unlicense", "homepage": "https://github.com/matepek/vscode-catch2-test-adapter", "repository": { diff --git a/src/GoogleTestSuiteInfo.ts b/src/GoogleTestSuiteInfo.ts index d34f41c0..d67c6e41 100644 --- a/src/GoogleTestSuiteInfo.ts +++ b/src/GoogleTestSuiteInfo.ts @@ -266,7 +266,7 @@ export class GoogleTestSuiteInfo extends TestSuiteInfoBase { test: data.currentChild!, state: 'failed', }; - if (runInfo.timeout !== undefined) { + if (runInfo.timeout != undefined) { ev.message = this._getTimeoutMessage(runInfo.timeout); } else { ev.message = 'Fatal error: (Wrong Google Test output.)\nError: ' + inspect(codeOrReason) + '\n'; @@ -277,10 +277,10 @@ export class GoogleTestSuiteInfo extends TestSuiteInfoBase { } } - const isTestRemoved = (runInfo.childrenToRun === 'all' && - this.children.filter(c => !c.skipped).length > - data.processedTestCases.length) || - (runInfo.childrenToRun !== 'all' && data.processedTestCases.length == 0); + const isTestRemoved = (runInfo.timeout == undefined) + && ((runInfo.childrenToRun === 'all' + && this.children.filter(c => !c.skipped).length > data.processedTestCases.length) + || (runInfo.childrenToRun !== 'all' && data.processedTestCases.length == 0)); if (data.unprocessedTestCases.length > 0 || isTestRemoved) { this.allTests diff --git a/src/TaskQueue.ts b/src/TaskQueue.ts index f0b0bf83..4004fcc4 100644 --- a/src/TaskQueue.ts +++ b/src/TaskQueue.ts @@ -32,14 +32,9 @@ export class TaskQueue { return previous.then(task); }); - this._queue = current - .then( - (value: TResult1) => { - this._count--; - }, - (reason: any) => { - this._count--; - }); + const decr = () => { this._count--; }; + + this._queue = current.then(decr, decr); return current; }