Skip to content

Commit

Permalink
test_runner: change root test to be a suite
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLow committed Aug 3, 2023
1 parent 74c7664 commit d61c000
Show file tree
Hide file tree
Showing 20 changed files with 338 additions and 43 deletions.
16 changes: 11 additions & 5 deletions lib/internal/test_runner/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ const {
parseCommandLine,
setupTestReporters,
} = require('internal/test_runner/utils');
const { setImmediate } = require('timers');
const { bigint: hrtime } = process.hrtime;

const testResources = new SafeMap();

function createTestTree(options = kEmptyObject) {
return setup(new Test({ __proto__: null, ...options, name: '<root>' }));
return setup(new Suite({ __proto__: null, ...options, name: '<root>' }));
}

function createProcessEventHandler(eventName, rootTest) {
Expand Down Expand Up @@ -140,8 +141,8 @@ function setup(root) {
const rejectionHandler =
createProcessEventHandler('unhandledRejection', root);
const coverage = configureCoverage(root, globalOptions);
const exitHandler = async () => {
await root.run(new ERR_TEST_FAILURE(
const exitHandler = () => {
root.postRun(new ERR_TEST_FAILURE(
'Promise resolution is still pending but the event loop has already resolved',
kCancelledByParent));

Expand All @@ -150,8 +151,8 @@ function setup(root) {
process.removeListener('uncaughtException', exceptionHandler);
};

const terminationHandler = async () => {
await exitHandler();
const terminationHandler = () => {
exitHandler();
process.exit();
};

Expand Down Expand Up @@ -196,6 +197,7 @@ function getGlobalRoot() {
}
});
reportersSetup = setupTestReporters(globalRoot);
setImmediate(() => globalRoot.run());
}
return globalRoot;
}
Expand All @@ -208,6 +210,10 @@ async function startSubtest(subtest, parent) {
} else {
await subtest.start();
}
if (parent.parent === null && parent.endTime !== null) {
parent.endTime = null;
parent.processPendingSubtests();
}
}

function runInParentContext(Factory) {
Expand Down
22 changes: 12 additions & 10 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const {
PromisePrototypeThen,
SafePromiseAll,
SafePromiseAllReturnVoid,
SafePromiseAllSettledReturnVoid,
PromiseResolve,
SafeMap,
SafeSet,
Expand Down Expand Up @@ -378,7 +377,7 @@ function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) {
throw err;
}
});
return subtest.start();
return subtest.enqueue();
}

function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
Expand All @@ -404,6 +403,7 @@ function watchFiles(testFiles, root, inspectPort, signal, testNamePatterns) {
}
await runningSubtests.get(file);
runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns));
root.processPendingSubtests();
}, undefined, (error) => {
triggerUncaughtException(error, true /* fromPromise */);
}));
Expand Down Expand Up @@ -476,22 +476,24 @@ function run(options) {
testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1);
}

let postRun = () => root.postRun();
let filesWatcher;
if (watch) {
filesWatcher = watchFiles(testFiles, root, inspectPort, signal, testNamePatterns);
postRun = undefined;
}
const runFiles = () => {
root.harness.bootstrapComplete = true;
return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
filesWatcher?.runningSubtests.set(path, subtest);
return subtest;
});
for (let i = 0; i < testFiles.length; i++) {
const path = testFiles[i];
const enqueued = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns);
filesWatcher?.runningSubtests.set(path, enqueued);
}
if (filesWatcher) {
return root.processPendingSubtests();
}
return PromisePrototypeThen(root.run(), () => root.postRun());
};

PromisePrototypeThen(PromisePrototypeThen(PromiseResolve(setup?.(root)), runFiles), postRun);
PromisePrototypeThen(PromiseResolve(setup?.(root)), runFiles);

return root.reporter;
}
Expand Down
19 changes: 12 additions & 7 deletions lib/internal/test_runner/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ class SuiteContext {
get name() {
return this.#suite.name;
}

diagnostic(message) {
this.#suite.diagnostic(message);
}
}

class Test extends AsyncResource {
Expand Down Expand Up @@ -564,7 +568,7 @@ class Test extends AsyncResource {
}
}

async run(pendingSubtestsError) {
async run() {
this.startTime = hrtime();

if (this[kShouldAbort]()) {
Expand Down Expand Up @@ -651,7 +655,7 @@ class Test extends AsyncResource {

// Clean up the test. Then, try to report the results and execute any
// tests that were pending due to available concurrency.
this.postRun(pendingSubtestsError);
this.postRun();
}

postRun(pendingSubtestsError) {
Expand Down Expand Up @@ -853,13 +857,12 @@ class Suite extends Test {
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));
}),
() => {
this.buildPhaseFinished = true;
this.buildPhaseFinished = this.parent !== null;
},
);
} catch (err) {
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));

this.buildPhaseFinished = true;
this.buildPhaseFinished = this.parent !== null;
}
this.fn = () => {};
}
Expand All @@ -883,7 +886,7 @@ class Suite extends Test {
return;
}

