Skip to content

Commit

Permalink
more
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLow committed Nov 10, 2022
1 parent 94b2e25 commit 9be722d
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 22 deletions.
23 changes: 20 additions & 3 deletions lib/internal/test_runner/runner.js
Expand Up @@ -11,6 +11,7 @@ const {
ObjectAssign,
PromisePrototypeThen,
SafePromiseAll,
SafeMap,
SafeSet,
} = primordials;

Expand Down Expand Up @@ -115,6 +116,7 @@ function getRunArgs({ path, inspectPort }) {
return argv;
}

const runningProcesses = new SafeMap();

function runTestFile(path, root, inspectPort, filesWatcher) {
const subtest = root.createSubtest(Test, path, async (t) => {
Expand All @@ -127,6 +129,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
}

const child = spawn(process.execPath, args, { signal: t.signal, encoding: 'utf8', env, stdio });
runningProcesses.set(path, child);
// TODO(cjihrig): Implement a TAP parser to read the child's stdout
// instead of just displaying it all if the child fails.
let err;
Expand Down Expand Up @@ -156,6 +159,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
child.stdout.toArray({ signal: t.signal }),
]);

runningProcesses.delete(path);
if (code !== 0 || signal !== null) {
if (!err) {
err = ObjectAssign(new ERR_TEST_FAILURE('test failed', kSubtestsFailed), {
Expand Down Expand Up @@ -194,17 +198,30 @@ function run(options) {

let postRun = () => root.postRun();
let filesWatcher;
const runningSubtests = new SafeMap();
if (watch) {
filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter' });
filesWatcher.on('changed', ({ owners }) => {
ArrayPrototypeForEach(ArrayPrototypeFilter(testFiles, (file) => owners.has(file)), (file) => {
runTestFile(file, root, inspectPort, filesWatcher);
filesWatcher.unfilterFilesOwnedBy(owners);
const dependantTests = ArrayPrototypeFilter(testFiles, (file) => owners.has(file));
ArrayPrototypeForEach(dependantTests, async (file) => {
const runningProcess = runningProcesses.get(file);
if (runningProcess) {
runningProcess.kill();
await once(runningProcess, 'exit');
}
await runningSubtests.get(file);
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher));
});
});
postRun = undefined;
}

PromisePrototypeThen(SafePromiseAll(testFiles, (path) => runTestFile(path, root, inspectPort, filesWatcher)),
PromisePrototypeThen(SafePromiseAll(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher);
runningSubtests.set(path, subtest);
return subtest;
}),
postRun);


Expand Down
30 changes: 25 additions & 5 deletions lib/internal/watch_mode/files_watcher.js
Expand Up @@ -24,7 +24,8 @@ class FilesWatcher extends EventEmitter {
#watchers = new SafeMap();
#filteredFiles = new SafeSet();
#throttling = new SafeSet();
#depencencyMap = new SafeMap();
#depencencyOwners = new SafeMap();
#ownerDependencities = new SafeMap();
#throttle;
#mode;

Expand Down Expand Up @@ -73,7 +74,8 @@ class FilesWatcher extends EventEmitter {
return;
}
this.#throttling.add(trigger);
this.emit('changed', { owners: this.#depencencyMap.get(trigger) });
const owners = this.#depencencyOwners.get(trigger);
this.emit('changed', { owners });
setTimeout(() => this.#throttling.delete(trigger), this.#throttle).unref();
}

Expand Down Expand Up @@ -104,9 +106,12 @@ class FilesWatcher extends EventEmitter {
}
this.#filteredFiles.add(file);
if (owner) {
const owners = this.#depencencyMap.get(file) ?? new SafeSet();
const owners = this.#depencencyOwners.get(file) ?? new SafeSet();
const dependencies = this.#ownerDependencities.get(file) ?? new SafeSet();
owners.add(owner);
this.#depencencyMap.set(file, owners);
dependencies.add(file);
this.#depencencyOwners.set(file, owners);
this.#ownerDependencities.set(owner, dependencies);
}
}
watchChildProcessModules(child, key = null) {
Expand All @@ -126,14 +131,29 @@ class FilesWatcher extends EventEmitter {
}
});
}
unfilterFilesOwnedBy(owners) {
owners.forEach((owner) => {
const dependencies = this.#ownerDependencities.get(owner);
if (dependencies) {
dependencies.forEach((dependency) => {
this.#filteredFiles.delete(dependency);
this.#depencencyOwners.delete(dependency);
});
}
this.#filteredFiles.delete(owner);
this.#depencencyOwners.delete(owner);
this.#ownerDependencities.delete(owner);
});
}
clearFileFilters() {
this.#filteredFiles.clear();
}
clear() {
this.#watchers.forEach(this.#unwatch);
this.#watchers.clear();
this.#filteredFiles.clear();
this.#depencencyMap.clear();
this.#depencencyOwners.clear();
this.#ownerDependencities.clear();
}
}

Expand Down
17 changes: 3 additions & 14 deletions test/parallel/test-runner-run-watch-mode.mjs
@@ -1,19 +1,8 @@
import * as common from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import { join } from 'node:path';
import { describe, it, run } from 'node:test';
import assert from 'node:assert';

const testFixtures = fixtures.path('test-runner');
import '../common/index.mjs';
import { describe, it } from 'node:test';

describe('test runner watch mode', { concurrency: true }, () => {

it('should run a test repeatedly when its dependencies change', async () => {
const stream = run({ files: [] });
stream.setEncoding('utf8');
stream.on('test:fail', common.mustNotCall());
stream.on('test:pass', common.mustNotCall());
// eslint-disable-next-line no-unused-vars
for await (const _ of stream); // TODO(MoLow): assert.snapshot
it('should run a test repeatedly when its dependencies change', { todo: true }, async () => {
});
});

0 comments on commit 9be722d

Please sign in to comment.