Skip to content

Commit

Permalink
improve
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxingbaoyu committed Oct 10, 2022
1 parent 7b49471 commit d1ab5c3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 41 deletions.
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/detectOpenHandles.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This usually means that there are asynchronous operations that weren't stopped i
exports[`prints out info about open handlers 1`] = `
"Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● TCPSERVERWRAP
DNSCHANNEL,TCPSERVERWRAP
12 | const app = new Server();
13 |
Expand Down
5 changes: 4 additions & 1 deletion packages/jest-core/src/__tests__/collectHandles.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ describe('collectHandles', () => {
it('should not collect the DNSCHANNEL open handle', async () => {
const handleCollector = collectHandles();

const resolver = new dns.Resolver();
let resolver = new dns.Resolver();
resolver.getServers();

// We must drop references to it
resolver = null;

const openHandles = await handleCollector();

expect(openHandles).not.toContainEqual(
Expand Down
84 changes: 45 additions & 39 deletions packages/jest-core/src/collectHandles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,7 @@ export default function collectHandles(): HandleCollectionResult {
// Skip resources that should not generally prevent the process from
// exiting, not last a meaningfully long time, or otherwise shouldn't be
// tracked.
if (
type === 'PROMISE' ||
type === 'TIMERWRAP' ||
type === 'ELDHISTOGRAM' ||
type === 'PerformanceObserver' ||
type === 'RANDOMBYTESREQUEST' ||
type === 'DNSCHANNEL' ||
type === 'ZLIB' ||
type === 'SIGNREQUEST'
) {
if (type === 'PROMISE') {
return;
}
const error = new ErrorWithStack(type, initHook, 100);
Expand Down Expand Up @@ -141,14 +132,18 @@ export default function collectHandles(): HandleCollectionResult {
// For example, Node.js TCP Servers are not destroyed until *after* their
// `close` callback runs. If someone finishes a test from the `close`
// callback, we will not yet have seen the resource be destroyed here.
await asyncSleep(100);
await asyncSleep(0);

if (activeHandles.size > 0) {
// For some special objects such as `TLSWRAP`.
// Ref: https://github.com/facebook/jest/issues/11665
runGC();
await asyncSleep(30);

await asyncSleep(0);
if (activeHandles.size > 0) {
// For some special objects such as `TLSWRAP`.
// Ref: https://github.com/facebook/jest/issues/11665
runGC();

await asyncSleep(0);
}
}

hook.disable();
Expand All @@ -167,33 +162,44 @@ export function formatHandleErrors(
errors: Array<Error>,
config: Config.ProjectConfig,
): Array<string> {
const stacks = new Set();

return (
errors
.map(err =>
formatExecError(err, config, {noStackTrace: false}, undefined, true),
)
// E.g. timeouts might give multiple traces to the same line of code
// This hairy filtering tries to remove entries with duplicate stack traces
.filter(handle => {
const ansiFree: string = stripAnsi(handle);

const match = ansiFree.match(/\s+at(.*)/);

if (!match || match.length < 2) {
return true;
}
const stacks = new Map<string, {stack: string; names: Set<string>}>();

errors.forEach(err => {
const formatted = formatExecError(
err,
config,
{noStackTrace: false},
undefined,
true,
);

const stack = ansiFree.substr(ansiFree.indexOf(match[1])).trim();
// E.g. timeouts might give multiple traces to the same line of code
// This hairy filtering tries to remove entries with duplicate stack traces

if (stacks.has(stack)) {
return false;
}
const ansiFree: string = stripAnsi(formatted);
const match = ansiFree.match(/\s+at(.*)/);
if (!match || match.length < 2) {
return;
}

stacks.add(stack);
const stackText = ansiFree.substr(ansiFree.indexOf(match[1])).trim();

const name = ansiFree.match(/(?<=● {2}).*$/m);
if (!name?.length) {
return;
}

const stack = stacks.get(stackText) || {
names: new Set(),
stack: formatted.replace(name[0], '%%OBJECT_NAME%%'),
};

stack.names.add(name[0]);

stacks.set(stackText, stack);
});

return true;
})
return Array.from(stacks.values()).map(({stack, names}) =>
stack.replace('%%OBJECT_NAME%%', Array.from(names).join(',')),
);
}

0 comments on commit d1ab5c3

Please sign in to comment.