mirror of
https://github.com/nodejs/node.git
synced 2025-04-30 07:19:19 +00:00

PR-URL: https://github.com/nodejs/node/pull/46629 Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
156 lines
4.2 KiB
JavaScript
156 lines
4.2 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayPrototypeFilter,
|
|
ArrayPrototypeFind,
|
|
NumberParseInt,
|
|
} = primordials;
|
|
const {
|
|
codes: { ERR_TAP_VALIDATION_ERROR },
|
|
} = require('internal/errors');
|
|
const { TokenKind } = require('internal/test_runner/tap_lexer');
|
|
|
|
// TODO(@manekinekko): add more validation rules based on the TAP14 spec.
|
|
// See https://testanything.org/tap-version-14-specification.html
|
|
class TAPValidationStrategy {
|
|
validate(ast) {
|
|
this.#validateVersion(ast);
|
|
this.#validatePlan(ast);
|
|
this.#validateTestPoints(ast);
|
|
|
|
return true;
|
|
}
|
|
|
|
#validateVersion(ast) {
|
|
const entry = ArrayPrototypeFind(
|
|
ast,
|
|
(node) => node.kind === TokenKind.TAP_VERSION,
|
|
);
|
|
|
|
if (!entry) {
|
|
throw new ERR_TAP_VALIDATION_ERROR('missing TAP version');
|
|
}
|
|
|
|
const { version } = entry.node;
|
|
|
|
// TAP14 specification is compatible with observed behavior of existing TAP13 consumers and producers
|
|
if (version !== '14' && version !== '13') {
|
|
throw new ERR_TAP_VALIDATION_ERROR('TAP version should be 13 or 14');
|
|
}
|
|
}
|
|
|
|
#validatePlan(ast) {
|
|
const entry = ArrayPrototypeFind(
|
|
ast,
|
|
(node) => node.kind === TokenKind.TAP_PLAN,
|
|
);
|
|
|
|
if (!entry) {
|
|
throw new ERR_TAP_VALIDATION_ERROR('missing TAP plan');
|
|
}
|
|
|
|
const plan = entry.node;
|
|
|
|
if (!plan.start) {
|
|
throw new ERR_TAP_VALIDATION_ERROR('missing plan start');
|
|
}
|
|
|
|
if (!plan.end) {
|
|
throw new ERR_TAP_VALIDATION_ERROR('missing plan end');
|
|
}
|
|
|
|
const planStart = NumberParseInt(plan.start, 10);
|
|
const planEnd = NumberParseInt(plan.end, 10);
|
|
|
|
if (planEnd !== 0 && planStart > planEnd) {
|
|
throw new ERR_TAP_VALIDATION_ERROR(
|
|
`plan start ${planStart} is greater than plan end ${planEnd}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// TODO(@manekinekko): since we are dealing with a flat AST, we need to
|
|
// validate test points grouped by their "nesting" level. This is because a set of
|
|
// Test points belongs to a TAP document. Each new subtest block creates a new TAP document.
|
|
// https://testanything.org/tap-version-14-specification.html#subtests
|
|
#validateTestPoints(ast) {
|
|
const bailoutEntry = ArrayPrototypeFind(
|
|
ast,
|
|
(node) => node.kind === TokenKind.TAP_BAIL_OUT,
|
|
);
|
|
const planEntry = ArrayPrototypeFind(
|
|
ast,
|
|
(node) => node.kind === TokenKind.TAP_PLAN,
|
|
);
|
|
const testPointEntries = ArrayPrototypeFilter(
|
|
ast,
|
|
(node) => node.kind === TokenKind.TAP_TEST_POINT,
|
|
);
|
|
|
|
const plan = planEntry.node;
|
|
|
|
const planStart = NumberParseInt(plan.start, 10);
|
|
const planEnd = NumberParseInt(plan.end, 10);
|
|
|
|
if (planEnd === 0 && testPointEntries.length > 0) {
|
|
throw new ERR_TAP_VALIDATION_ERROR(
|
|
`found ${testPointEntries.length} Test Point${
|
|
testPointEntries.length > 1 ? 's' : ''
|
|
} but plan is ${planStart}..0`,
|
|
);
|
|
}
|
|
|
|
if (planEnd > 0) {
|
|
if (testPointEntries.length === 0) {
|
|
throw new ERR_TAP_VALIDATION_ERROR('missing Test Points');
|
|
}
|
|
|
|
if (!bailoutEntry && testPointEntries.length !== planEnd) {
|
|
throw new ERR_TAP_VALIDATION_ERROR(
|
|
`test Points count ${testPointEntries.length} does not match plan count ${planEnd}`,
|
|
);
|
|
}
|
|
|
|
for (let i = 0; i < testPointEntries.length; i++) {
|
|
const test = testPointEntries[i].node;
|
|
const testId = NumberParseInt(test.id, 10);
|
|
|
|
if (testId < planStart || testId > planEnd) {
|
|
throw new ERR_TAP_VALIDATION_ERROR(
|
|
`test ${testId} is out of plan range ${planStart}..${planEnd}`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TAP14 and TAP13 are compatible with each other
|
|
class TAP13ValidationStrategy extends TAPValidationStrategy {}
|
|
class TAP14ValidationStrategy extends TAPValidationStrategy {}
|
|
|
|
class TapChecker {
|
|
static TAP13 = '13';
|
|
static TAP14 = '14';
|
|
|
|
constructor({ specs }) {
|
|
switch (specs) {
|
|
case TapChecker.TAP13:
|
|
this.strategy = new TAP13ValidationStrategy();
|
|
break;
|
|
default:
|
|
this.strategy = new TAP14ValidationStrategy();
|
|
}
|
|
}
|
|
|
|
check(ast) {
|
|
return this.strategy.validate(ast);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
TapChecker,
|
|
TAP14ValidationStrategy,
|
|
TAP13ValidationStrategy,
|
|
};
|