if (this.parent.hooks.before.length > 0) {
if (this.parent?.hooks.before.length > 0) {
await this.parent.runHook('before', this.parent.getRunArgs());
}

Expand All @@ -908,7 +911,9 @@ class Suite extends Test {
stopPromise?.[SymbolDispose]();
}

this.postRun();
if (this.parent !== null) {
this.postRun();
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions test/fixtures/test-runner/output/abort.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require('../../../common');
const test = require('node:test');

test('promise timeout signal', { signal: AbortSignal.timeout(1) }, async (t) => {
test('promise timeout signal', { signal: AbortSignal.timeout(100) }, async (t) => {
await Promise.all([
t.test('ok 1', async () => {}),
t.test('ok 2', () => {}),
Expand All @@ -23,7 +23,7 @@ test('promise abort signal', { signal: AbortSignal.abort() }, async (t) => {
await t.test('should not appear', () => {});
});

test('callback timeout signal', { signal: AbortSignal.timeout(1) }, (t, done) => {
test('callback timeout signal', { signal: AbortSignal.timeout(100) }, (t, done) => {
t.test('ok 1', async () => {});
t.test('ok 2', () => {});
t.test('ok 3', { signal: t.signal }, async () => {});
Expand All @@ -41,7 +41,7 @@ test('callback abort signal', { signal: AbortSignal.abort() }, (t, done) => {
t.test('should not appear', done);
});

// AbortSignal.timeout(1) doesn't prevent process from closing
// AbortSignal.timeout doesn't prevent process from closing
// thus we have to keep the process open to prevent cancelation
// of the entire test tree
setTimeout(() => {}, 1000);
8 changes: 8 additions & 0 deletions test/fixtures/test-runner/output/abort.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: not ok 2
not ok 6 - not ok 2
Expand All @@ -35,6 +37,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: not ok 3
not ok 7 - not ok 3
Expand Down Expand Up @@ -157,6 +161,8 @@ not ok 2 - promise abort signal
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 2)
...
# Subtest: not ok 2
not ok 6 - not ok 2
Expand All @@ -165,6 +171,8 @@ not ok 2 - promise abort signal
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 2)
...
# Subtest: not ok 3
not ok 7 - not ok 3
Expand Down
10 changes: 9 additions & 1 deletion test/fixtures/test-runner/output/abort_hooks.snapshot
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
TAP version 13
before
2.1
2.2
Expand All @@ -6,7 +7,6 @@ beforeEach
4.1
afterEach
4.2
TAP version 13
# Subtest: 1 before describe
# Subtest: test 1
not ok 1 - test 1
Expand All @@ -15,6 +15,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: test 2
not ok 2 - test 2
Expand All @@ -23,6 +25,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
1..2
not ok 1 - 1 before describe
Expand Down Expand Up @@ -126,6 +130,8 @@ not ok 3 - 3 beforeEach describe
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 2)
...
# Subtest: 4 afterEach describe
# Subtest: test 1
Expand Down Expand Up @@ -176,6 +182,8 @@ not ok 4 - 4 afterEach describe
failureType: 'subtestsFailed'
error: '2 subtests failed'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 3)
...
1..4
# tests 8
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/test-runner/output/abort_suite.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require('../../../common');
const { describe, it } = require('node:test');

describe('describe timeout signal', { signal: AbortSignal.timeout(1) }, (t) => {
describe('describe timeout signal', { signal: AbortSignal.timeout(100) }, (t) => {
it('ok 1', async () => {});
it('ok 2', () => {});
it('ok 3', { signal: t.signal }, async () => {});
Expand All @@ -21,7 +21,7 @@ describe('describe abort signal', { signal: AbortSignal.abort() }, () => {
it('should not appear', () => {});
});

// AbortSignal.timeout(1) doesn't prevent process from closing
// AbortSignal.timeout doesn't prevent process from closing
// thus we have to keep the process open to prevent cancelation
// of the entire test tree
setTimeout(() => {}, 1000);
4 changes: 4 additions & 0 deletions test/fixtures/test-runner/output/abort_suite.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: not ok 2
not ok 6 - not ok 2
Expand All @@ -35,6 +37,8 @@ TAP version 13
failureType: 'cancelledByParent'
error: 'test did not finish before its parent and was cancelled'
code: 'ERR_TEST_FAILURE'
stack: |-
async Promise.all (index 0)
...
# Subtest: not ok 3
not ok 7 - not ok 3
Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/test-runner/output/async-test-scheduling.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as common from '../../../common/index.mjs';
import { describe, test } from "node:test";
import { setTimeout } from "node:timers/promises";

test("test", common.mustCall(() => {}));
describe("suite", common.mustCall(async () => {
test("test", common.mustCall(() => {}));
await setTimeout(10);
test("scheduled async", common.mustCall(() => {}));
}));

await setTimeout(10);
test("scheduled async", common.mustCall(() => {}));
37 changes: 37 additions & 0 deletions test/fixtures/test-runner/output/async-test-scheduling.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
TAP version 13
# Subtest: test
ok 1 - test
---
duration_ms: *
...
# Subtest: suite
# Subtest: test
ok 1 - test
---
duration_ms: *
...
# Subtest: scheduled async
ok 2 - scheduled async
---
duration_ms: *
...
1..2
ok 2 - suite
---
duration_ms: *
type: 'suite'
...
# Subtest: scheduled async
ok 3 - scheduled async
---
duration_ms: *
...
1..3
# tests 4
# suites 1
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms *
2 changes: 2 additions & 0 deletions test/fixtures/test-runner/output/default_output.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*[39m
*[39m
*[39m
at async Promise.all (index 0)
*[39m

[90m﹣ should skip [90m(*ms)[39m # SKIP[39m
Expand Down Expand Up @@ -46,6 +47,7 @@
*[39m
*[39m
*[39m
at async Promise.all (index 0)
*[39m

[31m✖ should fail [90m(*ms)[39m[39m
Expand Down
Loading

0 comments on commit d61c000

Please sign in to comment.