From 73fc0a897904a6b19e45abb45ce0a14a79f7ddd6 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Fri, 26 Apr 2019 18:04:19 +0200 Subject: [PATCH] fix(clean up): prevent sandbox creation after dispose (#1527) --- packages/core/src/SandboxPool.ts | 16 +++++++++--- packages/core/test/unit/SandboxPool.spec.ts | 28 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/core/src/SandboxPool.ts b/packages/core/src/SandboxPool.ts index fa1877e8cd..498cdef801 100644 --- a/packages/core/src/SandboxPool.ts +++ b/packages/core/src/SandboxPool.ts @@ -1,6 +1,6 @@ import * as os from 'os'; import { range, Subject, Observable } from 'rxjs'; -import { flatMap, tap, zip, merge, map } from 'rxjs/operators'; +import { flatMap, tap, zip, merge, map, filter } from 'rxjs/operators'; import { File, StrykerOptions } from '@stryker-mutator/api/core'; import { TestFramework } from '@stryker-mutator/api/test_framework'; import Sandbox from './Sandbox'; @@ -60,9 +60,15 @@ export class SandboxPool implements Disposable { const concurrency = this.determineConcurrency(); return range(0, concurrency).pipe( - flatMap(n => { - return this.registerSandbox(Sandbox.create(this.options, n, this.initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext)); - }, MAX_CONCURRENT_INITIALIZING_SANDBOXES) + flatMap(async n => { + if (this.isDisposed) { + return null; + } else { + return this.registerSandbox(Sandbox.create(this.options, n, this.initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext)); + } + }, MAX_CONCURRENT_INITIALIZING_SANDBOXES), + filter(sandboxOrNull => !!sandboxOrNull), + map(sandbox => sandbox as Sandbox) ); } @@ -89,7 +95,9 @@ export class SandboxPool implements Disposable { return promisedSandbox; } + private isDisposed = false; public async dispose() { + this.isDisposed = true; const sandboxes = await Promise.all(this.allSandboxes); return Promise.all(sandboxes.map(sandbox => sandbox.dispose())); } diff --git a/packages/core/test/unit/SandboxPool.spec.ts b/packages/core/test/unit/SandboxPool.spec.ts index ca75355da4..65c30b84f7 100644 --- a/packages/core/test/unit/SandboxPool.spec.ts +++ b/packages/core/test/unit/SandboxPool.spec.ts @@ -178,6 +178,7 @@ describe(SandboxPool.name, () => { await expect(actRunMutants()).rejectedWith(expectedError); }); }); + describe('dispose', () => { it('should have disposed all sandboxes', async () => { sut = createSut(); @@ -216,5 +217,32 @@ describe(SandboxPool.name, () => { await disposePromise; expect(secondSandbox.dispose).called; }); + + it('should halt creating of new sandboxes', async () => { + // Arrange + sut = createSut(); + sinon.stub(os, 'cpus').returns([1, 2, 3]); // stub 3 cpus + const task = new Task(); + const task2 = new Task(); + createStub.reset(); + createStub + .onCall(0).returns(task.promise) + .onCall(1).returns(task2.promise) + .onCall(2).resolves(genericSandboxForAllSubsequentCallsToNewSandbox); // promise is not yet resolved + inputMutants.push(transpiledMutant(), transpiledMutant()); // 3 mutants + + // Act + const runPromise = sut.runMutants(from(inputMutants)) + .pipe(toArray()) + .toPromise(); + const disposePromise = sut.dispose(); + task.resolve(firstSandbox as unknown as Sandbox); + task2.resolve(secondSandbox as unknown as Sandbox); + await disposePromise; + await runPromise; + + // Assert + expect(createStub).calledTwice; + }); }); });