test: split test-perf-hooks-timerify

This test has been flaky in the CI. It squeezes too many independent
test cases into one file, so split it up so that we can mark the
persistent flaky test case and leave the unproblematic ones alone.

PR-URL: https://github.com/nodejs/node/pull/60568
Refs: https://github.com/nodejs/node/issues/54803
Refs: https://github.com/nodejs/reliability/blob/main/reports/2025-11-03.md
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Joyee Cheung
2025-11-05 16:19:04 +01:00
committed by GitHub
parent fa9918e412
commit b4b1413779
9 changed files with 178 additions and 154 deletions

View File

@@ -0,0 +1,25 @@
// Test basic functionality of timerify and PerformanceObserver.
'use strict';
const common = require('../common');
const assert = require('assert');
const { timerify, PerformanceObserver } = require('perf_hooks');
// Verifies that `performance.timerify` is an alias of `perf_hooks.timerify`.
assert.strictEqual(performance.timerify, timerify);
// Intentional non-op. Do not wrap in common.mustCall();
const n = timerify(function noop() {});
const obs = new PerformanceObserver(common.mustCall((list) => {
const entries = list.getEntries();
const entry = entries[0];
assert(entry);
assert.strictEqual(entry.name, 'noop');
assert.strictEqual(entry.entryType, 'function');
assert.strictEqual(typeof entry.duration, 'number');
assert.strictEqual(typeof entry.startTime, 'number');
obs.disconnect();
}));
obs.observe({ entryTypes: ['function'] });
n();

View File

@@ -0,0 +1,27 @@
// Test that timerify works with class constructors and creates performance
// entries with the correct name.
'use strict';
const common = require('../common');
const assert = require('assert');
const { timerify, PerformanceObserver } = require('perf_hooks');
class N {}
const n = timerify(N);
const obs = new PerformanceObserver(common.mustCall((list) => {
const entries = list.getEntries();
const entry = entries[0];
assert.strictEqual(entry[0], 1);
assert.strictEqual(entry[1], 'abc');
assert(entry);
assert.strictEqual(entry.name, 'N');
assert.strictEqual(entry.entryType, 'function');
assert.strictEqual(typeof entry.duration, 'number');
assert.strictEqual(typeof entry.startTime, 'number');
obs.disconnect();
}));
obs.observe({ entryTypes: ['function'] });
new n(1, 'abc');

View File

@@ -0,0 +1,16 @@
// Test that errors thrown in timerified functions bubble up without creating
// performance timeline entries.
'use strict';
const common = require('../common');
const assert = require('assert');
const { timerify, PerformanceObserver } = require('perf_hooks');
const obs = new PerformanceObserver(common.mustNotCall());
obs.observe({ entryTypes: ['function'] });
const n = timerify(() => {
throw new Error('test');
});
assert.throws(() => n(), /^Error: test$/);
obs.disconnect();

View File

@@ -0,0 +1,17 @@
// Test that timerify works with histogram option for asynchronous functions.
import '../common/index.mjs';
import assert from 'assert';
import { createHistogram, timerify } from 'perf_hooks';
import { setTimeout as sleep } from 'timers/promises';
const histogram = createHistogram();
const m = async (a, b = 1) => {
await sleep(10);
};
const n = timerify(m, { histogram });
assert.strictEqual(histogram.max, 0);
for (let i = 0; i < 10; i++) {
await n();
}
assert.notStrictEqual(histogram.max, 0);

View File

@@ -0,0 +1,22 @@
// Test that timerify works with histogram option for synchronous functions.
import '../common/index.mjs';
import assert from 'assert';
import { createHistogram, timerify } from 'perf_hooks';
import { setTimeout as sleep } from 'timers/promises';
let _deadCode;
const histogram = createHistogram();
const m = (a, b = 1) => {
for (let i = 0; i < 1e3; i++)
_deadCode = i;
};
const n = timerify(m, { histogram });
assert.strictEqual(histogram.max, 0);
for (let i = 0; i < 10; i++) {
n();
await sleep(10);
}
assert(_deadCode >= 0);
assert.notStrictEqual(histogram.max, 0);

View File

@@ -0,0 +1,28 @@
// Test that timerify throws appropriate errors for invalid argument types.
'use strict';
require('../common');
const assert = require('assert');
const { timerify } = require('perf_hooks');
[1, {}, [], null, undefined, Infinity].forEach((input) => {
assert.throws(() => timerify(input),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "fn" argument must be of type function/
});
});
const asyncFunc = async (a, b = 1) => {};
const syncFunc = (a, b = 1) => {};
[1, '', {}, [], false].forEach((histogram) => {
assert.throws(() => timerify(asyncFunc, { histogram }), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => timerify(syncFunc, { histogram }), {
code: 'ERR_INVALID_ARG_TYPE'
});
});

View File

