test_runner: call abort on test finish
PR-URL: https://github.com/nodejs/node/pull/48827 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
This commit is contained in:
parent
4ee4718857
commit
24c3d8a1b3
@ -601,6 +601,12 @@ class Test extends AsyncResource {
|
|||||||
} else {
|
} else {
|
||||||
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));
|
this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure));
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
// Do not abort hooks and the root test as hooks instance are shared between tests suite so aborting them will
|
||||||
|
// cause them to not run for further tests.
|
||||||
|
if (this.parent !== null) {
|
||||||
|
this.#abortController.abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the test. Then, try to report the results and execute any
|
// Clean up the test. Then, try to report the results and execute any
|
||||||
|
25
test/fixtures/test-runner/aborts/failed-test-still-call-abort.js
vendored
Normal file
25
test/fixtures/test-runner/aborts/failed-test-still-call-abort.js
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const {test, afterEach} = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const { waitForAbort } = require('./wait-for-abort-helper');
|
||||||
|
|
||||||
|
let testCount = 0;
|
||||||
|
let signal;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
|
||||||
|
waitForAbort({ testNumber: ++testCount, signal });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("sync", (t) => {
|
||||||
|
signal = t.signal;
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
throw new Error('failing the sync test');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("async", async (t) => {
|
||||||
|
await null;
|
||||||
|
signal = t.signal;
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
throw new Error('failing the async test');
|
||||||
|
});
|
23
test/fixtures/test-runner/aborts/successful-test-still-call-abort.js
vendored
Normal file
23
test/fixtures/test-runner/aborts/successful-test-still-call-abort.js
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const {test, afterEach} = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const {waitForAbort} = require("./wait-for-abort-helper");
|
||||||
|
|
||||||
|
let testCount = 0;
|
||||||
|
let signal;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
|
||||||
|
waitForAbort({ testNumber: ++testCount, signal });
|
||||||
|
});
|
||||||
|
|
||||||
|
test("sync", (t) => {
|
||||||
|
signal = t.signal;
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("async", async (t) => {
|
||||||
|
await null;
|
||||||
|
signal = t.signal;
|
||||||
|
assert.equal(signal.aborted, false);
|
||||||
|
});
|
19
test/fixtures/test-runner/aborts/wait-for-abort-helper.js
vendored
Normal file
19
test/fixtures/test-runner/aborts/wait-for-abort-helper.js
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module.exports = {
|
||||||
|
waitForAbort: function ({ testNumber, signal }) {
|
||||||
|
let retries = 0;
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
retries++;
|
||||||
|
if(signal.aborted) {
|
||||||
|
console.log(`abort called for test ${testNumber}`);
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(retries > 100) {
|
||||||
|
clearInterval(interval);
|
||||||
|
throw new Error(`abort was not called for test ${testNumber}`);
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
@ -118,9 +118,27 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
|
|||||||
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
|
assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emit "test:watch:drained" event on watch mode', async () => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
await run({
|
||||||
|
files: [join(testFixtures, 'test/random.cjs')],
|
||||||
|
watch: true,
|
||||||
|
signal: controller.signal,
|
||||||
|
}).on('data', function({ type }) {
|
||||||
|
if (type === 'test:watch:drained') {
|
||||||
|
controller.abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AbortSignal', () => {
|
||||||
it('should stop watch mode when abortSignal aborts', async () => {
|
it('should stop watch mode when abortSignal aborts', async () => {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const result = await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
|
const result = await run({
|
||||||
|
files: [join(testFixtures, 'test/random.cjs')],
|
||||||
|
watch: true,
|
||||||
|
signal: controller.signal,
|
||||||
|
})
|
||||||
.compose(async function* (source) {
|
.compose(async function* (source) {
|
||||||
for await (const chunk of source) {
|
for await (const chunk of source) {
|
||||||
if (chunk.type === 'test:pass') {
|
if (chunk.type === 'test:pass') {
|
||||||
@ -133,13 +151,70 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
|
|||||||
assert.deepStrictEqual(result, ['this should pass']);
|
assert.deepStrictEqual(result, ['this should pass']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit "test:watch:drained" event on watch mode', async () => {
|
it('should abort when test succeeded', async () => {
|
||||||
const controller = new AbortController();
|
const stream = run({
|
||||||
await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
|
files: [
|
||||||
.on('data', function({ type }) {
|
fixtures.path(
|
||||||
if (type === 'test:watch:drained') {
|
'test-runner',
|
||||||
controller.abort();
|
'aborts',
|
||||||
|
'successful-test-still-call-abort.js'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let passedTestCount = 0;
|
||||||
|
let failedTestCount = 0;
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
for await (const data of stream) {
|
||||||
|
if (data.type === 'test:stdout') {
|
||||||
|
output += data.data.message.toString();
|
||||||
}
|
}
|
||||||
|
if (data.type === 'test:fail') {
|
||||||
|
failedTestCount++;
|
||||||
|
}
|
||||||
|
if (data.type === 'test:pass') {
|
||||||
|
passedTestCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.match(output, /abort called for test 1/);
|
||||||
|
assert.match(output, /abort called for test 2/);
|
||||||
|
assert.strictEqual(failedTestCount, 0, new Error('no tests should fail'));
|
||||||
|
assert.strictEqual(passedTestCount, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should abort when test failed', async () => {
|
||||||
|
const stream = run({
|
||||||
|
files: [
|
||||||
|
fixtures.path(
|
||||||
|
'test-runner',
|
||||||
|
'aborts',
|
||||||
|
'failed-test-still-call-abort.js'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let passedTestCount = 0;
|
||||||
|
let failedTestCount = 0;
|
||||||
|
|
||||||
|
let output = '';
|
||||||
|
for await (const data of stream) {
|
||||||
|
if (data.type === 'test:stdout') {
|
||||||
|
output += data.data.message.toString();
|
||||||
|
}
|
||||||
|
if (data.type === 'test:fail') {
|
||||||
|
failedTestCount++;
|
||||||
|
}
|
||||||
|
if (data.type === 'test:pass') {
|
||||||
|
passedTestCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.match(output, /abort called for test 1/);
|
||||||
|
assert.match(output, /abort called for test 2/);
|
||||||
|
assert.strictEqual(passedTestCount, 0, new Error('no tests should pass'));
|
||||||
|
assert.strictEqual(failedTestCount, 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user