Skip to content

Commit 3d2eb37

Browse files
mjesuncpojer
authored andcommitted
Detect memory leaks (#4895)
* Make use of jest-leak-detector to detect tests leaking memory * Addressed feedback
1 parent e5f58a6 commit 3d2eb37

14 files changed

Lines changed: 104 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151

5252
### Features
5353

54+
* `[jest-runner]` Enable experimental detection of leaked contexts
55+
([#4895](https://github.com/facebook/jest/pull/4895))
5456
* `[jest-cli]` Add combined coverage threshold for directories.
5557
([#4885](https://github.com/facebook/jest/pull/4885))
5658
* `[jest-mock]` Add `timestamps` to mock state.

integration_tests/__tests__/__snapshots__/show_config.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
1212
\\"coveragePathIgnorePatterns\\": [
1313
\\"/node_modules/\\"
1414
],
15+
\\"detectLeaks\\": false,
1516
\\"globals\\": {},
1617
\\"haste\\": {
1718
\\"providesModuleNodeModules\\": []
@@ -66,6 +67,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
6667
\\"lcov\\",
6768
\\"clover\\"
6869
],
70+
\\"detectLeaks\\": false,
6971
\\"expand\\": false,
7072
\\"listTests\\": false,
7173
\\"mapCoverage\\": false,

packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export const runAndTransformResultsToJestFormat = async ({
129129
console: null,
130130
displayName: config.displayName,
131131
failureMessage,
132+
leaks: false, // That's legacy code, just adding it so Flow is happy.
132133
numFailingTests,
133134
numPassingTests,
134135
numPendingTests,

packages/jest-cli/src/cli/args.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ export const options = {
202202
description: 'Print debugging info about your jest config.',
203203
type: 'boolean',
204204
},
205+
detectLeaks: {
206+
default: false,
207+
description:
208+
'**EXPERIMENTAL**: Detect memory leaks in tests. After executing a ' +
209+
'test, it will try to garbage collect the global object used, and fail ' +
210+
'if it was leaked',
211+
type: 'boolean',
212+
},
205213
env: {
206214
description:
207215
'The test environment used for all tests. This can point to ' +

packages/jest-cli/src/test_result_helpers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const buildFailureTestResult = (
5454
console: null,
5555
displayName: '',
5656
failureMessage: null,
57+
leaks: false,
5758
numFailingTests: 0,
5859
numPassingTests: 0,
5960
numPendingTests: 0,

packages/jest-cli/src/test_scheduler.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {GlobalConfig, ReporterConfig} from 'types/Config';
1212
import type {Context} from 'types/Context';
1313
import type {Reporter, Test} from 'types/TestRunner';
1414

15+
import chalk from 'chalk';
1516
import {formatExecError} from 'jest-message-util';
1617
import {
1718
addResult,
@@ -88,14 +89,33 @@ export default class TestScheduler {
8889
if (watcher.isInterrupted()) {
8990
return Promise.resolve();
9091
}
92+
9193
if (testResult.testResults.length === 0) {
9294
const message = 'Your test suite must contain at least one test.';
93-
await onFailure(test, {
95+
96+
return onFailure(test, {
9497
message,
9598
stack: new Error(message).stack,
9699
});
97-
return Promise.resolve();
98100
}
101+
102+
// Throws when the context is leaked after executinga test.
103+
if (testResult.leaks) {
104+
const message =
105+
chalk.red.bold('EXPERIMENTAL FEATURE!\n') +
106+
'Your test suite is leaking memory. Please ensure all references are cleaned.\n' +
107+
'\n' +
108+
'There is a number of things that can leak memory:\n' +
109+
' - Async operations that have not finished (e.g. fs.readFile).\n' +
110+
' - Timers not properly mocked (e.g. setInterval, setTimeout).\n' +
111+
' - Keeping references to the global scope.';
112+
113+
return onFailure(test, {
114+
message,
115+
stack: new Error(message).stack,
116+
});
117+
}
118+
99119
addResult(aggregatedResults, testResult);
100120
await this._dispatcher.onTestResult(test, testResult, aggregatedResults);
101121
return this._bailIfNeeded(contexts, aggregatedResults, watcher);

packages/jest-config/src/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export default ({
3636
clearMocks: false,
3737
coveragePathIgnorePatterns: [NODE_MODULES_REGEXP],
3838
coverageReporters: ['json', 'text', 'lcov', 'clover'],
39+
detectLeaks: false,
3940
expand: false,
4041
globals: {},
4142
haste: {

packages/jest-config/src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const getConfigs = (
8181
coverageDirectory: options.coverageDirectory,
8282
coverageReporters: options.coverageReporters,
8383
coverageThreshold: options.coverageThreshold,
84+
detectLeaks: options.detectLeaks,
8485
expand: options.expand,
8586
findRelatedTests: options.findRelatedTests,
8687
forceExit: options.forceExit,
@@ -123,6 +124,7 @@ const getConfigs = (
123124
clearMocks: options.clearMocks,
124125
coveragePathIgnorePatterns: options.coveragePathIgnorePatterns,
125126
cwd: options.cwd,
127+
detectLeaks: options.detectLeaks,
126128
displayName: options.displayName,
127129
globals: options.globals,
128130
haste: options.haste,

packages/jest-config/src/normalize.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
455455
case 'collectCoverage':
456456
case 'coverageReporters':
457457
case 'coverageThreshold':
458+
case 'detectLeaks':
458459
case 'displayName':
459460
case 'expand':
460461
case 'globals':

packages/jest-runner/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"jest-docblock": "^21.2.0",
1313
"jest-haste-map": "^21.2.0",
1414
"jest-jasmine2": "^21.2.1",
15+
"jest-leak-detector": "^21.2.1",
1516
"jest-message-util": "^21.2.1",
1617
"jest-runtime": "^21.2.1",
1718
"jest-util": "^21.2.1",

0 commit comments

Comments
 (0)