From 4186b8be414be54753caab5e3665259c855be3c2 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Sat, 5 Feb 2022 18:47:46 +0200 Subject: [PATCH] stream: initial port of test262 tests PR-URL: https://github.com/nodejs/node/pull/41775 Reviewed-By: Nitzan Uziely Reviewed-By: James M Snell --- lib/internal/streams/operators.js | 6 +- lib/stream.js | 33 ++++- ...-stream-iterator-helpers-test262-tests.mjs | 115 ++++++++++++++++++ 3 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 test/parallel/test-stream-iterator-helpers-test262-tests.mjs diff --git a/lib/internal/streams/operators.js b/lib/internal/streams/operators.js index 6dd6e015eed..a6efa92afb7 100644 --- a/lib/internal/streams/operators.js +++ b/lib/internal/streams/operators.js @@ -166,7 +166,7 @@ function map(fn, options) { }.call(this); } -function asIndexedPairs(options) { +function asIndexedPairs(options = undefined) { if (options != null && typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', ['Object']); } @@ -350,7 +350,7 @@ function toIntegerOrInfinity(number) { return number; } -function drop(number, options) { +function drop(number, options = undefined) { if (options != null && typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', ['Object']); } @@ -374,7 +374,7 @@ function drop(number, options) { }.call(this); } -function take(number, options) { +function take(number, options = undefined) { if (options != null && typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', ['Object']); } diff --git a/lib/stream.js b/lib/stream.js index 2c3261123af..9794bdb45b4 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -35,6 +35,12 @@ const { streamReturningOperators, promiseReturningOperators, } = require('internal/streams/operators'); + +const { + codes: { + ERR_ILLEGAL_CONSTRUCTOR, + }, +} = require('internal/errors'); const compose = require('internal/streams/compose'); const { pipeline } = require('internal/streams/pipeline'); const { destroyer } = require('internal/streams/destroy'); @@ -51,15 +57,34 @@ Stream.isReadable = utils.isReadable; Stream.Readable = require('internal/streams/readable'); for (const key of ObjectKeys(streamReturningOperators)) { const op = streamReturningOperators[key]; - Stream.Readable.prototype[key] = function(...args) { + function fn(...args) { + if (new.target) { + throw ERR_ILLEGAL_CONSTRUCTOR(); + } return Stream.Readable.from(ReflectApply(op, this, args)); - }; + } + ObjectDefineProperty(fn, 'name', { value: op.name }); + ObjectDefineProperty(fn, 'length', { value: op.length }); + ObjectDefineProperty(Stream.Readable.prototype, key, { + value: fn, + enumerable: false, + configurable: true, + writable: false, + }); } for (const key of ObjectKeys(promiseReturningOperators)) { const op = promiseReturningOperators[key]; - Stream.Readable.prototype[key] = function(...args) { + function fn(...args) { return ReflectApply(op, this, args); - }; + } + ObjectDefineProperty(fn, 'name', { value: op.name }); + ObjectDefineProperty(fn, 'length', { value: op.length }); + ObjectDefineProperty(Stream.Readable.prototype, key, { + value: fn, + enumerable: false, + configurable: true, + writable: false, + }); } Stream.Writable = require('internal/streams/writable'); Stream.Duplex = require('internal/streams/duplex'); diff --git a/test/parallel/test-stream-iterator-helpers-test262-tests.mjs b/test/parallel/test-stream-iterator-helpers-test262-tests.mjs new file mode 100644 index 00000000000..d2e3a8bae02 --- /dev/null +++ b/test/parallel/test-stream-iterator-helpers-test262-tests.mjs @@ -0,0 +1,115 @@ +import '../common/index.mjs'; +import { Readable } from 'stream'; +import assert from 'assert'; + +// These tests are manually ported from the draft PR for the test262 test suite +// Authored by Rick Waldron in https://github.com/tc39/test262/pull/2818/files + +// test262 license: +// The << Software identified by reference to the Ecma Standard* ("Software)">> +// is protected by copyright and is being made available under the +// "BSD License", included below. This Software may be subject to third party +// rights (rights from parties other than Ecma International), including patent +// rights, and no licenses under such third party rights are granted under this +// license even if the third party concerned is a member of Ecma International. +// SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT +// http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION +// REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA +// INTERNATIONAL STANDARDS* + +// Copyright (C) 2012-2013 Ecma International +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the authors nor Ecma International may be used to +// endorse or promote products derived from this software without specific +// prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +// NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// * Ecma International Standards hereafter means Ecma International Standards +// as well as Ecma Technical Reports + + +// Note all the tests that check AsyncIterator's prototype itself and things +// that happen before stream conversion were not ported. +{ + // asIndexedPairs/is-function + assert.strictEqual(typeof Readable.prototype.asIndexedPairs, 'function'); + // asIndexedPairs/indexed-pairs.js + const iterator = Readable.from([0, 1]); + const indexedPairs = iterator.asIndexedPairs(); + + for await (const [i, v] of indexedPairs) { + assert.strictEqual(i, v); + } + // asIndexedPairs/length.js + assert.strictEqual(Readable.prototype.asIndexedPairs.length, 0); + // asIndexedPairs/name.js + assert.strictEqual(Readable.prototype.asIndexedPairs.name, 'asIndexedPairs'); + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'asIndexedPairs' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, false); +} +{ + // drop/length + assert.strictEqual(Readable.prototype.drop.length, 1); + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'drop' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, false); + // drop/limit-equals-total + const iterator = Readable.from([1, 2]).drop(2); + const result = await iterator[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result, { done: true, value: undefined }); + // drop/limit-greater-than-total.js + const iterator2 = Readable.from([1, 2]).drop(3); + const result2 = await iterator2[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result2, { done: true, value: undefined }); + // drop/limit-less-than-total.js + const iterator3 = Readable.from([1, 2]).drop(1); + const result3 = await iterator3[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result3, { done: false, value: 2 }); + // drop/limit-rangeerror + assert.throws(() => Readable.from([1]).drop(-1), RangeError); + assert.throws(() => { + Readable.from([1]).drop({ + valueOf() { + throw new Error('boom'); + } + }); + }, /boom/); + // drop/limit-tointeger + const two = await Readable.from([1, 2]).drop({ valueOf: () => 1 }).toArray(); + assert.deepStrictEqual(two, [2]); + // drop/name + assert.strictEqual(Readable.prototype.drop.name, 'drop'); + // drop/non-constructible + assert.throws(() => new Readable.prototype.drop(1), TypeError); + // drop/proto + const proto = Object.getPrototypeOf(Readable.prototype.drop); + assert.strictEqual(proto, Function.prototype); + +}