mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 00:20:08 +01:00
The test has been flaky for years and new platforms keep popping up. As it squeezes too many independent test cases into one file, split them into individual files to avoid masking regressions and help only mark the real flaky ones as flaky. This might also help with the flakiness itself by avoiding updating a shared tmpdir being watched by differet tests and avoiding running all these time-consuming tests in one file, which can cause a timeout on slow machines. PR-URL: https://github.com/nodejs/node/pull/60391 Refs: https://github.com/nodejs/node/issues/49605 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
174 lines
4.7 KiB
JavaScript
174 lines
4.7 KiB
JavaScript
'use strict';
|
|
const common = require('./index.js');
|
|
const tmpdir = require('./tmpdir.js');
|
|
const fixtures = require('./fixtures.js');
|
|
const { writeFileSync, readdirSync, readFileSync, renameSync, unlinkSync } = require('node:fs');
|
|
const { spawn } = require('node:child_process');
|
|
const { once } = require('node:events');
|
|
const assert = require('node:assert');
|
|
const { setTimeout } = require('node:timers/promises');
|
|
|
|
function skipIfNoWatch() {
|
|
if (common.isIBMi) {
|
|
common.skip('IBMi does not support `fs.watch()`');
|
|
}
|
|
|
|
if (common.isAIX) {
|
|
common.skip('folder watch capability is limited in AIX.');
|
|
}
|
|
}
|
|
|
|
function skipIfNoWatchModeSignals() {
|
|
if (common.isWindows) {
|
|
common.skip('no signals on Windows');
|
|
}
|
|
|
|
if (common.isIBMi) {
|
|
common.skip('IBMi does not support `fs.watch()`');
|
|
}
|
|
|
|
if (common.isAIX) {
|
|
common.skip('folder watch capability is limited in AIX.');
|
|
}
|
|
}
|
|
|
|
const fixturePaths = {};
|
|
const fixtureContent = {};
|
|
|
|
function refreshForTestRunnerWatch() {
|
|
tmpdir.refresh();
|
|
const files = readdirSync(fixtures.path('test-runner-watch'));
|
|
for (const file of files) {
|
|
const src = fixtures.path('test-runner-watch', file);
|
|
const dest = tmpdir.resolve(file);
|
|
fixturePaths[file] = dest;
|
|
fixtureContent[file] = readFileSync(src, 'utf8');
|
|
writeFileSync(dest, fixtureContent[file]);
|
|
}
|
|
}
|
|
|
|
async function testRunnerWatch({
|
|
fileToUpdate,
|
|
file,
|
|
action = 'update',
|
|
fileToCreate,
|
|
isolation,
|
|
}) {
|
|
const ran1 = Promise.withResolvers();
|
|
const ran2 = Promise.withResolvers();
|
|
const child = spawn(process.execPath,
|
|
['--watch', '--test', '--test-reporter=spec',
|
|
isolation ? `--test-isolation=${isolation}` : '',
|
|
file ? fixturePaths[file] : undefined].filter(Boolean),
|
|
{ encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path });
|
|
let stdout = '';
|
|
let currentRun = '';
|
|
const runs = [];
|
|
|
|
child.stdout.on('data', (data) => {
|
|
stdout += data.toString();
|
|
currentRun += data.toString();
|
|
const testRuns = stdout.match(/duration_ms\s\d+/g);
|
|
if (testRuns?.length >= 1) ran1.resolve();
|
|
if (testRuns?.length >= 2) ran2.resolve();
|
|
});
|
|
|
|
const testUpdate = async () => {
|
|
await ran1.promise;
|
|
runs.push(currentRun);
|
|
currentRun = '';
|
|
const content = fixtureContent[fileToUpdate];
|
|
const path = fixturePaths[fileToUpdate];
|
|
writeFileSync(path, content);
|
|
await setTimeout(common.platformTimeout(1000));
|
|
await ran2.promise;
|
|
runs.push(currentRun);
|
|
child.kill();
|
|
await once(child, 'exit');
|
|
|
|
assert.strictEqual(runs.length, 2);
|
|
|
|
for (const run of runs) {
|
|
assert.match(run, /tests 1/);
|
|
assert.match(run, /pass 1/);
|
|
assert.match(run, /fail 0/);
|
|
assert.match(run, /cancelled 0/);
|
|
}
|
|
};
|
|
|
|
const testRename = async () => {
|
|
await ran1.promise;
|
|
runs.push(currentRun);
|
|
currentRun = '';
|
|
const fileToRenamePath = tmpdir.resolve(fileToUpdate);
|
|
const newFileNamePath = tmpdir.resolve(`test-renamed-${fileToUpdate}`);
|
|
renameSync(fileToRenamePath, newFileNamePath);
|
|
await setTimeout(common.platformTimeout(1000));
|
|
await ran2.promise;
|
|
runs.push(currentRun);
|
|
child.kill();
|
|
await once(child, 'exit');
|
|
|
|
assert.strictEqual(runs.length, 2);
|
|
|
|
for (const run of runs) {
|
|
assert.match(run, /tests 1/);
|
|
assert.match(run, /pass 1/);
|
|
assert.match(run, /fail 0/);
|
|
assert.match(run, /cancelled 0/);
|
|
}
|
|
};
|
|
|
|
const testDelete = async () => {
|
|
await ran1.promise;
|
|
runs.push(currentRun);
|
|
currentRun = '';
|
|
const fileToDeletePath = tmpdir.resolve(fileToUpdate);
|
|
unlinkSync(fileToDeletePath);
|
|
await setTimeout(common.platformTimeout(2000));
|
|
ran2.resolve();
|
|
runs.push(currentRun);
|
|
child.kill();
|
|
await once(child, 'exit');
|
|
|
|
assert.strictEqual(runs.length, 2);
|
|
|
|
for (const run of runs) {
|
|
assert.doesNotMatch(run, /MODULE_NOT_FOUND/);
|
|
}
|
|
};
|
|
|
|
const testCreate = async () => {
|
|
await ran1.promise;
|
|
runs.push(currentRun);
|
|
currentRun = '';
|
|
const newFilePath = tmpdir.resolve(fileToCreate);
|
|
writeFileSync(newFilePath, 'module.exports = {};');
|
|
await setTimeout(common.platformTimeout(1000));
|
|
await ran2.promise;
|
|
runs.push(currentRun);
|
|
child.kill();
|
|
await once(child, 'exit');
|
|
|
|
for (const run of runs) {
|
|
assert.match(run, /tests 1/);
|
|
assert.match(run, /pass 1/);
|
|
assert.match(run, /fail 0/);
|
|
assert.match(run, /cancelled 0/);
|
|
}
|
|
};
|
|
|
|
action === 'update' && await testUpdate();
|
|
action === 'rename' && await testRename();
|
|
action === 'delete' && await testDelete();
|
|
action === 'create' && await testCreate();
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
skipIfNoWatch,
|
|
skipIfNoWatchModeSignals,
|
|
testRunnerWatch,
|
|
refreshForTestRunnerWatch,
|
|
};
|