Skip to content

Commit

Permalink
Use weak-napi instead of weak package and update `jest-leak-detec…
Browse files Browse the repository at this point in the history
…tor` package, readme and test cases to be compatible with the new Node version
  • Loading branch information
lh0x00 committed Jul 13, 2019
1 parent b76f01b commit 7b1d1c9
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 51 deletions.
21 changes: 13 additions & 8 deletions packages/jest-leak-detector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ Internally creates a weak reference to the object, and forces garbage collection
## Example

```javascript
let reference = {};
(async function() {
let reference = {};
let isLeaking;

const detector = new LeakDetector(reference);
const detector = new LeakDetector(reference);

// Reference is held in memory.
console.log(detector.isLeaking()); // true
// Reference is held in memory.
isLeaking = await detector.isLeaking();
console.log(isLeaking); // true

// We destroy the only reference to the object.
reference = null;
// We destroy the only reference to the object.
reference = null;

// Reference is gone.
console.log(detector.isLeaking()); // false
// Reference is gone.
isLeaking = await detector.isLeaking();
console.log(isLeaking); // false
})();
```
3 changes: 1 addition & 2 deletions packages/jest-leak-detector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
"pretty-format": "^24.8.0"
},
"devDependencies": {
"@types/weak": "^1.0.0",
"weak": "^1.0.1"
"weak-napi": "^1.0.3"
},
"engines": {
"node": ">= 6"
Expand Down
56 changes: 38 additions & 18 deletions packages/jest-leak-detector/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,42 @@ it('complains if the value is a primitive', () => {
expect(() => new LeakDetector(NaN)).toThrowErrorMatchingSnapshot();
});

it('does not show the GC if hidden', () => {
it('does not show the GC if hidden', async () => {
const detector = new LeakDetector({});

// @ts-ignore: purposefully removed
global.gc = undefined;
detector.isLeaking();
await detector.isLeaking();
expect(global.gc).not.toBeDefined();
});

it('does not hide the GC if visible', () => {
it('does not hide the GC if visible', async () => {
const detector = new LeakDetector({});

global.gc = () => {};
detector.isLeaking();
await detector.isLeaking();
expect(global.gc).toBeDefined();
});

it('correctly checks simple leaks', () => {
it('correctly checks simple leaks', async () => {
let reference: unknown = {};
let isLeaking: boolean;

const detector = new LeakDetector(reference);

// Reference is still held in memory.
expect(detector.isLeaking()).toBe(true);
isLeaking = await detector.isLeaking();
expect(isLeaking).toBe(true);

// We destroy the only reference to the object we had.
reference = null;

// Reference should be gone.
expect(detector.isLeaking()).toBe(false);
isLeaking = await detector.isLeaking();
expect(isLeaking).toBe(false);
});

it('tests different objects', () => {
it('tests different objects', async () => {
const refs = [
function() {},
() => {},
Expand All @@ -68,12 +71,20 @@ it('tests different objects', () => {

const detectors = refs.map(ref => new LeakDetector(ref));

detectors.forEach(detector => expect(detector.isLeaking()).toBe(true));
refs.forEach((_, i) => (refs[i] = null));
detectors.forEach(detector => expect(detector.isLeaking()).toBe(false));
let isLeaking: boolean;
for (const i in detectors) {
isLeaking = await detectors[i].isLeaking();
expect(isLeaking).toBe(true);
refs[i] = null;
}

for (const i in detectors) {
isLeaking = await detectors[i].isLeaking();
expect(isLeaking).toBe(false);
}
});

it('correctly checks more complex leaks', () => {
it('correctly checks more complex leaks', async () => {
let ref1: any = {};
let ref2: any = {};

Expand All @@ -84,21 +95,30 @@ it('correctly checks more complex leaks', () => {
const detector1 = new LeakDetector(ref1);
const detector2 = new LeakDetector(ref2);

let isLeaking1: boolean;
let isLeaking2: boolean;

// References are still held in memory.
expect(detector1.isLeaking()).toBe(true);
expect(detector2.isLeaking()).toBe(true);
isLeaking1 = await detector1.isLeaking();
expect(isLeaking1).toBe(true);
isLeaking2 = await detector2.isLeaking();
expect(isLeaking2).toBe(true);

// We destroy the reference to ref1.
ref1 = null;

// It will still be referenced by ref2, so both references are still leaking.
expect(detector1.isLeaking()).toBe(true);
expect(detector2.isLeaking()).toBe(true);
isLeaking1 = await detector1.isLeaking();
expect(isLeaking1).toBe(true);
isLeaking2 = await detector2.isLeaking();
expect(isLeaking2).toBe(true);

// We destroy the reference to ref2.
ref2 = null;

// Now both references should be gone (yay mark & sweep!).
expect(detector1.isLeaking()).toBe(false);
expect(detector2.isLeaking()).toBe(false);
isLeaking1 = await detector1.isLeaking();
expect(isLeaking1).toBe(false);
isLeaking2 = await detector2.isLeaking();
expect(isLeaking2).toBe(false);
});
10 changes: 6 additions & 4 deletions packages/jest-leak-detector/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ export default class {

try {
// eslint-disable-next-line import/no-extraneous-dependencies
weak = require('weak');
weak = require('weak-napi');
} catch (err) {
if (!err || err.code !== 'MODULE_NOT_FOUND') {
throw err;
}

throw new Error(
'The leaking detection mechanism requires the "weak" package to be installed and work. ' +
'The leaking detection mechanism requires the "weak-api" package to be installed and work. ' +
'Please install it as a dependency on your main project',
);
}
Expand All @@ -46,10 +46,12 @@ export default class {
value = null;
}

isLeaking(): boolean {
isLeaking(): Promise<boolean> {
this._runGarbageCollector();

return this._isReferenceBeingHeld;
return new Promise(resolve =>
setImmediate(() => resolve(this._isReferenceBeingHeld)),
);
}

private _runGarbageCollector() {
Expand Down
5 changes: 1 addition & 4 deletions packages/jest-runner/src/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,8 @@ export default async function runTest(
);

if (leakDetector) {
// We wanna allow a tiny but time to pass to allow last-minute cleanup
await new Promise(resolve => setTimeout(resolve, 100));

// Resolve leak detector, outside the "runTestInternal" closure.
result.leaks = leakDetector.isLeaking();
result.leaks = await leakDetector.isLeaking();
} else {
result.leaks = false;
}
Expand Down
48 changes: 33 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1961,13 +1961,6 @@
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d"
integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==

"@types/weak@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/weak/-/weak-1.0.0.tgz#7b3bf891c4b53e2b8a144b7e41f4b0f6e78c2ba8"
integrity sha512-6WXZpeAac3vj5+OfQvlqYEtc88oOgvkcxbrnmBw53Da6gA+MGztL+Hns3BpnyUevgz+4DxsJblgAew1A/tkcng==
dependencies:
"@types/node" "*"

"@types/write-file-atomic@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-2.1.1.tgz#7f9fcd6c5c8d194dba03472e3fa6cb29a839764c"
Expand Down Expand Up @@ -2967,7 +2960,7 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.0.tgz#9523e001306a32444b907423f1de2164222f6ab1"
integrity sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==

bindings@^1.2.1:
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
Expand Down Expand Up @@ -6261,6 +6254,18 @@ get-stream@^4.0.0, get-stream@^4.1.0:
dependencies:
pump "^3.0.0"

get-symbol-from-current-process-h@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.1.tgz#7e4809087e7d2f3a78a785b36f787e2183ba4c5d"
integrity sha512-QvP1+tCDjgTiu+akjdEYd8eK8MFYy6nRCRNjfiCeQB9RJEHQZpN+WE+CVqPRNqjIVMwSqd0WiD008B+b7iIdaA==

get-uv-event-loop-napi-h@^1.0.2:
version "1.0.5"
resolved "https://registry.yarnpkg.com/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.5.tgz#1904a1dc1fa6df7487c9e8eaf87302bcc9e33e47"
integrity sha512-uWDHId313vRTyqeLhlLWJS0CJOP8QXY5en/9Pv14dnPvAlRfKBfD6h2EDtoy7jxxOIWB9QgzYK16VCN3pCZOBg==
dependencies:
get-symbol-from-current-process-h "^1.0.1"

get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
Expand Down Expand Up @@ -9072,7 +9077,7 @@ [email protected], mute-stream@~0.0.4:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=

nan@^2.0.5, nan@^2.9.2:
nan@^2.9.2:
version "2.12.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
Expand Down Expand Up @@ -9134,6 +9139,11 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==

node-addon-api@^1.1.0:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.3.tgz#3998d4593e2dca2ea82114670a4eb003386a9fe1"
integrity sha512-FXWH6mqjWgU8ewuahp4spec8LkroFZK2NicOv6bNwZC3kcwZUI8LeZdG80UzTSLLhK4T7MsgNwlYDVRlDdfTDg==

[email protected]:
version "1.0.4"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.4.tgz#0b784a6551426bfc16d3b2208424dcbc2b2ff038"
Expand Down Expand Up @@ -11759,6 +11769,13 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"

setimmediate-napi@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/setimmediate-napi/-/setimmediate-napi-1.0.3.tgz#f5ef99da0d9b7a1036dd375b35a687cfb483c172"
integrity sha512-ah02BktAAJJ1eHANtD93ZdvKZrCXJwSHXww5arS1YcihOlpJlwsVkns4BXh6sRJNAyWTLl6TkjVx8CjKV9qwcQ==
dependencies:
get-uv-event-loop-napi-h "^1.0.2"

setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
Expand Down Expand Up @@ -13273,13 +13290,14 @@ wcwidth@^1.0.0:
dependencies:
defaults "^1.0.3"

weak@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/weak/-/weak-1.0.1.tgz#ab99aab30706959aa0200cb8cf545bb9cb33b99e"
integrity sha1-q5mqswcGlZqgIAy4z1RbucszuZ4=
weak-napi@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/weak-napi/-/weak-napi-1.0.3.tgz#ff4dfa818db1c509ba4166530b42414ef74cbba6"
integrity sha512-cyqeMaYA5qI7RoZKAKvIHwEROEKDNxK7jXj3u56nF2rGBh+HFyhYmBb1/wAN4RqzRmkYKVVKQyqHpBoJjqtGUA==
dependencies:
bindings "^1.2.1"
nan "^2.0.5"
bindings "^1.3.0"
node-addon-api "^1.1.0"
setimmediate-napi "^1.0.3"

webidl-conversions@^4.0.2:
version "4.0.2"
Expand Down

0 comments on commit 7b1d1c9

Please sign in to comment.