Signed-off-by: marcopiraccini <marco.piraccini@gmail.com> PR-URL: https://github.com/nodejs/node/pull/63781 Fixes: https://github.com/nodejs/node/issues/63767 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
302 lines
8.2 KiB
JavaScript
302 lines
8.2 KiB
JavaScript
import * as common from '../common/index.mjs';
|
|
import tmpdir from '../common/tmpdir.js';
|
|
import assert from 'node:assert';
|
|
import path from 'node:path';
|
|
import { execPath } from 'node:process';
|
|
import { describe, it } from 'node:test';
|
|
import { spawn } from 'node:child_process';
|
|
import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
import { inspect } from 'node:util';
|
|
import { pathToFileURL } from 'node:url';
|
|
import { createInterface } from 'node:readline';
|
|
|
|
if (common.isIBMi)
|
|
common.skip('IBMi does not support `fs.watch()`');
|
|
|
|
function restart(file) {
|
|
appendFileSync(file, '\n');
|
|
const timer = setInterval(() => appendFileSync(file, '\n'), common.platformTimeout(250));
|
|
return () => clearInterval(timer);
|
|
}
|
|
|
|
function changeDetected(file) {
|
|
return `Change detected in ${inspect(file)}`;
|
|
}
|
|
|
|
let tmpFiles = 0;
|
|
function createTmpFile(content = 'console.log(\'running\');', ext = '.js', basename = tmpdir.path) {
|
|
const file = path.join(basename, `${tmpFiles++}${ext}`);
|
|
writeFileSync(file, content);
|
|
return file;
|
|
}
|
|
|
|
function createTmpDir() {
|
|
const dir = path.join(tmpdir.path, `${tmpFiles++}`);
|
|
mkdirSync(dir);
|
|
return dir;
|
|
}
|
|
|
|
async function runWriteSucceed({
|
|
file,
|
|
watchedFile,
|
|
watchFlag = '--watch',
|
|
args = [file],
|
|
completed = 'Completed running',
|
|
restarts = 2,
|
|
options = {},
|
|
shouldFail = false,
|
|
}) {
|
|
args.unshift('--no-warnings');
|
|
if (watchFlag !== null) args.unshift(watchFlag);
|
|
|
|
const child = spawn(execPath, args, { encoding: 'utf8', stdio: 'pipe', ...options });
|
|
|
|
let completes = 0;
|
|
let cancelRestarts = () => {};
|
|
let stderr = '';
|
|
const stdout = [];
|
|
|
|
child.stderr.on('data', (data) => {
|
|
stderr += data;
|
|
});
|
|
|
|
try {
|
|
for await (const data of createInterface({ input: child.stdout })) {
|
|
if (!data.startsWith('Waiting for graceful termination') &&
|
|
!data.startsWith('Gracefully restarted')) {
|
|
stdout.push(data);
|
|
}
|
|
|
|
if (data.startsWith(completed)) {
|
|
completes++;
|
|
|
|
if (completes === restarts) break;
|
|
|
|
if (completes === 1) {
|
|
cancelRestarts = restart(watchedFile);
|
|
}
|
|
}
|
|
|
|
if (!shouldFail && data.startsWith('Failed running')) break;
|
|
}
|
|
} finally {
|
|
child.kill();
|
|
cancelRestarts();
|
|
}
|
|
|
|
return { stdout, stderr, pid: child.pid };
|
|
}
|
|
|
|
tmpdir.refresh();
|
|
|
|
describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_000 }, () => {
|
|
it('should watch changes to worker - cjs', async () => {
|
|
const dir = createTmpDir();
|
|
const worker = path.join(dir, 'worker.js');
|
|
|
|
writeFileSync(worker, `
|
|
console.log('worker running');
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
const { Worker } = require('node:worker_threads');
|
|
const w = new Worker(${JSON.stringify(worker)});
|
|
`, '.js', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: worker,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'worker running',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(worker),
|
|
`Restarting ${inspect(file)}`,
|
|
'worker running',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
|
|
it('should watch changes to worker dependencies - cjs', async () => {
|
|
const dir = createTmpDir();
|
|
const dep = path.join(dir, 'dep.js');
|
|
const worker = path.join(dir, 'worker.js');
|
|
|
|
writeFileSync(dep, `
|
|
module.exports = 'dep v1';
|
|
`);
|
|
|
|
writeFileSync(worker, `
|
|
const dep = require('./dep.js');
|
|
console.log(dep);
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
const { Worker } = require('node:worker_threads');
|
|
const w = new Worker(${JSON.stringify(worker)});
|
|
`, '.js', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: dep,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(dep),
|
|
`Restarting ${inspect(file)}`,
|
|
'dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
|
|
it('should watch changes to nested worker dependencies - cjs', async () => {
|
|
const dir = createTmpDir();
|
|
const subDep = path.join(dir, 'sub-dep.js');
|
|
const dep = path.join(dir, 'dep.js');
|
|
const worker = path.join(dir, 'worker.js');
|
|
|
|
writeFileSync(subDep, `
|
|
module.exports = 'sub-dep v1';
|
|
`);
|
|
|
|
writeFileSync(dep, `
|
|
const subDep = require('./sub-dep.js');
|
|
console.log(subDep);
|
|
module.exports = 'dep v1';
|
|
`);
|
|
|
|
writeFileSync(worker, `
|
|
const dep = require('./dep.js');
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
const { Worker } = require('node:worker_threads');
|
|
const w = new Worker(${JSON.stringify(worker)});
|
|
`, '.js', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: subDep,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'sub-dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(subDep),
|
|
`Restarting ${inspect(file)}`,
|
|
'sub-dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
|
|
it('should watch changes to worker - esm', async () => {
|
|
const dir = createTmpDir();
|
|
const worker = path.join(dir, 'worker.mjs');
|
|
|
|
writeFileSync(worker, `
|
|
console.log('worker running');
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
import { Worker } from 'node:worker_threads';
|
|
new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));
|
|
`, '.mjs', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: worker,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'worker running',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(worker),
|
|
`Restarting ${inspect(file)}`,
|
|
'worker running',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
|
|
it('should watch changes to worker dependencies - esm', async () => {
|
|
const dir = createTmpDir();
|
|
const dep = path.join(dir, 'dep.mjs');
|
|
const worker = path.join(dir, 'worker.mjs');
|
|
|
|
writeFileSync(dep, `
|
|
export default 'dep v1';
|
|
`);
|
|
|
|
writeFileSync(worker, `
|
|
import dep from ${JSON.stringify(pathToFileURL(dep))};
|
|
console.log(dep);
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
import { Worker } from 'node:worker_threads';
|
|
new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));
|
|
`, '.mjs', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: dep,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(dep),
|
|
`Restarting ${inspect(file)}`,
|
|
'dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
|
|
it('should watch changes to nested worker dependencies - esm', async () => {
|
|
const dir = createTmpDir();
|
|
const subDep = path.join(dir, 'sub-dep.mjs');
|
|
const dep = path.join(dir, 'dep.mjs');
|
|
const worker = path.join(dir, 'worker.mjs');
|
|
|
|
writeFileSync(subDep, `
|
|
export default 'sub-dep v1';
|
|
`);
|
|
|
|
writeFileSync(dep, `
|
|
import subDep from ${JSON.stringify(pathToFileURL(subDep))};
|
|
console.log(subDep);
|
|
export default 'dep v1';
|
|
`);
|
|
|
|
writeFileSync(worker, `
|
|
import dep from ${JSON.stringify(pathToFileURL(dep))};
|
|
`);
|
|
|
|
const file = createTmpFile(`
|
|
import { Worker } from 'node:worker_threads';
|
|
new Worker(new URL(${JSON.stringify(pathToFileURL(worker))}));
|
|
`, '.mjs', dir);
|
|
|
|
const { stderr, stdout } = await runWriteSucceed({
|
|
file,
|
|
watchedFile: subDep,
|
|
});
|
|
|
|
assert.strictEqual(stderr, '');
|
|
assert.deepStrictEqual(stdout, [
|
|
'sub-dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
changeDetected(subDep),
|
|
`Restarting ${inspect(file)}`,
|
|
'sub-dep v1',
|
|
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
|
|
]);
|
|
});
|
|
});
|