2025-11-25 13:06:50 -08:00

237 lines
6.1 KiB
JavaScript

// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Basic Iterator.zip test using all three possible modes.
includes: [compareArray.js, propertyHelper.js]
features: [joint-iteration]
---*/
// Assert |result| is an object created by CreateIteratorResultObject.
function assertIteratorResult(result, value, done) {
assert.sameValue(
Object.getPrototypeOf(result),
Object.prototype,
"[[Prototype]] of iterator result is Object.prototype"
);
assert(Object.isExtensible(result), "iterator result is extensible");
var ownKeys = Reflect.ownKeys(result);
assert.sameValue(ownKeys.length, 2, "iterator result has two own properties");
assert.sameValue(ownKeys[0], "value", "first property is 'value'");
assert.sameValue(ownKeys[1], "done", "second property is 'done'");
verifyProperty(result, "value", {
value: value,
writable: true,
enumerable: true,
configurable: true,
});
verifyProperty(result, "done", {
value: done,
writable: true,
enumerable: true,
configurable: true,
});
}
// Assert |array| is a packed array with default property attributes.
function assertIsPackedArray(array) {
assert(Array.isArray(array), "array is an array exotic object");
assert.sameValue(
Object.getPrototypeOf(array),
Array.prototype,
"[[Prototype]] of array is Array.prototype"
);
assert(Object.isExtensible(array), "array is extensible");
// Ensure "length" property has its default property attributes.
verifyProperty(array, "length", {
writable: true,
enumerable: false,
configurable: false,
});
// Ensure no holes and all elements have the default property attributes.
for (var i = 0; i < array.length; i++) {
verifyProperty(array, i, {
writable: true,
enumerable: true,
configurable: true,
});
}
}
// Yield all prefixes of the string |s|.
function* prefixes(s) {
for (var i = 0; i <= s.length; ++i) {
yield s.slice(0, i);
}
}
// Empty iterable doesn't yield any values.
var empty = {
*[Symbol.iterator]() {
}
};
// Yield a single value.
var single = {
*[Symbol.iterator]() {
yield 1000;
}
};
// Yield an infinite amount of numbers.
var numbers = {
*[Symbol.iterator]() {
var i = 0;
while (true) {
yield 100 + i++;
}
}
};
// |iterables| is an array whose elements are array(-like) objects. Pass it as
// the "iterables" argument to |Iterator.zip|, using |options| as the "options"
// argument.
//
// Then iterate over the returned |Iterator.zip| iterator and check all
// returned iteration values have the expected values.
function test(iterables, options) {
var mode = (options && options.mode) || "shortest";
var padding = options && options.padding;
var lengths = iterables.map(function(array) {
return array.length;
});
var min = Math.min.apply(null, lengths);
var max = Math.max.apply(null, lengths);
// Expected number of iterations.
var count;
switch (mode) {
case "shortest":
count = min;
break;
case "longest":
count = max;
break;
case "strict":
count = max;
break;
}
// Compute padding array when |mode| is "longest".
if (mode === "longest") {
if (padding) {
padding = Iterator.from(padding).take(iterables.length).toArray();
} else {
padding = [];
}
// Fill with undefined until there are exactly |iterables.length| elements.
padding = padding.concat(Array(iterables.length - padding.length).fill(undefined));
assert.sameValue(padding.length, iterables.length);
}
// Last returned elements array.
var last = null;
var it = Iterator.zip(iterables, options);
for (var i = 0; i < count; i++) {
// "strict" mode throws an error if number of elements don't match.
if (mode === "strict" && min < max && i === min) {
assert.throws(TypeError, function() {
it.next();
});
break;
}
var result = it.next();
var value = result.value;
// Test IteratorResult structure.
assertIteratorResult(result, value, false);
// Ensure value is a new array.
assert.notSameValue(value, last, "returns a new array");
last = value;
// Ensure all array elements have the expected value.
var expected = iterables.map(function(array, k) {
if (i < array.length) {
return array[i];
}
assert.sameValue(mode, "longest", "padding is only used for 'longest' mode");
return padding[k];
});
assert.compareArray(value, expected);
// Ensure value is a packed array with default data properties.
//
// This operation is destructive, so it has to happen last.
assertIsPackedArray(value);
}
// Iterator is closed.
assertIteratorResult(it.next(), undefined, true);
}
var validOptions = [
undefined,
{},
{mode: "shortest"},
{mode: "longest"},
{mode: "longest", padding: empty},
{mode: "longest", padding: single},
{mode: "longest", padding: numbers},
{mode: "strict"},
];
for (var options of validOptions) {
// Zip an empty iterable.
var it = Iterator.zip([], options);
assertIteratorResult(it.next(), undefined, true);
// Zip a single iterator.
for (var prefix of prefixes("abcd")) {
// Split prefix into an array.
test([prefix.split("")], options);
// Use String wrapper as the iterable.
test([new String(prefix)], options);
}
// Zip two iterators.
for (var prefix1 of prefixes("abcd")) {
for (var prefix2 of prefixes("efgh")) {
// Split prefixes into arrays.
test([prefix1.split(""), prefix2.split("")], options);
// Use String wrappers as the iterables.
test([new String(prefix1), new String(prefix2)], options);
}
}
// Zip three iterators.
for (var prefix1 of prefixes("abcd")) {
for (var prefix2 of prefixes("efgh")) {
for (var prefix3 of prefixes("ijkl")) {
// Split prefixes into arrays.
test([prefix1.split(""), prefix2.split(""), prefix3.split("")], options);
// Use String wrappers as the iterables.
test([new String(prefix1), new String(prefix2), new String(prefix3)], options);
}
}
}
}