Skip to content

Commit 31ee085

Browse files
authored
fix(dispose): clean up child processes in alternative flows (#1520)
Make sure Stryker doesn't _hang indefinitely_ when it supposed to _exit prematurely_. Before, there was a code path that didn't dispose all child processes. Now using typed-injects [dispose functionality](https://github.com/nicojs/typed-inject/#-disposing-provided-stuff).
1 parent 3c83bbe commit 31ee085

File tree

14 files changed

+113
-35
lines changed

14 files changed

+113
-35
lines changed

e2e/test/exit-prematurely/.babelrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"presets": [
3+
"@babel/env"
4+
]
5+
}

e2e/test/exit-prematurely/package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "exit-prematurely",
3+
"version": "1.0.0",
4+
"private": true,
5+
"description": "A module to test the alternative flow when Stryker should exit prematurely, see https://github.com/stryker-mutator/stryker/issues/1519",
6+
"main": "index.js",
7+
"scripts": {
8+
"pretest": "rimraf \"reports\" stryker.log .stryker-tmp ",
9+
"test": "stryker run",
10+
"posttest": "mocha --require ts-node/register verify/*.ts"
11+
},
12+
"keywords": [],
13+
"author": "",
14+
"license": "ISC"
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = function (config) {
2+
config.set({
3+
mutate: [
4+
'src/*.js'
5+
],
6+
testFramework: 'mocha',
7+
testRunner: 'mocha',
8+
coverageAnalysis: 'off',
9+
mutator: 'javascript',
10+
transpilers: [
11+
'babel'
12+
],
13+
timeoutMS: 60000,
14+
reporters: ['clear-text', 'html', 'event-recorder'],
15+
maxConcurrentTestRunners: 2,
16+
logLevel: 'info',
17+
fileLogLevel: 'info'
18+
});
19+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Idle
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"declaration": false
5+
},
6+
"files": [
7+
"verify/verify.ts"
8+
]
9+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as fs from 'fs';
2+
import { expect } from 'chai';
3+
4+
describe('Verify stryker has ran correctly', () => {
5+
6+
const strykerLog = fs.readFileSync('./stryker.log', 'utf8');
7+
8+
it('exit prematurely', async () => {
9+
expect(strykerLog).contains('No tests were executed. Stryker will exit prematurely.');
10+
});
11+
12+
it('should log about a mutant free world', async () => {
13+
expect(strykerLog).contains('It\'s a mutant-free world, nothing to test');
14+
});
15+
16+
it('should warn about the globbing expression resulting in no files', () => {
17+
expect(strykerLog).contains('Globbing expression "src/*.js" did not result in any files.');
18+
});
19+
});

packages/core/src/SandboxPool.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import { InitialTestRunResult } from './process/InitialTestExecutor';
1111
import { Logger } from '@stryker-mutator/api/logging';
1212
import TranspiledMutant from './TranspiledMutant';
1313
import { MutantResult } from '@stryker-mutator/api/report';
14+
import { Disposable } from 'typed-inject';
1415

1516
const MAX_CONCURRENT_INITIALIZING_SANDBOXES = 2;
1617

17-
export class SandboxPool {
18+
export class SandboxPool implements Disposable {
1819

1920
private readonly allSandboxes: Promise<Sandbox>[] = [];
2021
private readonly overheadTimeMS: number;
@@ -84,11 +85,11 @@ export class SandboxPool {
8485
}
8586

8687
private readonly registerSandbox = async (promisedSandbox: Promise<Sandbox>): Promise<Sandbox> => {
87-
this.allSandboxes.push(promisedSandbox);
88-
return promisedSandbox;
88+
this.allSandboxes.push(promisedSandbox);
89+
return promisedSandbox;
8990
}
9091

91-
public async disposeAll() {
92+
public async dispose() {
9293
const sandboxes = await Promise.all(this.allSandboxes);
9394
return Promise.all(sandboxes.map(sandbox => sandbox.dispose()));
9495
}

packages/core/src/Stryker.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,23 @@ export default class Stryker {
7979
const testableMutants = await mutationTestProcessInjector
8080
.injectClass(MutantTestMatcher)
8181
.matchWithMutants(mutator.mutate(inputFiles.filesToMutate));
82-
if (initialRunResult.runResult.tests.length && testableMutants.length) {
83-
const mutationTestExecutor = mutationTestProcessInjector.injectClass(MutationTestExecutor);
84-
const mutantResults = await mutationTestExecutor.run(testableMutants);
85-
await this.reportScore(mutantResults, inputFileInjector);
86-
await TempFolder.instance().clean();
87-
await this.logDone();
82+
try {
83+
if (initialRunResult.runResult.tests.length && testableMutants.length) {
84+
const mutationTestExecutor = mutationTestProcessInjector
85+
.injectClass(MutationTestExecutor);
86+
const mutantResults = await mutationTestExecutor.run(testableMutants);
87+
await this.reportScore(mutantResults, inputFileInjector);
88+
await TempFolder.instance().clean();
89+
await this.logDone();
90+
return mutantResults;
91+
} else {
92+
this.logRemark();
93+
}
94+
} finally {
95+
// `injector.dispose` calls `dispose` on all created instances
96+
// Namely the `SandboxPool` and the `ChildProcessProxy` instances
97+
mutationTestProcessInjector.dispose();
8898
await LogConfigurator.shutdown();
89-
return mutantResults;
90-
} else {
91-
this.logRemark();
9299
}
93100
}
94101
return Promise.resolve([]);

packages/core/src/process/MutationTestExecutor.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ export class MutationTestExecutor {
2828
tap(this.reportAll)
2929
).toPromise();
3030

31-
// TODO: Let typed inject dispose of sandbox pool
32-
await this.sandboxPool.disposeAll();
33-
await this.mutantTranspileScheduler.dispose();
3431
return results;
3532
}
3633

0 commit comments

Comments
 (0)