Skip to content

Commit cb71d5c

Browse files
committed
test_runner: use v8.serialize instead of TAP
1 parent af9b48a commit cb71d5c

17 files changed

+261
-4605
lines changed

doc/api/cli.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2319,7 +2319,8 @@ on unsupported platforms will not be fixed.
23192319
### `NODE_TEST_CONTEXT=value`
23202320

23212321
If `value` equals `'child'`, test reporter options will be overridden and test
2322-
output will be sent to stdout in the TAP format.
2322+
output will be sent to stdout in the TAP format. If any other value is provided,
2323+
Node.js makes no guarantees about the reporter format used or its stability.
23232324

23242325
### `NODE_TLS_REJECT_UNAUTHORIZED=value`
23252326

lib/internal/error_serdes.js

+45-7
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,24 @@ const {
1313
ObjectGetOwnPropertyNames,
1414
ObjectGetPrototypeOf,
1515
ObjectKeys,
16+
ObjectPrototypeHasOwnProperty,
1617
ObjectPrototypeToString,
1718
RangeError,
1819
ReferenceError,
1920
SafeSet,
2021
SymbolToStringTag,
2122
SyntaxError,
23+
SymbolFor,
2224
TypeError,
2325
URIError,
2426
} = primordials;
27+
const { inspect: { custom: customInspectSymbol } } = require('util');
2528

2629
const kSerializedError = 0;
2730
const kSerializedObject = 1;
2831
const kInspectedError = 2;
32+
const kInspectedSymbol = 3;
33+
const kCustomInspecteObject = 4;
2934

3035
const errors = {
3136
Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError,
@@ -52,7 +57,13 @@ function TryGetAllProperties(object, target = object) {
5257
// Continue regardless of error.
5358
}
5459
}
55-
if ('value' in descriptor && typeof descriptor.value !== 'function') {
60+
if (key === 'cause') {
61+
delete descriptor.get;
62+
delete descriptor.set;
63+
descriptor.value = serializeError(descriptor.value);
64+
all[key] = descriptor;
65+
} else if ('value' in descriptor &&
66+
typeof descriptor.value !== 'function' && typeof descriptor.value !== 'symbol') {
5667
delete descriptor.get;
5768
delete descriptor.set;
5869
all[key] = descriptor;
@@ -95,6 +106,10 @@ function inspect(...args) {
95106
let serialize;
96107
function serializeError(error) {
97108
if (!serialize) serialize = require('v8').serialize;
109+
if (typeof error === 'symbol') {
110+
return Buffer.concat([Buffer.from([kInspectedSymbol]),
111+
Buffer.from(inspect(error), 'utf8')]);
112+
}
98113
try {
99114
if (typeof error === 'object' &&
100115
ObjectPrototypeToString(error) === '[object Error]') {
@@ -113,6 +128,15 @@ function serializeError(error) {
113128
} catch {
114129
// Continue regardless of error.
115130
}
131+
try {
132+
if (error != null &&
133+
ObjectPrototypeHasOwnProperty(error, customInspectSymbol)) {
134+
return Buffer.concat([Buffer.from([kCustomInspecteObject]),
135+
Buffer.from(inspect(error), 'utf8')]);
136+
}
137+
} catch {
138+
// Continue regardless of error.
139+
}
116140
try {
117141
const serialized = serialize(error);
118142
return Buffer.concat([Buffer.from([kSerializedObject]), serialized]);
@@ -123,6 +147,12 @@ function serializeError(error) {
123147
Buffer.from(inspect(error), 'utf8')]);
124148
}
125149

150+
function fromBuffer(error) {
151+
return Buffer.from(error.buffer,
152+
error.byteOffset + 1,
153+
error.byteLength - 1);
154+
}
155+
126156
let deserialize;
127157
function deserializeError(error) {
128158
if (!deserialize) deserialize = require('v8').deserialize;
@@ -132,19 +162,27 @@ function deserializeError(error) {
132162
const ctor = errors[constructor];
133163
ObjectDefineProperty(properties, SymbolToStringTag, {
134164
__proto__: null,
135-
value: { value: 'Error', configurable: true },
165+
value: { __proto__: null, value: 'Error', configurable: true },
136166
enumerable: true,
137167
});
168+
if ('cause' in properties && 'value' in properties.cause) {
169+
properties.cause.value = deserializeError(properties.cause.value);
170+
}
138171
return ObjectCreate(ctor.prototype, properties);
139172
}
140173
case kSerializedObject:
141174
return deserialize(error.subarray(1));
142-
case kInspectedError: {
143-
const buf = Buffer.from(error.buffer,
144-
error.byteOffset + 1,
145-
error.byteLength - 1);
146-
return buf.toString('utf8');
175+
case kInspectedError:
176+
return fromBuffer(error).toString('utf8');
177+
case kInspectedSymbol: {
178+
const buf = fromBuffer(error);
179+
return SymbolFor(buf.toString('utf8').substring('Symbol('.length, buf.length - 1));
147180
}
181+
case kCustomInspecteObject:
182+
return {
183+
__proto__: null,
184+
[customInspectSymbol]: () => fromBuffer(error).toString('utf8'),
185+
};
148186
}
149187
require('assert').fail('This should not happen');
150188
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const { DefaultSerializer } = require('v8');
4+
const { Buffer } = require('buffer');
5+
const { serializeError } = require('internal/error_serdes');
6+
7+
8+
module.exports = async function* v8Reporter(source) {
9+
const serializer = new DefaultSerializer();
10+
11+
for await (const item of source) {
12+
const originalError = item.data.details?.error;
13+
if (originalError) {
14+
item.data.details.error = serializeError(originalError);
15+
}
16+
// Add 4 bytes, to later populate with message length
17+
serializer.writeRawBytes(Buffer.allocUnsafe(4));
18+
serializer.writeHeader();
19+
serializer.writeValue(item);
20+
21+
if (originalError) {
22+
item.data.details.error = originalError;
23+
}
24+
25+
const serializedMessage = serializer.releaseBuffer();
26+
const serializedMessageLength = serializedMessage.length - 4;
27+
28+
serializedMessage.set([
29+
serializedMessageLength >> 24 & 0xFF,
30+
serializedMessageLength >> 16 & 0xFF,
31+
serializedMessageLength >> 8 & 0xFF,
32+
serializedMessageLength & 0xFF,
33+
], 0);
34+
yield serializedMessage;
35+
}
36+
};

0 commit comments

Comments
 (0)