@@ -0,0 +1,19 @@
// Test that functions can be wrapped multiple times and verify length and name
// properties are preserved correctly.
'use strict';
require('../common');
const assert = require('assert');
const { timerify } = require('perf_hooks');
const m = (a, b = 1) => {};
const n = timerify(m);
const o = timerify(m);
const p = timerify(n);
assert.notStrictEqual(n, o);
assert.notStrictEqual(n, p);
assert.notStrictEqual(o, p);
assert.strictEqual(n.length, m.length);
assert.strictEqual(n.name, 'timerified m');
assert.strictEqual(p.name, 'timerified timerified m');

View File

@@ -0,0 +1,24 @@
// Regression tests for https://github.com/nodejs/node/issues/40623
// Test that timerify preserves return values and class constructor behavior.
'use strict';
require('../common');
const assert = require('assert');
const { timerify } = require('perf_hooks');
assert.strictEqual(timerify(function func() {
return 1;
})(), 1);
assert.strictEqual(timerify(function() {
return 1;
})(), 1);
assert.strictEqual(timerify(() => {
return 1;
})(), 1);
class C {}
const wrap = timerify(C);
assert.ok(new wrap() instanceof C);
assert.throws(() => wrap(), {
name: 'TypeError',
});

View File

@@ -1,154 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const {
createHistogram,
timerify,
performance,
PerformanceObserver
} = require('perf_hooks');
const {
setTimeout: sleep
} = require('timers/promises');
// Verifies that `performance.timerify` is an alias of `perf_hooks.timerify`.
assert.strictEqual(performance.timerify, timerify);
{
// Intentional non-op. Do not wrap in common.mustCall();
const n = timerify(function noop() {});
const obs = new PerformanceObserver(common.mustCall((list) => {
const entries = list.getEntries();
const entry = entries[0];
assert(entry);
assert.strictEqual(entry.name, 'noop');
assert.strictEqual(entry.entryType, 'function');
assert.strictEqual(typeof entry.duration, 'number');
assert.strictEqual(typeof entry.startTime, 'number');
obs.disconnect();
}));
obs.observe({ entryTypes: ['function'] });
n();
}
{
// If the error throws, the error should just be bubbled up and the
// performance timeline entry will not be reported.
const obs = new PerformanceObserver(common.mustNotCall());
obs.observe({ entryTypes: ['function'] });
const n = timerify(() => {
throw new Error('test');
});
assert.throws(() => n(), /^Error: test$/);
obs.disconnect();
}
{
class N {}
const n = timerify(N);
const obs = new PerformanceObserver(common.mustCall((list) => {
const entries = list.getEntries();
const entry = entries[0];
assert.strictEqual(entry[0], 1);
assert.strictEqual(entry[1], 'abc');
assert(entry);
assert.strictEqual(entry.name, 'N');
assert.strictEqual(entry.entryType, 'function');
assert.strictEqual(typeof entry.duration, 'number');
assert.strictEqual(typeof entry.startTime, 'number');
obs.disconnect();
}));
obs.observe({ entryTypes: ['function'] });
new n(1, 'abc');
}
{
[1, {}, [], null, undefined, Infinity].forEach((input) => {
assert.throws(() => timerify(input),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: /The "fn" argument must be of type function/
});
});
}
// Function can be wrapped many times, also check length and name
{
const m = (a, b = 1) => {};
const n = timerify(m);
const o = timerify(m);
const p = timerify(n);
assert.notStrictEqual(n, o);
assert.notStrictEqual(n, p);
assert.notStrictEqual(o, p);
assert.strictEqual(n.length, m.length);
assert.strictEqual(n.name, 'timerified m');
assert.strictEqual(p.name, 'timerified timerified m');
}
(async () => {
let _deadCode;
const histogram = createHistogram();
const m = (a, b = 1) => {
for (let i = 0; i < 1e3; i++)
_deadCode = i;
};
const n = timerify(m, { histogram });
assert.strictEqual(histogram.max, 0);
for (let i = 0; i < 10; i++) {
n();
await sleep(10);
}
assert.ok(_deadCode >= 0);
assert.notStrictEqual(histogram.max, 0);
[1, '', {}, [], false].forEach((histogram) => {
assert.throws(() => timerify(m, { histogram }), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
})().then(common.mustCall());
(async () => {
const histogram = createHistogram();
const m = async (a, b = 1) => {
await sleep(10);
};
const n = timerify(m, { histogram });
assert.strictEqual(histogram.max, 0);
for (let i = 0; i < 10; i++) {
await n();
}
assert.notStrictEqual(histogram.max, 0);
[1, '', {}, [], false].forEach((histogram) => {
assert.throws(() => timerify(m, { histogram }), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
})().then(common.mustCall());
// Regression tests for https://github.com/nodejs/node/issues/40623
{
assert.strictEqual(timerify(function func() {
return 1;
})(), 1);
assert.strictEqual(timerify(function() {
return 1;
})(), 1);
assert.strictEqual(timerify(() => {
return 1;
})(), 1);
class C {}
const wrap = timerify(C);
assert.ok(new wrap() instanceof C);
assert.throws(() => wrap(), {
name: 'TypeError',
});
}