mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1039 lines
32 KiB
1039 lines
32 KiB
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
'use strict';
|
|
const common = require('../common');
|
|
const assert = require('assert');
|
|
const util = require('util');
|
|
const vm = require('vm');
|
|
|
|
assert.strictEqual(util.inspect(1), '1');
|
|
assert.strictEqual(util.inspect(false), 'false');
|
|
assert.strictEqual(util.inspect(''), "''");
|
|
assert.strictEqual(util.inspect('hello'), "'hello'");
|
|
assert.strictEqual(util.inspect(function() {}), '[Function]');
|
|
assert.strictEqual(util.inspect(() => {}), '[Function]');
|
|
assert.strictEqual(util.inspect(async function() {}), '[AsyncFunction]');
|
|
assert.strictEqual(util.inspect(async () => {}), '[AsyncFunction]');
|
|
assert.strictEqual(util.inspect(function*() {}), '[GeneratorFunction]');
|
|
assert.strictEqual(util.inspect(undefined), 'undefined');
|
|
assert.strictEqual(util.inspect(null), 'null');
|
|
assert.strictEqual(util.inspect(/foo(bar\n)?/gi), '/foo(bar\\n)?/gi');
|
|
assert.strictEqual(
|
|
util.inspect(new Date('Sun, 14 Feb 2010 11:48:40 GMT')),
|
|
new Date('2010-02-14T12:48:40+01:00').toISOString()
|
|
);
|
|
assert.strictEqual(util.inspect(new Date('')), (new Date('')).toString());
|
|
|
|
assert.strictEqual(util.inspect('\n\u0001'), "'\\n\\u0001'");
|
|
|
|
assert.strictEqual(util.inspect([]), '[]');
|
|
assert.strictEqual(util.inspect(Object.create([])), 'Array {}');
|
|
assert.strictEqual(util.inspect([1, 2]), '[ 1, 2 ]');
|
|
assert.strictEqual(util.inspect([1, [2, 3]]), '[ 1, [ 2, 3 ] ]');
|
|
|
|
assert.strictEqual(util.inspect({}), '{}');
|
|
assert.strictEqual(util.inspect({a: 1}), '{ a: 1 }');
|
|
assert.strictEqual(util.inspect({a: function() {}}), '{ a: [Function: a] }');
|
|
assert.strictEqual(util.inspect({a: () => {}}), '{ a: [Function: a] }');
|
|
assert.strictEqual(util.inspect({a: async function() {}}),
|
|
'{ a: [AsyncFunction: a] }');
|
|
assert.strictEqual(util.inspect({a: async () => {}}),
|
|
'{ a: [AsyncFunction: a] }');
|
|
assert.strictEqual(util.inspect({a: function*() {}}),
|
|
'{ a: [GeneratorFunction: a] }');
|
|
assert.strictEqual(util.inspect({a: 1, b: 2}), '{ a: 1, b: 2 }');
|
|
assert.strictEqual(util.inspect({'a': {}}), '{ a: {} }');
|
|
assert.strictEqual(util.inspect({'a': {'b': 2}}), '{ a: { b: 2 } }');
|
|
assert.strictEqual(util.inspect({'a': {'b': { 'c': { 'd': 2 }}}}),
|
|
'{ a: { b: { c: [Object] } } }');
|
|
assert.strictEqual(util.inspect({'a': {'b': { 'c': { 'd': 2 }}}}, false, null),
|
|
'{ a: { b: { c: { d: 2 } } } }');
|
|
assert.strictEqual(util.inspect([1, 2, 3], true), '[ 1, 2, 3, [length]: 3 ]');
|
|
assert.strictEqual(util.inspect({'a': {'b': { 'c': 2}}}, false, 0),
|
|
'{ a: [Object] }');
|
|
assert.strictEqual(util.inspect({'a': {'b': { 'c': 2}}}, false, 1),
|
|
'{ a: { b: [Object] } }');
|
|
assert.strictEqual(util.inspect({'a': {'b': ['c']}}, false, 1),
|
|
'{ a: { b: [Array] } }');
|
|
assert.strictEqual(util.inspect(Object.create({},
|
|
{visible: {value: 1, enumerable: true}, hidden: {value: 2}})),
|
|
'{ visible: 1 }'
|
|
);
|
|
assert.strictEqual(util.inspect(Object.assign(new String('hello'),
|
|
{ [Symbol('foo')]: 123 }), { showHidden: true }),
|
|
'{ [String: \'hello\'] [length]: 5, [Symbol(foo)]: 123 }');
|
|
|
|
assert.strictEqual(util.inspect(process.stdin._handle._externalStream),
|
|
'[External]');
|
|
|
|
{
|
|
const regexp = /regexp/;
|
|
regexp.aprop = 42;
|
|
assert.strictEqual(util.inspect({a: regexp}, false, 0), '{ a: /regexp/ }');
|
|
}
|
|
|
|
assert(/Object/.test(
|
|
util.inspect({a: {a: {a: {a: {}}}}}, undefined, undefined, true)
|
|
));
|
|
assert(!/Object/.test(
|
|
util.inspect({a: {a: {a: {a: {}}}}}, undefined, null, true)
|
|
));
|
|
|
|
for (const showHidden of [true, false]) {
|
|
const ab = new ArrayBuffer(4);
|
|
const dv = new DataView(ab, 1, 2);
|
|
assert.strictEqual(
|
|
util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4 }'
|
|
);
|
|
assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4 } }');
|
|
assert.strictEqual(
|
|
util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4 }'
|
|
);
|
|
assert.strictEqual(util.inspect(dv, showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4 } }');
|
|
ab.x = 42;
|
|
dv.y = 1337;
|
|
assert.strictEqual(util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4, x: 42 }');
|
|
assert.strictEqual(util.inspect(dv, showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' +
|
|
' y: 1337 }');
|
|
}
|
|
|
|
// Now do the same checks but from a different context
|
|
for (const showHidden of [true, false]) {
|
|
const ab = vm.runInNewContext('new ArrayBuffer(4)');
|
|
const dv = vm.runInNewContext('new DataView(ab, 1, 2)', { ab: ab });
|
|
assert.strictEqual(
|
|
util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4 }'
|
|
);
|
|
assert.strictEqual(util.inspect(new DataView(ab, 1, 2), showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4 } }');
|
|
assert.strictEqual(
|
|
util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4 }'
|
|
);
|
|
assert.strictEqual(util.inspect(dv, showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4 } }');
|
|
ab.x = 42;
|
|
dv.y = 1337;
|
|
assert.strictEqual(util.inspect(ab, showHidden),
|
|
'ArrayBuffer { byteLength: 4, x: 42 }');
|
|
assert.strictEqual(util.inspect(dv, showHidden),
|
|
'DataView {\n' +
|
|
' byteLength: 2,\n' +
|
|
' byteOffset: 1,\n' +
|
|
' buffer: ArrayBuffer { byteLength: 4, x: 42 },\n' +
|
|
' y: 1337 }');
|
|
}
|
|
|
|
|
|
[ Float32Array,
|
|
Float64Array,
|
|
Int16Array,
|
|
Int32Array,
|
|
Int8Array,
|
|
Uint16Array,
|
|
Uint32Array,
|
|
Uint8Array,
|
|
Uint8ClampedArray ].forEach((constructor) => {
|
|
const length = 2;
|
|
const byteLength = length * constructor.BYTES_PER_ELEMENT;
|
|
const array = new constructor(new ArrayBuffer(byteLength), 0, length);
|
|
array[0] = 65;
|
|
array[1] = 97;
|
|
assert.strictEqual(
|
|
util.inspect(array, true),
|
|
`${constructor.name} [\n` +
|
|
' 65,\n' +
|
|
' 97,\n' +
|
|
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
|
|
` [length]: ${length},\n` +
|
|
` [byteLength]: ${byteLength},\n` +
|
|
' [byteOffset]: 0,\n' +
|
|
` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`);
|
|
assert.strictEqual(
|
|
util.inspect(array, false),
|
|
`${constructor.name} [ 65, 97 ]`
|
|
);
|
|
});
|
|
|
|
// Now check that declaring a TypedArray in a different context works the same
|
|
[ Float32Array,
|
|
Float64Array,
|
|
Int16Array,
|
|
Int32Array,
|
|
Int8Array,
|
|
Uint16Array,
|
|
Uint32Array,
|
|
Uint8Array,
|
|
Uint8ClampedArray ].forEach((constructor) => {
|
|
const length = 2;
|
|
const byteLength = length * constructor.BYTES_PER_ELEMENT;
|
|
const array = vm.runInNewContext(
|
|
'new constructor(new ArrayBuffer(byteLength), 0, length)',
|
|
{ constructor, byteLength, length }
|
|
);
|
|
array[0] = 65;
|
|
array[1] = 97;
|
|
assert.strictEqual(
|
|
util.inspect(array, true),
|
|
`${constructor.name} [\n` +
|
|
' 65,\n' +
|
|
' 97,\n' +
|
|
` [BYTES_PER_ELEMENT]: ${constructor.BYTES_PER_ELEMENT},\n` +
|
|
` [length]: ${length},\n` +
|
|
` [byteLength]: ${byteLength},\n` +
|
|
' [byteOffset]: 0,\n' +
|
|
` [buffer]: ArrayBuffer { byteLength: ${byteLength} } ]`);
|
|
assert.strictEqual(
|
|
util.inspect(array, false),
|
|
`${constructor.name} [ 65, 97 ]`
|
|
);
|
|
});
|
|
|
|
// Due to the hash seed randomization it's not deterministic the order that
|
|
// the following ways this hash is displayed.
|
|
// See http://codereview.chromium.org/9124004/
|
|
|
|
{
|
|
const out = util.inspect(Object.create({},
|
|
{visible: {value: 1, enumerable: true}, hidden: {value: 2}}), true);
|
|
if (out !== '{ [hidden]: 2, visible: 1 }' &&
|
|
out !== '{ visible: 1, [hidden]: 2 }') {
|
|
assert.fail(`unexpected value for out ${out}`);
|
|
}
|
|
}
|
|
|
|
// Objects without prototype
|
|
{
|
|
const out = util.inspect(Object.create(null,
|
|
{ name: {value: 'Tim',
|
|
enumerable: true},
|
|
hidden: {value: 'secret'}}), true);
|
|
if (out !== "{ [hidden]: 'secret', name: 'Tim' }" &&
|
|
out !== "{ name: 'Tim', [hidden]: 'secret' }") {
|
|
assert.fail(`unexpected value for out ${out}`);
|
|
}
|
|
}
|
|
|
|
assert.strictEqual(
|
|
util.inspect(Object.create(null,
|
|
{name: {value: 'Tim', enumerable: true},
|
|
hidden: {value: 'secret'}})),
|
|
'{ name: \'Tim\' }'
|
|
);
|
|
|
|
|
|
// Dynamic properties
|
|
{
|
|
assert.strictEqual(util.inspect({get readonly() {}}),
|
|
'{ readonly: [Getter] }');
|
|
|
|
assert.strictEqual(util.inspect({get readwrite() {}, set readwrite(val) {}}),
|
|
'{ readwrite: [Getter/Setter] }');
|
|
|
|
assert.strictEqual(util.inspect({set writeonly(val) {}}),
|
|
'{ writeonly: [Setter] }');
|
|
|
|
const value = {};
|
|
value['a'] = value;
|
|
assert.strictEqual(util.inspect(value), '{ a: [Circular] }');
|
|
}
|
|
|
|
// Array with dynamic properties
|
|
{
|
|
const value = [1, 2, 3];
|
|
Object.defineProperty(
|
|
value,
|
|
'growingLength',
|
|
{
|
|
enumerable: true,
|
|
get: function() { this.push(true); return this.length; }
|
|
}
|
|
);
|
|
assert.strictEqual(util.inspect(value),
|
|
'[ 1, 2, 3, growingLength: [Getter] ]');
|
|
}
|
|
|
|
// Function with properties
|
|
{
|
|
const value = function() {};
|
|
value.aprop = 42;
|
|
assert.strictEqual(util.inspect(value), '{ [Function: value] aprop: 42 }');
|
|
}
|
|
|
|
// Anonymous function with properties
|
|
{
|
|
const value = (() => function() {})();
|
|
value.aprop = 42;
|
|
assert.strictEqual(util.inspect(value), '{ [Function] aprop: 42 }');
|
|
}
|
|
|
|
// Regular expressions with properties
|
|
{
|
|
const value = /123/ig;
|
|
value.aprop = 42;
|
|
assert.strictEqual(util.inspect(value), '{ /123/gi aprop: 42 }');
|
|
}
|
|
|
|
// Dates with properties
|
|
{
|
|
const value = new Date('Sun, 14 Feb 2010 11:48:40 GMT');
|
|
value.aprop = 42;
|
|
assert.strictEqual(util.inspect(value),
|
|
'{ 2010-02-14T11:48:40.000Z aprop: 42 }');
|
|
}
|
|
|
|
// test the internal isDate implementation
|
|
{
|
|
const Date2 = vm.runInNewContext('Date');
|
|
const d = new Date2();
|
|
const orig = util.inspect(d);
|
|
Date2.prototype.foo = 'bar';
|
|
const after = util.inspect(d);
|
|
assert.strictEqual(orig, after);
|
|
}
|
|
|
|
// test positive/negative zero
|
|
assert.strictEqual(util.inspect(0), '0');
|
|
assert.strictEqual(util.inspect(-0), '-0');
|
|
|
|
// test for sparse array
|
|
{
|
|
const a = ['foo', 'bar', 'baz'];
|
|
assert.strictEqual(util.inspect(a), '[ \'foo\', \'bar\', \'baz\' ]');
|
|
delete a[1];
|
|
assert.strictEqual(util.inspect(a), '[ \'foo\', <1 empty item>, \'baz\' ]');
|
|
assert.strictEqual(
|
|
util.inspect(a, true),
|
|
'[ \'foo\', <1 empty item>, \'baz\', [length]: 3 ]'
|
|
);
|
|
assert.strictEqual(util.inspect(new Array(5)), '[ <5 empty items> ]');
|
|
a[3] = 'bar';
|
|
a[100] = 'qux';
|
|
assert.strictEqual(
|
|
util.inspect(a, { breakLength: Infinity }),
|
|
'[ \'foo\', <1 empty item>, \'baz\', \'bar\', <96 empty items>, \'qux\' ]'
|
|
);
|
|
}
|
|
|
|
// test for Array constructor in different context
|
|
{
|
|
const Debug = vm.runInDebugContext('Debug');
|
|
const map = new Map();
|
|
map.set(1, 2);
|
|
const mirror = Debug.MakeMirror(map.entries(), true);
|
|
const vals = mirror.preview();
|
|
const valsOutput = [];
|
|
for (const o of vals) {
|
|
valsOutput.push(o);
|
|
}
|
|
|
|
assert.strictEqual(util.inspect(valsOutput), '[ [ 1, 2 ] ]');
|
|
}
|
|
|
|
// test for other constructors in different context
|
|
{
|
|
let obj = vm.runInNewContext('(function(){return {}})()', {});
|
|
assert.strictEqual(util.inspect(obj), '{}');
|
|
obj = vm.runInNewContext('var m=new Map();m.set(1,2);m', {});
|
|
assert.strictEqual(util.inspect(obj), 'Map { 1 => 2 }');
|
|
obj = vm.runInNewContext('var s=new Set();s.add(1);s.add(2);s', {});
|
|
assert.strictEqual(util.inspect(obj), 'Set { 1, 2 }');
|
|
obj = vm.runInNewContext('fn=function(){};new Promise(fn,fn)', {});
|
|
assert.strictEqual(util.inspect(obj), 'Promise { <pending> }');
|
|
}
|
|
|
|
// test for property descriptors
|
|
{
|
|
const getter = Object.create(null, {
|
|
a: {
|
|
get: function() { return 'aaa'; }
|
|
}
|
|
});
|
|
const setter = Object.create(null, {
|
|
b: {
|
|
set: function() {}
|
|
}
|
|
});
|
|
const getterAndSetter = Object.create(null, {
|
|
c: {
|
|
get: function() { return 'ccc'; },
|
|
set: function() {}
|
|
}
|
|
});
|
|
assert.strictEqual(util.inspect(getter, true), '{ [a]: [Getter] }');
|
|
assert.strictEqual(util.inspect(setter, true), '{ [b]: [Setter] }');
|
|
assert.strictEqual(
|
|
util.inspect(getterAndSetter, true),
|
|
'{ [c]: [Getter/Setter] }'
|
|
);
|
|
}
|
|
|
|
// exceptions should print the error message, not '{}'
|
|
{
|
|
const errors = [];
|
|
errors.push(new Error());
|
|
errors.push(new Error('FAIL'));
|
|
errors.push(new TypeError('FAIL'));
|
|
errors.push(new SyntaxError('FAIL'));
|
|
errors.forEach((err) => {
|
|
assert.strictEqual(util.inspect(err), err.stack);
|
|
});
|
|
try {
|
|
undef(); // eslint-disable-line no-undef
|
|
} catch (e) {
|
|
assert.strictEqual(util.inspect(e), e.stack);
|
|
}
|
|
const ex = util.inspect(new Error('FAIL'), true);
|
|
assert(ex.includes('Error: FAIL'));
|
|
assert(ex.includes('[stack]'));
|
|
assert(ex.includes('[message]'));
|
|
}
|
|
|
|
// Doesn't capture stack trace
|
|
{
|
|
function BadCustomError(msg) {
|
|
Error.call(this);
|
|
Object.defineProperty(this, 'message',
|
|
{ value: msg, enumerable: false });
|
|
Object.defineProperty(this, 'name',
|
|
{ value: 'BadCustomError', enumerable: false });
|
|
}
|
|
util.inherits(BadCustomError, Error);
|
|
assert.strictEqual(
|
|
util.inspect(new BadCustomError('foo')),
|
|
'[BadCustomError: foo]'
|
|
);
|
|
}
|
|
|
|
// GH-1941
|
|
// should not throw:
|
|
assert.strictEqual(util.inspect(Object.create(Date.prototype)), 'Date {}');
|
|
|
|
// GH-1944
|
|
assert.doesNotThrow(() => {
|
|
const d = new Date();
|
|
d.toUTCString = null;
|
|
util.inspect(d);
|
|
});
|
|
|
|
assert.doesNotThrow(() => {
|
|
const d = new Date();
|
|
d.toISOString = null;
|
|
util.inspect(d);
|
|
});
|
|
|
|
assert.doesNotThrow(() => {
|
|
const r = /regexp/;
|
|
r.toString = null;
|
|
util.inspect(r);
|
|
});
|
|
|
|
// bug with user-supplied inspect function returns non-string
|
|
assert.doesNotThrow(() => {
|
|
util.inspect([{
|
|
inspect: () => 123
|
|
}]);
|
|
});
|
|
|
|
// GH-2225
|
|
{
|
|
const x = { inspect: util.inspect };
|
|
assert.strictEqual(util.inspect(x).includes('inspect'), true);
|
|
}
|
|
|
|
// util.inspect should not display the escaped value of a key.
|
|
{
|
|
const w = {
|
|
'\\': 1,
|
|
'\\\\': 2,
|
|
'\\\\\\': 3,
|
|
'\\\\\\\\': 4,
|
|
};
|
|
|
|
const y = ['a', 'b', 'c'];
|
|
y['\\\\\\'] = 'd';
|
|
|
|
assert.strictEqual(
|
|
util.inspect(w),
|
|
'{ \'\\\': 1, \'\\\\\': 2, \'\\\\\\\': 3, \'\\\\\\\\\': 4 }'
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(y),
|
|
'[ \'a\', \'b\', \'c\', \'\\\\\\\': \'d\' ]'
|
|
);
|
|
}
|
|
|
|
// util.inspect.styles and util.inspect.colors
|
|
{
|
|
function testColorStyle(style, input, implicit) {
|
|
const colorName = util.inspect.styles[style];
|
|
let color = ['', ''];
|
|
if (util.inspect.colors[colorName])
|
|
color = util.inspect.colors[colorName];
|
|
|
|
const withoutColor = util.inspect(input, false, 0, false);
|
|
const withColor = util.inspect(input, false, 0, true);
|
|
const expect = '\u001b[' + color[0] + 'm' + withoutColor +
|
|
'\u001b[' + color[1] + 'm';
|
|
assert.strictEqual(
|
|
withColor,
|
|
expect,
|
|
`util.inspect color for style ${style}`);
|
|
}
|
|
|
|
testColorStyle('special', function() {});
|
|
testColorStyle('number', 123.456);
|
|
testColorStyle('boolean', true);
|
|
testColorStyle('undefined', undefined);
|
|
testColorStyle('null', null);
|
|
testColorStyle('string', 'test string');
|
|
testColorStyle('date', new Date());
|
|
testColorStyle('regexp', /regexp/);
|
|
}
|
|
|
|
// an object with "hasOwnProperty" overwritten should not throw
|
|
assert.doesNotThrow(() => {
|
|
util.inspect({
|
|
hasOwnProperty: null
|
|
});
|
|
});
|
|
|
|
// new API, accepts an "options" object
|
|
{
|
|
const subject = { foo: 'bar', hello: 31, a: { b: { c: { d: 0 } } } };
|
|
Object.defineProperty(subject, 'hidden', { enumerable: false, value: null });
|
|
|
|
assert.strictEqual(
|
|
util.inspect(subject, { showHidden: false }).includes('hidden'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { showHidden: true }).includes('hidden'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { colors: false }).includes('\u001b[32m'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { colors: true }).includes('\u001b[32m'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { depth: 2 }).includes('c: [Object]'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { depth: 0 }).includes('a: [Object]'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { depth: null }).includes('{ d: 0 }'),
|
|
true
|
|
);
|
|
}
|
|
|
|
{
|
|
// "customInspect" option can enable/disable calling inspect() on objects
|
|
const subject = { inspect: () => 123 };
|
|
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: true }).includes('123'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: true }).includes('inspect'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: false }).includes('123'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: false }).includes('inspect'),
|
|
true
|
|
);
|
|
|
|
// custom inspect() functions should be able to return other Objects
|
|
subject.inspect = () => ({ foo: 'bar' });
|
|
|
|
assert.strictEqual(util.inspect(subject), '{ foo: \'bar\' }');
|
|
|
|
subject.inspect = (depth, opts) => {
|
|
assert.strictEqual(opts.customInspectOptions, true);
|
|
};
|
|
|
|
util.inspect(subject, { customInspectOptions: true });
|
|
}
|
|
|
|
{
|
|
// "customInspect" option can enable/disable calling [util.inspect.custom]()
|
|
const subject = { [util.inspect.custom]: () => 123 };
|
|
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: true }).includes('123'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: false }).includes('123'),
|
|
false
|
|
);
|
|
|
|
// a custom [util.inspect.custom]() should be able to return other Objects
|
|
subject[util.inspect.custom] = () => ({ foo: 'bar' });
|
|
|
|
assert.strictEqual(util.inspect(subject), '{ foo: \'bar\' }');
|
|
|
|
subject[util.inspect.custom] = (depth, opts) => {
|
|
assert.strictEqual(opts.customInspectOptions, true);
|
|
};
|
|
|
|
util.inspect(subject, { customInspectOptions: true });
|
|
}
|
|
|
|
{
|
|
// [util.inspect.custom] takes precedence over inspect
|
|
const subject = {
|
|
[util.inspect.custom]() { return 123; },
|
|
inspect() { return 456; }
|
|
};
|
|
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: true }).includes('123'),
|
|
true
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: false }).includes('123'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: true }).includes('456'),
|
|
false
|
|
);
|
|
assert.strictEqual(
|
|
util.inspect(subject, { customInspect: false }).includes('456'),
|
|
false
|
|
);
|
|
}
|
|
|
|
{
|
|
// Returning `this` from a custom inspection function works.
|
|
assert.strictEqual(util.inspect({ a: 123, inspect() { return this; } }),
|
|
'{ a: 123, inspect: [Function: inspect] }');
|
|
|
|
const subject = { a: 123, [util.inspect.custom]() { return this; } };
|
|
const UIC = 'util.inspect.custom';
|
|
assert.strictEqual(util.inspect(subject),
|
|
`{ a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]] }`);
|
|
}
|
|
|
|
// util.inspect with "colors" option should produce as many lines as without it
|
|
{
|
|
function testLines(input) {
|
|
const countLines = (str) => (str.match(/\n/g) || []).length;
|
|
const withoutColor = util.inspect(input);
|
|
const withColor = util.inspect(input, {colors: true});
|
|
assert.strictEqual(countLines(withoutColor), countLines(withColor));
|
|
}
|
|
|
|
const bigArray = new Array(100).fill().map((value, index) => index);
|
|
|
|
testLines([1, 2, 3, 4, 5, 6, 7]);
|
|
testLines(bigArray);
|
|
testLines({foo: 'bar', baz: 35, b: {a: 35}});
|
|
testLines({
|
|
foo: 'bar',
|
|
baz: 35,
|
|
b: {a: 35},
|
|
veryLongKey: 'very long value',
|
|
evenLongerKey: ['with even longer value in array']
|
|
});
|
|
}
|
|
|
|
// test boxed primitives output the correct values
|
|
assert.strictEqual(util.inspect(new String('test')), '[String: \'test\']');
|
|
assert.strictEqual(
|
|
util.inspect(Object(Symbol('test'))),
|
|
'[Symbol: Symbol(test)]'
|
|
);
|
|
assert.strictEqual(util.inspect(new Boolean(false)), '[Boolean: false]');
|
|
assert.strictEqual(util.inspect(new Boolean(true)), '[Boolean: true]');
|
|
assert.strictEqual(util.inspect(new Number(0)), '[Number: 0]');
|
|
assert.strictEqual(util.inspect(new Number(-0)), '[Number: -0]');
|
|
assert.strictEqual(util.inspect(new Number(-1.1)), '[Number: -1.1]');
|
|
assert.strictEqual(util.inspect(new Number(13.37)), '[Number: 13.37]');
|
|
|
|
// test boxed primitives with own properties
|
|
{
|
|
const str = new String('baz');
|
|
str.foo = 'bar';
|
|
assert.strictEqual(util.inspect(str), '{ [String: \'baz\'] foo: \'bar\' }');
|
|
|
|
const bool = new Boolean(true);
|
|
bool.foo = 'bar';
|
|
assert.strictEqual(util.inspect(bool), '{ [Boolean: true] foo: \'bar\' }');
|
|
|
|
const num = new Number(13.37);
|
|
num.foo = 'bar';
|
|
assert.strictEqual(util.inspect(num), '{ [Number: 13.37] foo: \'bar\' }');
|
|
}
|
|
|
|
// test es6 Symbol
|
|
if (typeof Symbol !== 'undefined') {
|
|
assert.strictEqual(util.inspect(Symbol()), 'Symbol()');
|
|
assert.strictEqual(util.inspect(Symbol(123)), 'Symbol(123)');
|
|
assert.strictEqual(util.inspect(Symbol('hi')), 'Symbol(hi)');
|
|
assert.strictEqual(util.inspect([Symbol()]), '[ Symbol() ]');
|
|
assert.strictEqual(util.inspect({ foo: Symbol() }), '{ foo: Symbol() }');
|
|
|
|
const options = { showHidden: true };
|
|
let subject = {};
|
|
|
|
subject[Symbol('symbol')] = 42;
|
|
|
|
assert.strictEqual(util.inspect(subject), '{ [Symbol(symbol)]: 42 }');
|
|
assert.strictEqual(
|
|
util.inspect(subject, options),
|
|
'{ [Symbol(symbol)]: 42 }'
|
|
);
|
|
|
|
Object.defineProperty(
|
|
subject,
|
|
Symbol(),
|
|
{enumerable: false, value: 'non-enum'});
|
|
assert.strictEqual(util.inspect(subject), '{ [Symbol(symbol)]: 42 }');
|
|
assert.strictEqual(
|
|
util.inspect(subject, options),
|
|
'{ [Symbol(symbol)]: 42, [Symbol()]: \'non-enum\' }'
|
|
);
|
|
|
|
subject = [1, 2, 3];
|
|
subject[Symbol('symbol')] = 42;
|
|
|
|
assert.strictEqual(util.inspect(subject),
|
|
'[ 1, 2, 3, [Symbol(symbol)]: 42 ]');
|
|
}
|
|
|
|
// test Set
|
|
{
|
|
assert.strictEqual(util.inspect(new Set()), 'Set {}');
|
|
assert.strictEqual(util.inspect(new Set([1, 2, 3])), 'Set { 1, 2, 3 }');
|
|
const set = new Set(['foo']);
|
|
set.bar = 42;
|
|
assert.strictEqual(
|
|
util.inspect(set, true),
|
|
'Set { \'foo\', [size]: 1, bar: 42 }'
|
|
);
|
|
}
|
|
|
|
// test Map
|
|
{
|
|
assert.strictEqual(util.inspect(new Map()), 'Map {}');
|
|
assert.strictEqual(util.inspect(new Map([[1, 'a'], [2, 'b'], [3, 'c']])),
|
|
'Map { 1 => \'a\', 2 => \'b\', 3 => \'c\' }');
|
|
const map = new Map([['foo', null]]);
|
|
map.bar = 42;
|
|
assert.strictEqual(util.inspect(map, true),
|
|
'Map { \'foo\' => null, [size]: 1, bar: 42 }');
|
|
}
|
|
|
|
// test Promise
|
|
{
|
|
const resolved = Promise.resolve(3);
|
|
assert.strictEqual(util.inspect(resolved), 'Promise { 3 }');
|
|
|
|
const rejected = Promise.reject(3);
|
|
assert.strictEqual(util.inspect(rejected), 'Promise { <rejected> 3 }');
|
|
// squelch UnhandledPromiseRejection
|
|
rejected.catch(common.noop);
|
|
|
|
const pending = new Promise(common.noop);
|
|
assert.strictEqual(util.inspect(pending), 'Promise { <pending> }');
|
|
|
|
const promiseWithProperty = Promise.resolve('foo');
|
|
promiseWithProperty.bar = 42;
|
|
assert.strictEqual(util.inspect(promiseWithProperty),
|
|
'Promise { \'foo\', bar: 42 }');
|
|
}
|
|
|
|
// Make sure it doesn't choke on polyfills. Unlike Set/Map, there is no standard
|
|
// interface to synchronously inspect a Promise, so our techniques only work on
|
|
// a bonafide native Promise.
|
|
{
|
|
const oldPromise = Promise;
|
|
global.Promise = function() { this.bar = 42; };
|
|
assert.strictEqual(util.inspect(new Promise()), '{ bar: 42 }');
|
|
global.Promise = oldPromise;
|
|
}
|
|
|
|
// Test Map iterators
|
|
{
|
|
const map = new Map([['foo', 'bar']]);
|
|
assert.strictEqual(util.inspect(map.keys()), 'MapIterator { \'foo\' }');
|
|
assert.strictEqual(util.inspect(map.values()), 'MapIterator { \'bar\' }');
|
|
assert.strictEqual(util.inspect(map.entries()),
|
|
'MapIterator { [ \'foo\', \'bar\' ] }');
|
|
// make sure the iterator doesn't get consumed
|
|
const keys = map.keys();
|
|
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
|
|
assert.strictEqual(util.inspect(keys), 'MapIterator { \'foo\' }');
|
|
}
|
|
|
|
// Test Set iterators
|
|
{
|
|
const aSet = new Set([1, 3]);
|
|
assert.strictEqual(util.inspect(aSet.keys()), 'SetIterator { 1, 3 }');
|
|
assert.strictEqual(util.inspect(aSet.values()), 'SetIterator { 1, 3 }');
|
|
assert.strictEqual(util.inspect(aSet.entries()),
|
|
'SetIterator { [ 1, 1 ], [ 3, 3 ] }');
|
|
// make sure the iterator doesn't get consumed
|
|
const keys = aSet.keys();
|
|
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
|
|
assert.strictEqual(util.inspect(keys), 'SetIterator { 1, 3 }');
|
|
}
|
|
|
|
// Test alignment of items in container
|
|
// Assumes that the first numeric character is the start of an item.
|
|
{
|
|
function checkAlignment(container) {
|
|
const lines = util.inspect(container).split('\n');
|
|
let pos;
|
|
lines.forEach((line) => {
|
|
const npos = line.search(/\d/);
|
|
if (npos !== -1) {
|
|
if (pos !== undefined) {
|
|
assert.strictEqual(pos, npos, 'container items not aligned');
|
|
}
|
|
pos = npos;
|
|
}
|
|
});
|
|
}
|
|
|
|
const bigArray = [];
|
|
for (let i = 0; i < 100; i++) {
|
|
bigArray.push(i);
|
|
}
|
|
|
|
const obj = {};
|
|
bigArray.forEach((prop) => {
|
|
obj[prop] = null;
|
|
});
|
|
|
|
checkAlignment(bigArray);
|
|
checkAlignment(obj);
|
|
checkAlignment(new Set(bigArray));
|
|
checkAlignment(new Map(bigArray.map((number) => [number, null])));
|
|
}
|
|
|
|
|
|
// Test display of constructors
|
|
{
|
|
class ObjectSubclass {}
|
|
class ArraySubclass extends Array {}
|
|
class SetSubclass extends Set {}
|
|
class MapSubclass extends Map {}
|
|
class PromiseSubclass extends Promise {}
|
|
|
|
const x = new ObjectSubclass();
|
|
x.foo = 42;
|
|
assert.strictEqual(util.inspect(x),
|
|
'ObjectSubclass { foo: 42 }');
|
|
assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)),
|
|
'ArraySubclass [ 1, 2, 3 ]');
|
|
assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])),
|
|
'SetSubclass { 1, 2, 3 }');
|
|
assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])),
|
|
'MapSubclass { \'foo\' => 42 }');
|
|
assert.strictEqual(util.inspect(new PromiseSubclass(common.noop)),
|
|
'PromiseSubclass { <pending> }');
|
|
}
|
|
|
|
// Corner cases.
|
|
{
|
|
const x = { constructor: 42 };
|
|
assert.strictEqual(util.inspect(x), '{ constructor: 42 }');
|
|
}
|
|
|
|
{
|
|
const x = {};
|
|
Object.defineProperty(x, 'constructor', {
|
|
get: function() {
|
|
throw new Error('should not access constructor');
|
|
},
|
|
enumerable: true
|
|
});
|
|
assert.strictEqual(util.inspect(x), '{ constructor: [Getter] }');
|
|
}
|
|
|
|
{
|
|
const x = new function() {}; // eslint-disable-line new-parens
|
|
assert.strictEqual(util.inspect(x), '{}');
|
|
}
|
|
|
|
{
|
|
const x = Object.create(null);
|
|
assert.strictEqual(util.inspect(x), '{}');
|
|
}
|
|
|
|
// The following maxArrayLength tests were introduced after v6.0.0 was released.
|
|
// Do not backport to v5/v4 unless all of
|
|
// https://github.com/nodejs/node/pull/6334 is backported.
|
|
{
|
|
const x = new Array(101).fill();
|
|
assert(util.inspect(x).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Array(101).fill();
|
|
assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Array(101).fill();
|
|
assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
|
|
'[ ... 101 more items ]');
|
|
}
|
|
|
|
{
|
|
const x = Array(101);
|
|
assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
|
|
'[ ... 101 more items ]');
|
|
}
|
|
|
|
{
|
|
const x = new Uint8Array(101);
|
|
assert(util.inspect(x).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Uint8Array(101);
|
|
assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Uint8Array(101);
|
|
assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
|
|
'Uint8Array [ ... 101 more items ]');
|
|
}
|
|
|
|
{
|
|
const x = Array(101);
|
|
assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = Array(101);
|
|
assert(!util.inspect(
|
|
x, { maxArrayLength: Infinity }
|
|
).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Uint8Array(101);
|
|
assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const x = new Uint8Array(101);
|
|
assert(!util.inspect(
|
|
x, { maxArrayLength: Infinity }
|
|
).endsWith('1 more item ]'));
|
|
}
|
|
|
|
{
|
|
const obj = {foo: 'abc', bar: 'xyz'};
|
|
const oneLine = util.inspect(obj, {breakLength: Infinity});
|
|
// Subtract four for the object's two curly braces and two spaces of padding.
|
|
// Add one more to satisfy the strictly greater than condition in the code.
|
|
const breakpoint = oneLine.length - 5;
|
|
const twoLines = util.inspect(obj, {breakLength: breakpoint});
|
|
|
|
assert.strictEqual(oneLine, '{ foo: \'abc\', bar: \'xyz\' }');
|
|
assert.strictEqual(oneLine, util.inspect(obj, {breakLength: breakpoint + 1}));
|
|
assert.strictEqual(twoLines, '{ foo: \'abc\',\n bar: \'xyz\' }');
|
|
}
|
|
|
|
// util.inspect.defaultOptions tests
|
|
{
|
|
const arr = new Array(101).fill();
|
|
const obj = {a: {a: {a: {a: 1}}}};
|
|
|
|
const oldOptions = Object.assign({}, util.inspect.defaultOptions);
|
|
|
|
// Set single option through property assignment
|
|
util.inspect.defaultOptions.maxArrayLength = null;
|
|
assert(!/1 more item/.test(util.inspect(arr)));
|
|
util.inspect.defaultOptions.maxArrayLength = oldOptions.maxArrayLength;
|
|
assert(/1 more item/.test(util.inspect(arr)));
|
|
util.inspect.defaultOptions.depth = null;
|
|
assert(!/Object/.test(util.inspect(obj)));
|
|
util.inspect.defaultOptions.depth = oldOptions.depth;
|
|
assert(/Object/.test(util.inspect(obj)));
|
|
assert.strictEqual(
|
|
JSON.stringify(util.inspect.defaultOptions),
|
|
JSON.stringify(oldOptions)
|
|
);
|
|
|
|
// Set multiple options through object assignment
|
|
util.inspect.defaultOptions = {maxArrayLength: null, depth: null};
|
|
assert(!/1 more item/.test(util.inspect(arr)));
|
|
assert(!/Object/.test(util.inspect(obj)));
|
|
util.inspect.defaultOptions = oldOptions;
|
|
assert(/1 more item/.test(util.inspect(arr)));
|
|
assert(/Object/.test(util.inspect(obj)));
|
|
assert.strictEqual(
|
|
JSON.stringify(util.inspect.defaultOptions),
|
|
JSON.stringify(oldOptions)
|
|
);
|
|
|
|
assert.throws(() => {
|
|
util.inspect.defaultOptions = null;
|
|
}, /"options" must be an object/);
|
|
|
|
assert.throws(() => {
|
|
util.inspect.defaultOptions = 'bad';
|
|
}, /"options" must be an object/);
|
|
}
|
|
|
|
assert.doesNotThrow(() => util.inspect(process));
|
|
|