зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1809662 - Part 5: Add tests for new Set methods. r=dminor
The tests are partially based on the old tests. Test262 tests are still in progress. Differential Revision: https://phabricator.services.mozilla.com/D166561
This commit is contained in:
Родитель
89da60a76f
Коммит
d7b5956390
|
@ -1,4 +1,462 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.difference)
|
||||
|
||||
assertEq(typeof Set.prototype.difference, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.difference, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.difference, "name"), {
|
||||
value: "difference", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Difference of two empty sets is an empty set.
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(emptySet), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(emptySetLike), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(emptyMap), []);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Difference with an empty set.
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(emptySet), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(emptySetLike), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(emptyMap), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(new Set(values)), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(new SetLike(values)), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(asMap(values)), []);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(new Set(values)), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(new SetLike(values)), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).difference(asMap(values)), []);
|
||||
|
||||
// Difference of the same set object.
|
||||
let set = new Set(values);
|
||||
assertSetContainsExactOrderedItems(set.difference(set), []);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Input has more elements than the this-value.
|
||||
sizeValue = 1;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Input has fewer elements than the this-value.
|
||||
sizeValue = 0;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(new Set([1]).difference(setLike), [1]);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.difference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.difference([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.difference(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.difference(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.difference(emptySet), []);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertSetContainsExactOrderedItems(Set.prototype.difference.call(set, emptySet), [1, 2, 3]);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.difference.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't return the original Set object.
|
||||
{
|
||||
let set = new Set([1]);
|
||||
assertEq(set.difference(emptySet) !== set, true);
|
||||
assertEq(set.difference(new Set([2])) !== set, true);
|
||||
}
|
||||
|
||||
// Test insertion order
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
// Case 1: Input is empty.
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([])), [1, 2, 3]);
|
||||
|
||||
// Case 2: Input has fewer elements.
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([1, 2])), [3]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([2, 1])), [3]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([11, 2])), [1, 3]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([2, 11])), [1, 3]);
|
||||
|
||||
// Case 3: Input has same number of elements.
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([1, 2, 3])), []);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 1])), []);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([3, 2, 1])), []);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([11, 2, 3])), [1]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 11])), [1]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([3, 2, 11])), [1]);
|
||||
|
||||
// Case 4: Input has more elements.
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([2, 3, 4, 5])), [1]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([4, 5, 2, 3])), [1]);
|
||||
assertSetContainsExactOrderedItems(set.difference(new Set([5, 4, 3, 2])), [1]);
|
||||
}
|
||||
|
||||
// Calls |has| when the this-value has fewer or the same number of keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [keys.length, 100, Infinity]) {
|
||||
let i = 0;
|
||||
|
||||
let setLike = {
|
||||
size,
|
||||
has(v) {
|
||||
assertEq(this, setLike);
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(i < keys.length, true);
|
||||
assertEq(v, keys[i++]);
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertSetContainsExactOrderedItems(new Set(keys).difference(setLike), []);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls |keys| when the this-value has more keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [0, 1, 2]) {
|
||||
let i = 0;
|
||||
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
keys() {
|
||||
assertEq(this, setLike);
|
||||
assertEq(arguments.length, 0);
|
||||
|
||||
let iterator = {
|
||||
next() {
|
||||
assertEq(this, iterator);
|
||||
assertEq(arguments.length, 0);
|
||||
if (i < keys.length) {
|
||||
return {
|
||||
done: false,
|
||||
value: keys[i++],
|
||||
};
|
||||
}
|
||||
return {
|
||||
done: true,
|
||||
get value() {
|
||||
throw new Error("Unexpected call to |value| getter");
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return iterator;
|
||||
},
|
||||
};
|
||||
|
||||
assertSetContainsExactOrderedItems(new Set(keys).difference(setLike), []);
|
||||
}
|
||||
}
|
||||
|
||||
// Test result set order when the this-value was modified.
|
||||
{
|
||||
let originalKeys = null;
|
||||
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
if (!originalKeys) {
|
||||
assertSetContainsExactOrderedItems(set, [1, 2, 3, 4]);
|
||||
|
||||
originalKeys = [...set.keys()];
|
||||
|
||||
// Remove all existing items.
|
||||
set.clear();
|
||||
|
||||
// Add new keys 11 and 22.
|
||||
set.add(11);
|
||||
set.add(22);
|
||||
}
|
||||
|
||||
// |has| is called exactly once for each key.
|
||||
assertEq(originalKeys.includes(v), true);
|
||||
|
||||
originalKeys.splice(originalKeys.indexOf(v), 1);
|
||||
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
let set = new Set([1, 2, 3, 4]);
|
||||
|
||||
assertSetContainsExactOrderedItems(set.difference(setLike), []);
|
||||
assertSetContainsExactOrderedItems(set, [11, 22]);
|
||||
assertEqArray(originalKeys, []);
|
||||
}
|
||||
{
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
*keys() {
|
||||
assertSetContainsExactOrderedItems(set, [1, 2, 3, 4]);
|
||||
|
||||
let originalKeys = [...set.keys()];
|
||||
|
||||
// Remove all existing items.
|
||||
set.clear();
|
||||
|
||||
// Add new keys 11 and 22.
|
||||
set.add(11);
|
||||
set.add(22);
|
||||
|
||||
// Yield the original keys of |set|.
|
||||
yield* originalKeys;
|
||||
},
|
||||
};
|
||||
|
||||
let set = new Set([1, 2, 3, 4]);
|
||||
|
||||
assertSetContainsExactOrderedItems(set.difference(setLike), []);
|
||||
assertSetContainsExactOrderedItems(set, [11, 22]);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.difference(other), []);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,478 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.intersection)
|
||||
|
||||
assertEq(typeof Set.prototype.intersection, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.intersection, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.intersection, "name"), {
|
||||
value: "intersection", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Intersection of two empty sets is an empty set.
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(emptySet), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(emptySetLike), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(emptyMap), []);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Intersection with an empty set.
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(emptySet), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(emptySetLike), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(emptyMap), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(new Set(values)), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(new SetLike(values)), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(asMap(values)), []);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(new Set(values)), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(new SetLike(values)), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).intersection(asMap(values)), values);
|
||||
|
||||
// Intersection of the same set object.
|
||||
let set = new Set(values);
|
||||
assertSetContainsExactOrderedItems(set.intersection(set), values);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Input has more elements than the this-value.
|
||||
sizeValue = 1;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Input has fewer elements than the this-value.
|
||||
sizeValue = 0;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(new Set([1]).intersection(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.intersection(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.intersection([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.intersection(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.intersection(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.intersection(emptySet), []);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertSetContainsExactOrderedItems(Set.prototype.intersection.call(set, emptySet), []);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.intersection.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't return the original Set object.
|
||||
{
|
||||
let set = new Set([1]);
|
||||
assertEq(set.intersection(emptySet) !== set, true);
|
||||
assertEq(set.intersection(new Set([2])) !== set, true);
|
||||
}
|
||||
|
||||
// Test insertion order
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
// Case 1: Input is empty.
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([])), []);
|
||||
|
||||
// Case 2: Input has fewer elements.
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([1, 2])), [1, 2]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([2, 1])), [2, 1]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([11, 2])), [2]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([2, 11])), [2]);
|
||||
|
||||
// Case 3: Input has same number of elements.
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([1, 2, 3])), [1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 1])), [1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([3, 2, 1])), [1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([11, 2, 3])), [2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 11])), [2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([3, 2, 11])), [2, 3]);
|
||||
|
||||
// Case 4: Input has more elements.
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([2, 3, 4, 5])), [2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([4, 5, 2, 3])), [2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(new Set([5, 4, 3, 2])), [2, 3]);
|
||||
}
|
||||
|
||||
// Calls |has| when the this-value has fewer or the same number of keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [keys.length, 100, Infinity]) {
|
||||
let i = 0;
|
||||
|
||||
let setLike = {
|
||||
size,
|
||||
has(v) {
|
||||
assertEq(this, setLike);
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(i < keys.length, true);
|
||||
assertEq(v, keys[i++]);
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertSetContainsExactOrderedItems(new Set(keys).intersection(setLike), keys);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls |keys| when the this-value has more keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [0, 1, 2]) {
|
||||
let i = 0;
|
||||
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
keys() {
|
||||
assertEq(this, setLike);
|
||||
assertEq(arguments.length, 0);
|
||||
|
||||
let iterator = {
|
||||
next() {
|
||||
assertEq(this, iterator);
|
||||
assertEq(arguments.length, 0);
|
||||
if (i < keys.length) {
|
||||
return {
|
||||
done: false,
|
||||
value: keys[i++],
|
||||
};
|
||||
}
|
||||
return {
|
||||
done: true,
|
||||
get value() {
|
||||
throw new Error("Unexpected call to |value| getter");
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return iterator;
|
||||
},
|
||||
};
|
||||
|
||||
assertSetContainsExactOrderedItems(new Set(keys).intersection(setLike), keys);
|
||||
}
|
||||
}
|
||||
|
||||
// Test result set order when the this-value was modified.
|
||||
{
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
*keys() {
|
||||
// Yield the same keys as in |set|.
|
||||
yield* set.keys();
|
||||
|
||||
// Remove all existing items.
|
||||
set.clear();
|
||||
|
||||
// Re-add keys 2 and 3, but in reversed order.
|
||||
set.add(3);
|
||||
set.add(2);
|
||||
|
||||
// Additionally add 99.
|
||||
set.add(99);
|
||||
},
|
||||
};
|
||||
|
||||
let set = new Set([1, 2, 3, 4]);
|
||||
|
||||
assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3, 4]);
|
||||
assertSetContainsExactOrderedItems(set, [3, 2, 99]);
|
||||
}
|
||||
{
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
*keys() {
|
||||
// Yield the same keys as in |set|.
|
||||
yield* set.keys();
|
||||
|
||||
// Remove only keys 2 and 3.
|
||||
set.delete(2);
|
||||
set.delete(3);
|
||||
|
||||
// Re-add keys 2 and 3, but in reversed order.
|
||||
set.add(3);
|
||||
set.add(2);
|
||||
},
|
||||
};
|
||||
|
||||
let set = new Set([1, 2, 3, 4]);
|
||||
|
||||
assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3, 4]);
|
||||
assertSetContainsExactOrderedItems(set, [1, 4, 3, 2]);
|
||||
}
|
||||
|
||||
// Test the same item can't be added multiple times.
|
||||
{
|
||||
let seen = [];
|
||||
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
// Remove and then re-add 2.
|
||||
if (v === 2 && !seen.includes(v)) {
|
||||
set.delete(v);
|
||||
set.add(v);
|
||||
}
|
||||
|
||||
// Remember all visited keys.
|
||||
seen.push(v);
|
||||
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
assertSetContainsExactOrderedItems(set.intersection(setLike), [1, 2, 3]);
|
||||
assertEqArray(seen, [1, 2, 3, 2]);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.intersection(other), [1, 2, 3]);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,448 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isDisjointFrom)
|
||||
|
||||
assertEq(typeof Set.prototype.isDisjointFrom, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isDisjointFrom, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isDisjointFrom, "name"), {
|
||||
value: "isDisjointFrom", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Empty set is disjoint from the empty set.
|
||||
assertEq(emptySet.isDisjointFrom(emptySet), true);
|
||||
assertEq(emptySet.isDisjointFrom(emptySetLike), true);
|
||||
assertEq(emptySet.isDisjointFrom(emptyMap), true);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Disjoint operation with an empty set.
|
||||
assertEq(new Set(values).isDisjointFrom(emptySet), true);
|
||||
assertEq(new Set(values).isDisjointFrom(emptySetLike), true);
|
||||
assertEq(new Set(values).isDisjointFrom(emptyMap), true);
|
||||
assertEq(emptySet.isDisjointFrom(new Set(values)), true);
|
||||
assertEq(emptySet.isDisjointFrom(new SetLike(values)), true);
|
||||
assertEq(emptySet.isDisjointFrom(asMap(values)), true);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertEq(new Set(values).isDisjointFrom(new Set(values)), values.length === 0);
|
||||
assertEq(new Set(values).isDisjointFrom(new SetLike(values)), values.length === 0);
|
||||
assertEq(new Set(values).isDisjointFrom(asMap(values)), values.length === 0);
|
||||
|
||||
// Disjoint operation of the same set object.
|
||||
let set = new Set(values);
|
||||
assertEq(set.isDisjointFrom(set), values.length === 0);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
assertEq(emptySet.isDisjointFrom(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// |keys| is called when the this-value has more elements.
|
||||
|
||||
log.length = 0;
|
||||
assertEq(new Set([1]).isDisjointFrom(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertEq(emptySet.isDisjointFrom(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertEq(emptySet.isDisjointFrom(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => new Set([1]).isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isDisjointFrom(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.isDisjointFrom([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertEq(emptySet.isDisjointFrom(myEmptySet), true);
|
||||
assertEq(myEmptySet.isDisjointFrom(myEmptySet), true);
|
||||
assertEq(myEmptySet.isDisjointFrom(emptySet), true);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertEq(Set.prototype.isDisjointFrom.call(set, emptySet), true);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.isDisjointFrom.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Calls |has| when the this-value has fewer keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [keys.length, 100, Infinity]) {
|
||||
let i = 0;
|
||||
|
||||
let setLike = {
|
||||
size,
|
||||
has(v) {
|
||||
assertEq(this, setLike);
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(i < keys.length, true);
|
||||
assertEq(v, keys[i++]);
|
||||
return false;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls |keys| when the this-value has more keys.
|
||||
{
|
||||
const keys = [1, 2, 3];
|
||||
|
||||
for (let size of [0, 1, 2]) {
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
*keys() {
|
||||
yield* [4, 5, 6];
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(new Set(keys).isDisjointFrom(setLike), true);
|
||||
}
|
||||
|
||||
// Also test early return after first match.
|
||||
for (let size of [0, 1, 2]) {
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
*keys() {
|
||||
yield keys[0];
|
||||
|
||||
throw new Error("keys iterator called too many times");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(new Set(keys).isDisjointFrom(setLike), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Test when this-value is modified during iteration.
|
||||
{
|
||||
let set = new Set([]);
|
||||
|
||||
// |setLike| has more entries than |set|.
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
assertEq(set.has(v), true);
|
||||
set.delete(v);
|
||||
return false;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isDisjointFrom(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, []);
|
||||
}
|
||||
{
|
||||
let set = new Set([0]);
|
||||
|
||||
let keys = [1, 2, 3];
|
||||
|
||||
let lastValue;
|
||||
let keysIter = {
|
||||
next() {
|
||||
if (lastValue !== undefined) {
|
||||
assertEq(set.has(lastValue), false);
|
||||
set.add(lastValue);
|
||||
|
||||
lastValue = undefined;
|
||||
}
|
||||
|
||||
if (keys.length) {
|
||||
let value = keys.shift();
|
||||
lastValue = value;
|
||||
return {
|
||||
done: false,
|
||||
get value() {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
done: true,
|
||||
get value() {
|
||||
throw new Error("Unexpected call to |value| getter");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// |setLike| has fewer entries than |set|.
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return keysIter;
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isDisjointFrom(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, [0, 1, 2, 3]);
|
||||
}
|
||||
|
||||
// IteratorClose is called for early returns.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let keysIter = {
|
||||
next() {
|
||||
log.push("next");
|
||||
return {done: false, value: 1};
|
||||
},
|
||||
return() {
|
||||
log.push("return");
|
||||
return {
|
||||
get value() { throw new Error("Unexpected call to |value| getter"); },
|
||||
get done() { throw new Error("Unexpected call to |done| getter"); },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return keysIter;
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), false);
|
||||
|
||||
assertEqArray(log, ["next", "return"]);
|
||||
}
|
||||
|
||||
// IteratorClose isn't called for non-early returns.
|
||||
{
|
||||
let setLike = new SetLike([4, 5, 6]);
|
||||
|
||||
assertEq(new Set([1, 2, 3]).isDisjointFrom(setLike), true);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertEq(set.isDisjointFrom(other), false);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,333 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isSubsetOf)
|
||||
|
||||
assertEq(typeof Set.prototype.isSubsetOf, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSubsetOf, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSubsetOf, "name"), {
|
||||
value: "isSubsetOf", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Empty set is a subset of the empty set.
|
||||
assertEq(emptySet.isSubsetOf(emptySet), true);
|
||||
assertEq(emptySet.isSubsetOf(emptySetLike), true);
|
||||
assertEq(emptySet.isSubsetOf(emptyMap), true);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Subset operation with an empty set.
|
||||
assertEq(new Set(values).isSubsetOf(emptySet), values.length === 0);
|
||||
assertEq(new Set(values).isSubsetOf(emptySetLike), values.length === 0);
|
||||
assertEq(new Set(values).isSubsetOf(emptyMap), values.length === 0);
|
||||
assertEq(emptySet.isSubsetOf(new Set(values)), true);
|
||||
assertEq(emptySet.isSubsetOf(new SetLike(values)), true);
|
||||
assertEq(emptySet.isSubsetOf(asMap(values)), true);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertEq(new Set(values).isSubsetOf(new Set(values)), true);
|
||||
assertEq(new Set(values).isSubsetOf(new SetLike(values)), true);
|
||||
assertEq(new Set(values).isSubsetOf(asMap(values)), true);
|
||||
|
||||
// Subset operation of the same set object.
|
||||
let set = new Set(values);
|
||||
assertEq(set.isSubsetOf(set), true);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
assertEq(emptySet.isSubsetOf(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
}, log);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSubsetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.isSubsetOf([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertEq(emptySet.isSubsetOf(myEmptySet), true);
|
||||
assertEq(myEmptySet.isSubsetOf(myEmptySet), true);
|
||||
assertEq(myEmptySet.isSubsetOf(emptySet), true);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertEq(Set.prototype.isSubsetOf.call(set, emptySet), true);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.isSubsetOf.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't call |has| when this-value has more elements.
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
for (let size of [0, 1, 2]) {
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSubsetOf(setLike), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Test when this-value is modified during iteration.
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
let seen = new Set();
|
||||
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(this, setLike);
|
||||
assertEq(set.has(v), true);
|
||||
|
||||
assertEq(seen.has(v), false);
|
||||
seen.add(v);
|
||||
|
||||
// Delete the current element.
|
||||
set.delete(v);
|
||||
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSubsetOf(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, []);
|
||||
assertSetContainsExactOrderedItems(seen, [1, 2, 3]);
|
||||
}
|
||||
{
|
||||
let set = new Set([1]);
|
||||
|
||||
let seen = new Set();
|
||||
let newKeys = [2, 3, 4, 5];
|
||||
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(this, setLike);
|
||||
assertEq(set.has(v), true);
|
||||
|
||||
assertEq(seen.has(v), false);
|
||||
seen.add(v);
|
||||
|
||||
// Delete the current element.
|
||||
set.delete(v);
|
||||
|
||||
// Add new elements.
|
||||
if (newKeys.length) {
|
||||
set.add(newKeys.shift());
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSubsetOf(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, []);
|
||||
assertSetContainsExactOrderedItems(seen, [1, 2, 3, 4, 5]);
|
||||
assertEq(newKeys.length, 0);
|
||||
}
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
let seen = new Set();
|
||||
let deleted = false;
|
||||
|
||||
let setLike = {
|
||||
size: 100,
|
||||
has(v) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(this, setLike);
|
||||
assertEq(set.has(v), true);
|
||||
|
||||
assertEq(seen.has(v), false);
|
||||
seen.add(v);
|
||||
|
||||
if (!deleted) {
|
||||
assertEq(v, 1);
|
||||
set.delete(2);
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSubsetOf(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, [1, 3]);
|
||||
assertSetContainsExactOrderedItems(seen, [1, 3]);
|
||||
assertEq(deleted, true);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertEq(set.isSubsetOf(other), true);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,360 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.isSupersetOf)
|
||||
|
||||
assertEq(typeof Set.prototype.isSupersetOf, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSupersetOf, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.isSupersetOf, "name"), {
|
||||
value: "isSupersetOf", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Empty set is a superset of the empty set.
|
||||
assertEq(emptySet.isSupersetOf(emptySet), true);
|
||||
assertEq(emptySet.isSupersetOf(emptySetLike), true);
|
||||
assertEq(emptySet.isSupersetOf(emptyMap), true);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Superset operation with an empty set.
|
||||
assertEq(new Set(values).isSupersetOf(emptySet), true);
|
||||
assertEq(new Set(values).isSupersetOf(emptySetLike), true);
|
||||
assertEq(new Set(values).isSupersetOf(emptyMap), true);
|
||||
assertEq(emptySet.isSupersetOf(new Set(values)), values.length === 0);
|
||||
assertEq(emptySet.isSupersetOf(new SetLike(values)), values.length === 0);
|
||||
assertEq(emptySet.isSupersetOf(asMap(values)), values.length === 0);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertEq(new Set(values).isSupersetOf(new Set(values)), true);
|
||||
assertEq(new Set(values).isSupersetOf(new SetLike(values)), true);
|
||||
assertEq(new Set(values).isSupersetOf(asMap(values)), true);
|
||||
|
||||
// Superset operation of the same set object.
|
||||
let set = new Set(values);
|
||||
assertEq(set.isSupersetOf(set), true);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
assertEq(emptySet.isSupersetOf(setLike), true);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
|
||||
// |keys| isn't called when the this-value has fewer elements.
|
||||
sizeValue = 1;
|
||||
|
||||
log.length = 0;
|
||||
assertEq(emptySet.isSupersetOf(setLike), false);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.isSupersetOf([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertEq(emptySet.isSupersetOf(myEmptySet), true);
|
||||
assertEq(myEmptySet.isSupersetOf(myEmptySet), true);
|
||||
assertEq(myEmptySet.isSupersetOf(emptySet), true);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertEq(Set.prototype.isSupersetOf.call(set, emptySet), true);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.isSupersetOf.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't call |has| nor |keys| when this-value has fewer elements.
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
for (let size of [100, Infinity]) {
|
||||
let setLike = {
|
||||
size,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
throw new Error("Unexpected call to |keys| method");
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSupersetOf(setLike), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Test when this-value is modified during iteration.
|
||||
{
|
||||
let set = new Set([]);
|
||||
|
||||
let keys = [1, 2, 3];
|
||||
|
||||
let keysIter = {
|
||||
next() {
|
||||
if (keys.length) {
|
||||
let value = keys.shift();
|
||||
return {
|
||||
done: false,
|
||||
get value() {
|
||||
assertEq(set.has(value), false);
|
||||
set.add(value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
done: true,
|
||||
get value() {
|
||||
throw new Error("Unexpected call to |value| getter");
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return keysIter;
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(set.isSupersetOf(setLike), true);
|
||||
assertSetContainsExactOrderedItems(set, [1, 2, 3]);
|
||||
}
|
||||
|
||||
// IteratorClose is called for early returns.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let keysIter = {
|
||||
next() {
|
||||
log.push("next");
|
||||
return {done: false, value: 1};
|
||||
},
|
||||
return() {
|
||||
log.push("return");
|
||||
return {
|
||||
get value() { throw new Error("Unexpected call to |value| getter"); },
|
||||
get done() { throw new Error("Unexpected call to |done| getter"); },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return keysIter;
|
||||
},
|
||||
};
|
||||
|
||||
assertEq(new Set([2, 3, 4]).isSupersetOf(setLike), false);
|
||||
|
||||
assertEqArray(log, ["next", "return"]);
|
||||
}
|
||||
|
||||
// IteratorClose isn't called for non-early returns.
|
||||
{
|
||||
let setLike = new SetLike([1, 2, 3]);
|
||||
|
||||
assertEq(new Set([1, 2, 3]).isSupersetOf(setLike), true);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertEq(set.isSupersetOf(other), true);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
(function(global) {
|
||||
// Save the primordial values.
|
||||
const {Array, Error, Object, Proxy, Reflect, Set} = global;
|
||||
|
||||
const ArrayIsArray = Array.isArray;
|
||||
const ReflectApply = Reflect.apply;
|
||||
const ReflectDefineProperty = Reflect.defineProperty;
|
||||
const ReflectGet = Reflect.get;
|
||||
const ReflectGetPrototypeOf = Reflect.getPrototypeOf;
|
||||
const SetPrototype = Set.prototype;
|
||||
const SetPrototypeHas = SetPrototype.has;
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(SetPrototype, "size").get;
|
||||
const SetPrototypeKeys = SetPrototype.keys;
|
||||
const SetIteratorPrototypeNext = new Set().values().next;
|
||||
|
||||
function assertSetContainsExactOrderedItems(actual, expected) {
|
||||
assertEq(ReflectGetPrototypeOf(actual), SetPrototype, "actual is a native Set object");
|
||||
assertEq(ArrayIsArray(expected), true, "expected is an Array object");
|
||||
|
||||
assertEq(ReflectApply(SetPrototypeSize, actual, []), expected.length);
|
||||
|
||||
let index = 0;
|
||||
let keys = ReflectApply(SetPrototypeKeys, actual, []);
|
||||
|
||||
while (true) {
|
||||
let {done, value: item} = ReflectApply(SetIteratorPrototypeNext, keys, []);
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
assertEq(item, expected[index], `Element at index ${index}:`);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
global.assertSetContainsExactOrderedItems = assertSetContainsExactOrderedItems;
|
||||
|
||||
class SetLike {
|
||||
#set;
|
||||
|
||||
constructor(values) {
|
||||
this.#set = new Set(values);
|
||||
}
|
||||
|
||||
get size() {
|
||||
return ReflectApply(SetPrototypeSize, this.#set, []);
|
||||
}
|
||||
|
||||
has(value) {
|
||||
return ReflectApply(SetPrototypeHas, this.#set, [value]);
|
||||
}
|
||||
|
||||
keys() {
|
||||
let keys = ReflectApply(SetPrototypeKeys, this.#set, []);
|
||||
return new SetIteratorLike(keys);
|
||||
}
|
||||
}
|
||||
global.SetLike = SetLike;
|
||||
|
||||
class SetIteratorLike {
|
||||
#keys;
|
||||
|
||||
constructor(keys) {
|
||||
this.#keys = keys;
|
||||
}
|
||||
|
||||
next() {
|
||||
return ReflectApply(SetIteratorPrototypeNext, this.#keys, []);
|
||||
}
|
||||
|
||||
// The |return| method of the iterator protocol is never called.
|
||||
return() {
|
||||
throw new Error("Unexpected call to |return| method");
|
||||
}
|
||||
|
||||
// The |throw| method of the iterator protocol is never called.
|
||||
throw() {
|
||||
throw new Error("Unexpected call to |throw| method");
|
||||
}
|
||||
}
|
||||
|
||||
function LoggingProxy(obj, log) {
|
||||
assertEq(ArrayIsArray(log), true);
|
||||
|
||||
let handler = new Proxy({
|
||||
get(t, pk, r) {
|
||||
ReflectDefineProperty(log, log.length, {
|
||||
value: pk, writable: true, enumerable: true, configurable: true,
|
||||
});
|
||||
return ReflectGet(t, pk, r);
|
||||
}
|
||||
}, {
|
||||
get(t, pk, r) {
|
||||
ReflectDefineProperty(log, log.length, {
|
||||
value: `[[${pk}]]`, writable: true, enumerable: true, configurable: true,
|
||||
});
|
||||
return ReflectGet(t, pk, r);
|
||||
}
|
||||
});
|
||||
|
||||
return {obj, proxy: new Proxy(obj, handler)};
|
||||
}
|
||||
global.LoggingProxy = LoggingProxy;
|
||||
})(this);
|
|
@ -1,4 +1,303 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.symmetricDifference)
|
||||
|
||||
assertEq(typeof Set.prototype.symmetricDifference, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.symmetricDifference, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.symmetricDifference, "name"), {
|
||||
value: "symmetricDifference", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Symmetric difference of two empty sets is an empty set.
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptySet), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptySetLike), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(emptyMap), []);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Symmetric difference with an empty set.
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptySet), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptySetLike), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(emptyMap), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(new Set(values)), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(new SetLike(values)), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(asMap(values)), values);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(new Set(values)), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(new SetLike(values)), []);
|
||||
assertSetContainsExactOrderedItems(new Set(values).symmetricDifference(asMap(values)), []);
|
||||
|
||||
// Symmetric difference of the same set object.
|
||||
let set = new Set(values);
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(set), []);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.symmetricDifference([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.symmetricDifference(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.symmetricDifference(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.symmetricDifference(emptySet), []);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertSetContainsExactOrderedItems(Set.prototype.symmetricDifference.call(set, emptySet), [1, 2, 3]);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.symmetricDifference.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't return the original Set object.
|
||||
{
|
||||
let set = new Set([1]);
|
||||
assertEq(set.symmetricDifference(emptySet) !== set, true);
|
||||
assertEq(set.symmetricDifference(new Set([2])) !== set, true);
|
||||
}
|
||||
|
||||
// Test insertion order
|
||||
{
|
||||
let set = new Set([1, 2]);
|
||||
|
||||
// Case 1: Input is empty.
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([])), [1, 2]);
|
||||
|
||||
// Case 2: Input has fewer elements.
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3])), [1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2])), [1]);
|
||||
|
||||
// Case 3: Input has same number of elements.
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3, 4])), [1, 2, 3, 4]);
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2, 3])), [1, 3]);
|
||||
|
||||
// Case 4: Input has more elements.
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([3, 4, 5])), [1, 2, 3, 4, 5]);
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(new Set([2, 4, 5])), [1, 4, 5]);
|
||||
}
|
||||
|
||||
// Test that the input set is copied after accessing the |next| property of the keys iterator.
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return {
|
||||
get next() {
|
||||
// Clear the set when getting the |next| method.
|
||||
set.clear();
|
||||
|
||||
// And then add a single new key.
|
||||
set.add(4);
|
||||
|
||||
return function() {
|
||||
return {done: true};
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// The result should consist of the single, newly added key.
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(setLike), [4]);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.symmetricDifference(other), []);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,300 @@
|
|||
// |reftest| shell-option(--enable-new-set-methods) skip-if(!Set.prototype.union)
|
||||
|
||||
assertEq(typeof Set.prototype.union, "function");
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.union, "length"), {
|
||||
value: 1, writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(Set.prototype.union, "name"), {
|
||||
value: "union", writable: false, enumerable: false, configurable: true,
|
||||
});
|
||||
|
||||
const emptySet = new Set();
|
||||
const emptySetLike = new SetLike();
|
||||
const emptyMap = new Map();
|
||||
|
||||
function asMap(values) {
|
||||
return new Map(values.map(v => [v, v]));
|
||||
}
|
||||
|
||||
// Union of two empty sets is an empty set.
|
||||
assertSetContainsExactOrderedItems(emptySet.union(emptySet), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.union(emptySetLike), []);
|
||||
assertSetContainsExactOrderedItems(emptySet.union(emptyMap), []);
|
||||
|
||||
// Test native Set, Set-like, and Map objects.
|
||||
for (let values of [
|
||||
[], [1], [1, 2], [1, true, null, {}],
|
||||
]) {
|
||||
// Union with an empty set.
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(emptySet), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(emptySetLike), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(emptyMap), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.union(new Set(values)), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.union(new SetLike(values)), values);
|
||||
assertSetContainsExactOrderedItems(emptySet.union(asMap(values)), values);
|
||||
|
||||
// Two sets with the exact same values.
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(new Set(values)), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(new SetLike(values)), values);
|
||||
assertSetContainsExactOrderedItems(new Set(values).union(asMap(values)), values);
|
||||
|
||||
// Union of the same set object.
|
||||
let set = new Set(values);
|
||||
assertSetContainsExactOrderedItems(set.union(set), values);
|
||||
}
|
||||
|
||||
// Check property accesses are in the correct order.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next() {
|
||||
log.push("next()");
|
||||
return {done: true};
|
||||
}
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.union(setLike), []);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
"next()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Check input validation.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
const nonCallable = {};
|
||||
let sizeValue = 0;
|
||||
|
||||
let {proxy: keysIter} = LoggingProxy({
|
||||
next: nonCallable,
|
||||
}, log);
|
||||
|
||||
let {proxy: setLike, obj: setLikeObj} = LoggingProxy({
|
||||
size: {
|
||||
valueOf() {
|
||||
log.push("valueOf()");
|
||||
return sizeValue;
|
||||
}
|
||||
},
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
log.push("keys()");
|
||||
return keysIter;
|
||||
},
|
||||
}, log);
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
"keys()",
|
||||
"[[get]]", "next",
|
||||
]);
|
||||
|
||||
// Change |keys| to return a non-object value.
|
||||
setLikeObj.keys = () => 123;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |keys| to a non-callable value.
|
||||
setLikeObj.keys = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
"[[get]]", "keys",
|
||||
]);
|
||||
|
||||
// Change |has| to a non-callable value.
|
||||
setLikeObj.has = nonCallable;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
"[[get]]", "has",
|
||||
]);
|
||||
|
||||
// Change |size| to NaN.
|
||||
sizeValue = NaN;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
|
||||
// Change |size| to undefined.
|
||||
sizeValue = undefined;
|
||||
|
||||
log.length = 0;
|
||||
assertThrowsInstanceOf(() => emptySet.union(setLike), TypeError);
|
||||
|
||||
assertEqArray(log, [
|
||||
"[[get]]", "size",
|
||||
"valueOf()",
|
||||
]);
|
||||
}
|
||||
|
||||
// Doesn't accept Array as an input.
|
||||
assertThrowsInstanceOf(() => emptySet.union([]), TypeError);
|
||||
|
||||
// Works with Set subclasses.
|
||||
{
|
||||
class MySet extends Set {}
|
||||
|
||||
let myEmptySet = new MySet;
|
||||
|
||||
assertSetContainsExactOrderedItems(emptySet.union(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.union(myEmptySet), []);
|
||||
assertSetContainsExactOrderedItems(myEmptySet.union(emptySet), []);
|
||||
}
|
||||
|
||||
// Doesn't access any properties on the this-value.
|
||||
{
|
||||
let log = [];
|
||||
|
||||
let {proxy: setProto} = LoggingProxy(Set.prototype, log);
|
||||
|
||||
let set = new Set([1, 2, 3]);
|
||||
Object.setPrototypeOf(set, setProto);
|
||||
|
||||
assertSetContainsExactOrderedItems(Set.prototype.union.call(set, emptySet), [1, 2, 3]);
|
||||
|
||||
assertEqArray(log, []);
|
||||
}
|
||||
|
||||
// Throws a TypeError when the this-value isn't a Set object.
|
||||
for (let thisValue of [
|
||||
null, undefined, true, "", {}, new Map, new Proxy(new Set, {}),
|
||||
]) {
|
||||
assertThrowsInstanceOf(() => Set.prototype.union.call(thisValue, emptySet), TypeError);
|
||||
}
|
||||
|
||||
// Doesn't return the original Set object.
|
||||
{
|
||||
let set = new Set([1]);
|
||||
assertEq(set.union(emptySet) !== set, true);
|
||||
assertEq(set.union(new Set([2])) !== set, true);
|
||||
}
|
||||
|
||||
// Test insertion order
|
||||
{
|
||||
let set = new Set([1, 2]);
|
||||
|
||||
// Case 1: Input is empty.
|
||||
assertSetContainsExactOrderedItems(set.union(new Set([])), [1, 2]);
|
||||
|
||||
// Case 2: Input has fewer elements.
|
||||
assertSetContainsExactOrderedItems(set.union(new Set([3])), [1, 2, 3]);
|
||||
|
||||
// Case 3: Input has same number of elements.
|
||||
assertSetContainsExactOrderedItems(set.union(new Set([3, 4])), [1, 2, 3, 4]);
|
||||
|
||||
// Case 4: Input has more elements.
|
||||
assertSetContainsExactOrderedItems(set.union(new Set([3, 4, 5])), [1, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
// Test that the input set is copied after accessing the |next| property of the keys iterator.
|
||||
{
|
||||
let set = new Set([1, 2, 3]);
|
||||
|
||||
let setLike = {
|
||||
size: 0,
|
||||
has() {
|
||||
throw new Error("Unexpected call to |has| method");
|
||||
},
|
||||
keys() {
|
||||
return {
|
||||
get next() {
|
||||
// Clear the set when getting the |next| method.
|
||||
set.clear();
|
||||
|
||||
// And then add a single new key.
|
||||
set.add(4);
|
||||
|
||||
return function() {
|
||||
return {done: true};
|
||||
};
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// The result should consist of the single, newly added key.
|
||||
assertSetContainsExactOrderedItems(set.union(setLike), [4]);
|
||||
}
|
||||
|
||||
// Tests which modify any built-ins should appear last, because modifications may disable
|
||||
// optimised code paths.
|
||||
|
||||
// Doesn't call the built-in |Set.prototype.{has, keys, size}| functions.
|
||||
const SetPrototypeHas = Object.getOwnPropertyDescriptor(Set.prototype, "has");
|
||||
const SetPrototypeKeys = Object.getOwnPropertyDescriptor(Set.prototype, "keys");
|
||||
const SetPrototypeSize = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
|
||||
delete Set.prototype.has;
|
||||
delete Set.prototype.keys;
|
||||
delete Set.prototype.size;
|
||||
|
||||
try {
|
||||
let set = new Set([1, 2, 3]);
|
||||
let other = new SetLike([1, 2, 3]);
|
||||
assertSetContainsExactOrderedItems(set.union(other), [1, 2, 3]);
|
||||
} finally {
|
||||
Object.defineProperty(Set.prototype, "has", SetPrototypeHas);
|
||||
Object.defineProperty(Set.prototype, "keys", SetPrototypeKeys);
|
||||
Object.defineProperty(Set.prototype, "size", SetPrototypeSize);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
Загрузка…
Ссылка в новой задаче