Skip to content

Commit 87a0e86

Browse files
MoLowjuanarbol
authored andcommitted
test_runner: parse yaml
PR-URL: #45815 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 874f6c3 commit 87a0e86

File tree

6 files changed

+785
-19
lines changed

6 files changed

+785
-19
lines changed

lib/internal/test_runner/runner.js

+3-15
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const {
44
ArrayPrototypeFilter,
55
ArrayPrototypeForEach,
66
ArrayPrototypeIncludes,
7-
ArrayPrototypeJoin,
87
ArrayPrototypePush,
98
ArrayPrototypeSlice,
109
ArrayPrototypeSort,
@@ -35,6 +34,7 @@ const { kEmptyObject } = require('internal/util');
3534
const { createTestTree } = require('internal/test_runner/harness');
3635
const { kDefaultIndent, kSubtestsFailed, Test } = require('internal/test_runner/test');
3736
const { TapParser } = require('internal/test_runner/tap_parser');
37+
const { YAMLToJs } = require('internal/test_runner/yaml_parser');
3838
const { TokenKind } = require('internal/test_runner/tap_lexer');
3939

4040
const {
@@ -129,18 +129,6 @@ class FileTest extends Test {
129129
#handleReportItem({ kind, node, nesting = 0 }) {
130130
const indent = StringPrototypeRepeat(kDefaultIndent, nesting + 1);
131131

132-
const details = (diagnostic) => {
133-
return (
134-
diagnostic && {
135-
__proto__: null,
136-
yaml:
137-
`${indent} ` +
138-
ArrayPrototypeJoin(diagnostic, `\n${indent} `) +
139-
'\n',
140-
}
141-
);
142-
};
143-
144132
switch (kind) {
145133
case TokenKind.TAP_VERSION:
146134
// TODO(manekinekko): handle TAP version coming from the parser.
@@ -174,15 +162,15 @@ class FileTest extends Test {
174162
indent,
175163
node.id,
176164
node.description,
177-
details(node.diagnostics),
165+
YAMLToJs(node.diagnostics),
178166
directive
179167
);
180168
} else {
181169
this.reporter.fail(
182170
indent,
183171
node.id,
184172
node.description,
185-
details(node.diagnostics),
173+
YAMLToJs(node.diagnostics),
186174
directive
187175
);
188176
}

lib/internal/test_runner/tap_stream.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,10 @@ class TapStream extends Readable {
8383
}
8484

8585
#details(indent, data = kEmptyObject) {
86-
const { error, duration, yaml } = data;
86+
const { error, duration_ms } = data;
8787
let details = `${indent} ---\n`;
8888

89-
details += `${yaml ? yaml : ''}`;
90-
details += jsToYaml(indent, 'duration_ms', duration);
89+
details += jsToYaml(indent, 'duration_ms', duration_ms);
9190
details += jsToYaml(indent, null, error);
9291
details += `${indent} ...\n`;
9392
this.#tryPush(details);

lib/internal/test_runner/test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ class Test extends AsyncResource {
680680
this.reportSubtest();
681681
}
682682
let directive;
683-
const details = { __proto__: null, duration: this.#duration() };
683+
const details = { __proto__: null, duration_ms: this.#duration() };
684684

685685
if (this.skipped) {
686686
directive = this.reporter.getSkip(this.message);
+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
'use strict';
2+
const {
3+
codes: {
4+
ERR_TEST_FAILURE,
5+
}
6+
} = require('internal/errors');
7+
const AssertionError = require('internal/assert/assertion_error');
8+
const {
9+
ArrayPrototypeJoin,
10+
ArrayPrototypePush,
11+
Error,
12+
Number,
13+
NumberIsNaN,
14+
RegExpPrototypeExec,
15+
StringPrototypeEndsWith,
16+
StringPrototypeRepeat,
17+
StringPrototypeSlice,
18+
StringPrototypeStartsWith,
19+
StringPrototypeSubstring,
20+
} = primordials;
21+
22+
const kYamlKeyRegex = /^(\s+)?(\w+):(\s)+([>|][-+])?(.*)$/;
23+
const kStackDelimiter = ' at ';
24+
25+
function reConstructError(parsedYaml) {
26+
if (!('error' in parsedYaml)) {
27+
return parsedYaml;
28+
}
29+
const isAssertionError = parsedYaml.code === 'ERR_ASSERTION' ||
30+
'actual' in parsedYaml || 'expected' in parsedYaml || 'operator' in parsedYaml;
31+
const isTestFailure = parsedYaml.code === 'ERR_TEST_FAILURE' || 'failureType' in parsedYaml;
32+
const stack = parsedYaml.stack ? kStackDelimiter + ArrayPrototypeJoin(parsedYaml.stack, `\n${kStackDelimiter}`) : '';
33+
let error, cause;
34+
35+
if (isAssertionError) {
36+
cause = new AssertionError({
37+
message: parsedYaml.error,
38+
actual: parsedYaml.actual,
39+
expected: parsedYaml.expected,
40+
operator: parsedYaml.operator
41+
});
42+
} else {
43+
// eslint-disable-next-line no-restricted-syntax
44+
cause = new Error(parsedYaml.error);
45+
cause.code = parsedYaml.code;
46+
}
47+
cause.stack = stack;
48+
49+
if (isTestFailure) {
50+
error = new ERR_TEST_FAILURE(cause, parsedYaml.failureType);
51+
error.stack = stack;
52+
}
53+
54+
parsedYaml.error = error ?? cause;
55+
delete parsedYaml.stack;
56+
delete parsedYaml.code;
57+
delete parsedYaml.failureType;
58+
delete parsedYaml.actual;
59+
delete parsedYaml.expected;
60+
delete parsedYaml.operator;
61+
62+
return parsedYaml;
63+
}
64+
65+
function getYamlValue(value) {
66+
if (StringPrototypeStartsWith(value, "'") && StringPrototypeEndsWith(value, "'")) {
67+
return StringPrototypeSlice(value, 1, -1);
68+
}
69+
if (value === 'true') {
70+
return true;
71+
}
72+
if (value === 'false') {
73+
return false;
74+
}
75+
if (value !== '') {
76+
const valueAsNumber = Number(value);
77+
return NumberIsNaN(valueAsNumber) ? value : valueAsNumber;
78+
}
79+
return value;
80+
}
81+
82+
// This parses the YAML generated by the built-in TAP reporter,
83+
// which is a subset of the full YAML spec. There are some
84+
// YAML features that won't be parsed here. This function should not be exposed publicly.
85+
function YAMLToJs(lines) {
86+
if (lines == null) {
87+
return undefined;
88+
}
89+
const result = { __proto__: null };
90+
let isInYamlBlock = false;
91+
for (let i = 0; i < lines.length; i++) {
92+
const line = lines[i];
93+
if (isInYamlBlock && !StringPrototypeStartsWith(line, StringPrototypeRepeat(' ', isInYamlBlock.indent))) {
94+
result[isInYamlBlock.key] = isInYamlBlock.key === 'stack' ?
95+
result[isInYamlBlock.key] : ArrayPrototypeJoin(result[isInYamlBlock.key], '\n');
96+
isInYamlBlock = false;
97+
}
98+
if (isInYamlBlock) {
99+
const blockLine = StringPrototypeSubstring(line, isInYamlBlock.indent);
100+
ArrayPrototypePush(result[isInYamlBlock.key], blockLine);
101+
continue;
102+
}
103+
const match = RegExpPrototypeExec(kYamlKeyRegex, line);
104+
if (match !== null) {
105+
const { 1: leadingSpaces, 2: key, 4: block, 5: value } = match;
106+
if (block) {
107+
isInYamlBlock = { key, indent: (leadingSpaces?.length ?? 0) + 2 };
108+
result[key] = [];
109+
} else {
110+
result[key] = getYamlValue(value);
111+
}
112+
}
113+
}
114+
return reConstructError(result);
115+
}
116+
117+
module.exports = {
118+
YAMLToJs,
119+
};
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Flags: --no-warnings
2+
'use strict';
3+
require('../common');
4+
const spawn = require('node:child_process').spawn;
5+
spawn(process.execPath,
6+
['--no-warnings', '--test', 'test/message/test_runner_output.js'], { stdio: 'inherit' });

0 commit comments

Comments
 (0)