Skip to content

Commit

Permalink
Memory leak fix: release source map info after processed and minor op…
Browse files Browse the repository at this point in the history
…timizations (#8234)

* Fix coverage memory leak and minor optimizations.

* Restore accidental deletion.

* Update CHANGELOG.md

* Review feedback.

* Move memory release to core.
  • Loading branch information
scotthovestadt authored Mar 29, 2019
1 parent 84466b7 commit d5d2f93
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
- `[jest-core]` Improve performance of SearchSource.findMatchingTests by 15% ([#8184](https://github.com/facebook/jest/pull/8184))
- `[jest-resolve]` Optimize internal cache lookup performance ([#8183](https://github.com/facebook/jest/pull/8183))
- `[jest-core]` Dramatically improve watch mode performance ([#8201](https://github.com/facebook/jest/pull/8201))
- `[jest-core]` Fix memory leak of source map info and minor performance improvements ([#8234](https://github.com/facebook/jest/pull/8234))
- `[jest-console]` Fix memory leak by releasing console output reference when printed to stdout ([#8233](https://github.com/facebook/jest/pull/8233))
- `[jest-runtime]` Use `Map` instead of `Object` for module registry ([#8232](https://github.com/facebook/jest/pull/8232))

Expand Down
2 changes: 2 additions & 0 deletions packages/jest-core/src/ReporterDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export default class ReporterDispatcher {
}

// Release memory if unused later.
testResult.sourceMaps = undefined;
testResult.coverage = undefined;
testResult.console = undefined;
}

Expand Down
11 changes: 6 additions & 5 deletions packages/jest-reporters/src/coverage_reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,24 @@ export default class CoverageReporter extends BaseReporter {
) {
if (testResult.coverage) {
this._coverageMap.merge(testResult.coverage);
// Remove coverage data to free up some memory.
delete testResult.coverage;
}

Object.keys(testResult.sourceMaps).forEach(sourcePath => {
const sourceMaps = testResult.sourceMaps;
if (sourceMaps) {
Object.keys(sourceMaps).forEach(sourcePath => {
let inputSourceMap: RawSourceMap | undefined;
try {
const coverage: FileCoverage = this._coverageMap.fileCoverageFor(
sourcePath,
);
({inputSourceMap} = coverage.toJSON() as any);
inputSourceMap = (coverage.toJSON() as any).inputSourceMap;
} finally {
if (inputSourceMap) {
this._sourceMapStore.registerMap(sourcePath, inputSourceMap);
} else {
this._sourceMapStore.registerURL(
sourcePath,
testResult.sourceMaps[sourcePath],
sourceMaps[sourcePath],
);
}
}
Expand Down
13 changes: 9 additions & 4 deletions packages/jest-runner/src/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,19 @@ async function runTestInternal(

result.perfStats = {end: Date.now(), start};
result.testFilePath = path;
result.coverage = runtime.getAllCoverageInfoCopy();
result.sourceMaps = runtime.getSourceMapInfo(
new Set(Object.keys(result.coverage || {})),
);
result.console = testConsole.getBuffer();
result.skipped = testCount === result.numPendingTests;
result.displayName = config.displayName;

const coverage = runtime.getAllCoverageInfoCopy();
if (coverage) {
const coverageKeys = Object.keys(coverage);
if (coverageKeys.length) {
result.coverage = coverage;
result.sourceMaps = runtime.getSourceMapInfo(new Set(coverageKeys));
}
}

if (globalConfig.logHeapUsage) {
if (global.gc) {
global.gc();
Expand Down
64 changes: 31 additions & 33 deletions packages/jest-test-result/src/formatTestResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,41 @@ import {
TestResult,
} from './types';

const formatResult = (
const formatTestResult = (
testResult: TestResult,
codeCoverageFormatter: CodeCoverageFormatter,
reporter: CodeCoverageReporter,
codeCoverageFormatter?: CodeCoverageFormatter,
reporter?: CodeCoverageReporter,
): FormattedTestResult => {
const now = Date.now();
const output: FormattedTestResult = {
assertionResults: [],
coverage: {},
endTime: now,
message: '',
name: testResult.testFilePath,
startTime: now,
status: 'failed',
summary: '',
};

const assertionResults = testResult.testResults.map(formatTestAssertion);
if (testResult.testExecError) {
output.message = testResult.testExecError.message;
output.coverage = {};
const now = Date.now();
return {
assertionResults,
coverage: {},
endTime: now,
message: testResult.failureMessage
? testResult.failureMessage
: testResult.testExecError.message,
name: testResult.testFilePath,
startTime: now,
status: 'failed',
summary: '',
};
} else {
const allTestsPassed = testResult.numFailingTests === 0;
output.status = allTestsPassed ? 'passed' : 'failed';
output.startTime = testResult.perfStats.start;
output.endTime = testResult.perfStats.end;
output.coverage = codeCoverageFormatter(testResult.coverage, reporter);
}

output.assertionResults = testResult.testResults.map(formatTestAssertion);

if (testResult.failureMessage) {
output.message = testResult.failureMessage;
return {
assertionResults,
coverage: codeCoverageFormatter
? codeCoverageFormatter(testResult.coverage, reporter)
: testResult.coverage,
endTime: testResult.perfStats.end,
message: testResult.failureMessage || '',
name: testResult.testFilePath,
startTime: testResult.perfStats.start,
status: allTestsPassed ? 'passed' : 'failed',
summary: '',
};
}

return output;
};

function formatTestAssertion(
Expand All @@ -72,13 +72,11 @@ function formatTestAssertion(

export default function formatTestResults(
results: AggregatedResult,
codeCoverageFormatter?: CodeCoverageFormatter | null,
codeCoverageFormatter?: CodeCoverageFormatter,
reporter?: CodeCoverageReporter,
): FormattedTestResults {
const formatter = codeCoverageFormatter || (coverage => coverage);

const testResults = results.testResults.map(testResult =>
formatResult(testResult, formatter, reporter),
formatTestResult(testResult, codeCoverageFormatter, reporter),
);

return {...results, testResults};
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-test-result/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export type TestResult = {
unmatched: number;
updated: number;
};
sourceMaps: {
sourceMaps?: {
[sourcePath: string]: string;
};
testExecError?: SerializableError;
Expand Down

0 comments on commit d5d2f93

Please sign in to comment.