// Copyright (C) 2025 André Bargull. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | Utility functions for testing Iterator.prototype.zip and Iterator.prototype.zipKeyed. Requires inclusion of propertyHelper.js. defines: - forEachSequenceCombination - forEachSequenceCombinationKeyed - assertZipped - assertZippedKeyed - assertIteratorResult - assertIsPackedArray ---*/ // Assert |result| is an object created by CreateIteratorResultObject. function assertIteratorResult(result, value, done, label) { assert.sameValue( Object.getPrototypeOf(result), Object.prototype, label + ": [[Prototype]] of iterator result is Object.prototype" ); assert(Object.isExtensible(result), label + ": iterator result is extensible"); var ownKeys = Reflect.ownKeys(result); assert.compareArray(ownKeys, ["value", "done"], label + ": iterator result properties"); 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, label) { assert(Array.isArray(array), label + ": array is an array exotic object"); assert.sameValue( Object.getPrototypeOf(array), Array.prototype, label + ": [[Prototype]] of array is Array.prototype" ); assert(Object.isExtensible(array), label + ": 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, }); } } // Assert |array| is an extensible null-prototype object with default property attributes. function _assertIsNullProtoMutableObject(object, label) { assert.sameValue( Object.getPrototypeOf(object), null, label + ": [[Prototype]] of object is null" ); assert(Object.isExtensible(object), label + ": object is extensible"); // Ensure all properties have the default property attributes. var keys = Object.getOwnPropertyNames(object); for (var i = 0; i < keys.length; i++) { verifyProperty(object, keys[i], { writable: true, enumerable: true, configurable: true, }); } } // Assert that the `zipped` iterator yields the first `count` outputs of Iterator.zip. // Assumes `inputs` is an array of arrays, each with length >= `count`. // Advances `zipped` by `count` steps. function assertZipped(zipped, inputs, count, label) { // Last returned elements array. var last = null; for (var i = 0; i < count; i++) { var itemLabel = label + ", step " + i; var result = zipped.next(); var value = result.value; // Test IteratorResult structure. assertIteratorResult(result, value, false, itemLabel); // Ensure value is a new array. assert.notSameValue(value, last, itemLabel + ": returns a new array"); last = value; // Ensure all array elements have the expected value. var expected = inputs.map(function (array) { return array[i]; }); assert.compareArray(value, expected, itemLabel + ": values"); // Ensure value is a packed array with default data properties. assertIsPackedArray(value, itemLabel); } } // Assert that the `zipped` iterator yields the first `count` outputs of Iterator.zipKeyed. // Assumes `inputs` is an object whose values are arrays, each with length >= `count`. // Advances `zipped` by `count` steps. function assertZippedKeyed(zipped, inputs, count, label) { // Last returned elements array. var last = null; var expectedKeys = Object.keys(inputs); for (var i = 0; i < count; i++) { var itemLabel = label + ", step " + i; var result = zipped.next(); var value = result.value; // Test IteratorResult structure. assertIteratorResult(result, value, false, itemLabel); // Ensure resulting object is a new object. assert.notSameValue(value, last, itemLabel + ": returns a new object"); last = value; // Ensure resulting object has the expected keys and values. assert.compareArray(Reflect.ownKeys(value), expectedKeys, itemLabel + ": result object keys"); var expectedValues = Object.values(inputs).map(function (array) { return array[i]; }); assert.compareArray(Object.values(value), expectedValues, itemLabel + ": result object values"); // Ensure resulting object is a null-prototype mutable object with default data properties. _assertIsNullProtoMutableObject(value, itemLabel); } } function forEachSequenceCombinationKeyed(callback) { return forEachSequenceCombination(function(inputs, inputsLabel, min, max) { var object = {}; for (var i = 0; i < inputs.length; ++i) { object["prop_" + i] = inputs[i]; } inputsLabel = "inputs = " + JSON.stringify(object); callback(object, inputsLabel, min, max); }); } function forEachSequenceCombination(callback) { function test(inputs) { if (inputs.length === 0) { callback(inputs, "inputs = []", 0, 0); return; } var lengths = inputs.map(function(array) { return array.length; }); var min = Math.min.apply(null, lengths); var max = Math.max.apply(null, lengths); var inputsLabel = "inputs = " + JSON.stringify(inputs); callback(inputs, inputsLabel, min, max); } // Yield all prefixes of the string |s|. function* prefixes(s) { for (var i = 0; i <= s.length; ++i) { yield s.slice(0, i); } } // Zip an empty iterable. test([]); // Zip a single iterator. for (var prefix of prefixes("abcd")) { test([prefix.split("")]); } // Zip two iterators. for (var prefix1 of prefixes("abcd")) { for (var prefix2 of prefixes("efgh")) { test([prefix1.split(""), prefix2.split("")]); } } // Zip three iterators. for (var prefix1 of prefixes("abcd")) { for (var prefix2 of prefixes("efgh")) { for (var prefix3 of prefixes("ijkl")) { test([prefix1.split(""), prefix2.split(""), prefix3.split("")]); } } } }