Skip to content

Commit

Permalink
fix: use WeakRef for tracked handles
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Apr 8, 2021
1 parent 4926564 commit 0e47000
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -48,6 +48,7 @@
- `[jest-config]` [**BREAKING**] Change default file extension order by moving json behind ts and tsx ([10572](https://github.com/facebook/jest/pull/10572))
- `[jest-console]` `console.dir` now respects the second argument correctly ([#10638](https://github.com/facebook/jest/pull/10638))
- `[jest-core]` Don't report PerformanceObserver as open handle ([#11123](https://github.com/facebook/jest/pull/11123))
- `[jest-core]` Use `WeakRef` to hold timers when detecting open handles ([#11277](https://github.com/facebook/jest/pull/11277))
- `[jest-each]` [**BREAKING**] Ignore excess words in headings ([#8766](https://github.com/facebook/jest/pull/8766))
- `[jest-environment]` [**BREAKING**] Drop support for `runScript` for test environments ([#11155](https://github.com/facebook/jest/pull/11155))
- `[jest-environment-jsdom]` Use inner realm’s `ArrayBuffer` constructor ([#10885](https://github.com/facebook/jest/pull/10885))
Expand Down
6 changes: 4 additions & 2 deletions packages/jest-core/src/cli/index.ts
Expand Up @@ -32,7 +32,7 @@ import watch from '../watch';

const {print: preRunMessagePrint} = preRunMessage;

type OnCompleteCallback = (results: AggregatedResult) => void;
type OnCompleteCallback = (results: AggregatedResult) => void | undefined;

export async function runCLI(
argv: Config.Argv,
Expand Down Expand Up @@ -89,7 +89,9 @@ export async function runCLI(
configsOfProjectsToRun,
hasDeprecationWarnings,
outputStream,
r => (results = r),
r => {
results = r;
},
);

if (argv.watch || argv.watchAll) {
Expand Down
26 changes: 19 additions & 7 deletions packages/jest-core/src/collectHandles.ts
Expand Up @@ -13,6 +13,8 @@ import type {Config} from '@jest/types';
import {formatExecError} from 'jest-message-util';
import {ErrorWithStack} from 'jest-util';

export type HandleCollectionResult = () => Array<Error>;

function stackIsFromUser(stack: string) {
// Either the test file, or something required by it
if (stack.includes('Runtime.requireModule')) {
Expand All @@ -37,13 +39,15 @@ function stackIsFromUser(stack: string) {

const alwaysActive = () => true;

const hasWeakRef = typeof WeakRef === 'function';

// Inspired by https://github.com/mafintosh/why-is-node-running/blob/master/index.js
// Extracted as we want to format the result ourselves
export default function collectHandles(): () => Array<Error> {
const activeHandles: Map<
export default function collectHandles(): HandleCollectionResult {
const activeHandles = new Map<
number,
{error: Error; isActive: () => boolean}
> = new Map();
>();
const hook = asyncHooks.createHook({
destroy(asyncId) {
activeHandles.delete(asyncId);
Expand All @@ -68,10 +72,18 @@ export default function collectHandles(): () => Array<Error> {
let isActive: () => boolean;

if (type === 'Timeout' || type === 'Immediate') {
// Timer that supports hasRef (Node v11+)
if ('hasRef' in resource) {
// Timer that supports hasRef (Node v11+)
// @ts-expect-error: doesn't exist in v10 typings
isActive = resource.hasRef.bind(resource);
if (hasWeakRef) {
const ref = new WeakRef(resource);
isActive = () => {
// @ts-expect-error: doesn't exist in v10 typings
return ref.deref()?.hasRef() ?? false;
};
} else {
// @ts-expect-error: doesn't exist in v10 typings
isActive = resource.hasRef.bind(resource);
}
} else {
// Timer that doesn't support hasRef
isActive = alwaysActive;
Expand All @@ -88,7 +100,7 @@ export default function collectHandles(): () => Array<Error> {

hook.enable();

return (): Array<Error> => {
return () => {
hook.disable();

// Get errors for every async resource still referenced at this moment
Expand Down
8 changes: 4 additions & 4 deletions packages/jest-core/src/runJest.ts
Expand Up @@ -26,7 +26,7 @@ import type FailedTestsCache from './FailedTestsCache';
import SearchSource from './SearchSource';
import TestScheduler, {TestSchedulerContext} from './TestScheduler';
import type TestWatcher from './TestWatcher';
import collectNodeHandles from './collectHandles';
import collectNodeHandles, {HandleCollectionResult} from './collectHandles';
import getNoTestsFoundMessage from './getNoTestsFoundMessage';
import runGlobalHook from './runGlobalHook';
import type {Filter, TestRunData} from './types';
Expand Down Expand Up @@ -70,7 +70,7 @@ type ProcessResultOptions = Pick<
Config.GlobalConfig,
'json' | 'outputFile' | 'testResultsProcessor'
> & {
collectHandles?: () => Array<Error>;
collectHandles?: HandleCollectionResult;
onComplete?: (result: AggregatedResult) => void;
outputStream: NodeJS.WriteStream;
};
Expand Down Expand Up @@ -111,7 +111,7 @@ const processResults = (
}
}

return onComplete && onComplete(runResults);
onComplete?.(runResults);
};

const testSchedulerContext: TestSchedulerContext = {
Expand Down Expand Up @@ -278,7 +278,7 @@ export default async function runJest({
await runGlobalHook({allTests, globalConfig, moduleName: 'globalTeardown'});
}

await processResults(results, {
processResults(results, {
collectHandles,
json: globalConfig.json,
onComplete,
Expand Down

0 comments on commit 0e47000

Please sign in to comment.