From c8cd136888a804772ef774c3b2fe73299e3c7ef3 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 10 Jan 2024 12:52:41 -0800 Subject: [PATCH] More set methods tests (#3966) * add tests for difference, intersection, symmetricDifference * add tests for predicates * more tests for ordering of calls * fix copyrights * add missed -0 test * pass argument to methods, not just this * assert methods return new objects * typo in filename * fix parens * Update test/built-ins/Set/prototype/difference/add-not-called.js Co-authored-by: Jordan Harband * fix isSupersetOf/set-like-array.js * add tests for negative zero with isDisjointFrom and isSupersetOf * fix message in isSupersetOf/converts-negative-zero.js * address comments from review --------- Co-authored-by: Jordan Harband --- .../prototype/difference/add-not-called.js | 27 +++ .../difference/allows-set-like-class.js | 35 ++++ .../difference/allows-set-like-object.js | 33 +++ .../Set/prototype/difference/array-throws.js | 17 ++ .../Set/prototype/difference/builtins.js | 25 +++ .../difference/called-with-object.js | 66 ++++++ .../Set/prototype/difference/combines-Map.js | 19 ++ .../difference/combines-empty-sets.js | 32 +++ .../prototype/difference/combines-itself.js | 16 ++ .../difference/combines-same-sets.js | 18 ++ .../Set/prototype/difference/combines-sets.js | 16 ++ .../difference/converts-negative-zero.js | 28 +++ .../Set/prototype/difference/difference.js | 20 ++ .../prototype/difference/has-is-callable.js | 36 ++++ .../prototype/difference/keys-is-callable.js | 33 +++ .../Set/prototype/difference/length.js | 18 ++ .../Set/prototype/difference/name.js | 18 ++ .../prototype/difference/not-a-constructor.js | 22 ++ .../prototype/difference/receiver-not-set.js | 44 ++++ .../difference/require-internal-slot.js | 27 +++ .../Set/prototype/difference/result-order.js | 37 ++++ .../prototype/difference/set-like-array.js | 26 +++ .../difference/set-like-class-mutation.js | 49 +++++ .../difference/set-like-class-order.js | 140 +++++++++++++ .../prototype/difference/size-is-a-number.js | 72 +++++++ .../difference/subclass-receiver-methods.js | 45 +++++ .../difference/subclass-symbol-species.js | 29 +++ .../Set/prototype/difference/subclass.js | 23 +++ .../prototype/intersection/add-not-called.js | 27 +++ .../intersection/allows-set-like-class.js | 35 ++++ .../intersection/allows-set-like-object.js | 33 +++ .../prototype/intersection/array-throws.js | 17 ++ .../Set/prototype/intersection/builtins.js | 25 +++ .../intersection/called-with-object.js | 66 ++++++ .../prototype/intersection/combines-Map.js | 19 ++ .../intersection/combines-empty-sets.js | 32 +++ .../prototype/intersection/combines-itself.js | 16 ++ .../intersection/combines-same-sets.js | 18 ++ .../prototype/intersection/combines-sets.js | 16 ++ .../intersection/converts-negative-zero.js | 28 +++ .../prototype/intersection/has-is-callable.js | 36 ++++ .../prototype/intersection/intersection.js | 20 ++ .../intersection/keys-is-callable.js | 33 +++ .../Set/prototype/intersection/length.js | 18 ++ .../Set/prototype/intersection/name.js | 18 ++ .../intersection/not-a-constructor.js | 22 ++ .../intersection/receiver-not-set.js | 44 ++++ .../intersection/require-internal-slot.js | 27 +++ .../prototype/intersection/result-order.js | 53 +++++ .../prototype/intersection/set-like-array.js | 26 +++ .../intersection/set-like-class-mutation.js | 49 +++++ .../intersection/set-like-class-order.js | 140 +++++++++++++ .../intersection/size-is-a-number.js | 72 +++++++ .../intersection/subclass-receiver-methods.js | 45 +++++ .../intersection/subclass-symbol-species.js | 29 +++ .../Set/prototype/intersection/subclass.js | 23 +++ .../isDisjointFrom/allows-set-like-class.js | 30 +++ .../isDisjointFrom/allows-set-like-object.js | 28 +++ .../prototype/isDisjointFrom/array-throws.js | 17 ++ .../Set/prototype/isDisjointFrom/builtins.js | 25 +++ .../isDisjointFrom/called-with-object.js | 66 ++++++ .../prototype/isDisjointFrom/compares-Map.js | 15 ++ .../isDisjointFrom/compares-empty-sets.js | 22 ++ .../isDisjointFrom/compares-itself.js | 11 + .../isDisjointFrom/compares-same-sets.js | 12 ++ .../prototype/isDisjointFrom/compares-sets.js | 16 ++ .../isDisjointFrom/converts-negative-zero.js | 22 ++ .../isDisjointFrom/has-is-callable.js | 36 ++++ .../isDisjointFrom/isDisjointFrom.js | 20 ++ .../isDisjointFrom/keys-is-callable.js | 33 +++ .../Set/prototype/isDisjointFrom/length.js | 18 ++ .../Set/prototype/isDisjointFrom/name.js | 18 ++ .../isDisjointFrom/not-a-constructor.js | 22 ++ .../isDisjointFrom/receiver-not-set.js | 44 ++++ .../isDisjointFrom/require-internal-slot.js | 27 +++ .../isDisjointFrom/set-like-array.js | 20 ++ .../isDisjointFrom/set-like-class-mutation.js | 38 ++++ .../isDisjointFrom/set-like-class-order.js | 191 ++++++++++++++++++ .../isDisjointFrom/size-is-a-number.js | 72 +++++++ .../subclass-receiver-methods.js | 37 ++++ .../isSubsetOf/allows-set-like-class.js | 31 +++ .../isSubsetOf/allows-set-like-object.js | 29 +++ .../Set/prototype/isSubsetOf/array-throws.js | 17 ++ .../Set/prototype/isSubsetOf/builtins.js | 25 +++ .../isSubsetOf/called-with-object.js | 66 ++++++ .../Set/prototype/isSubsetOf/compares-Map.js | 15 ++ .../isSubsetOf/compares-empty-sets.js | 22 ++ .../prototype/isSubsetOf/compares-itself.js | 11 + .../isSubsetOf/compares-same-sets.js | 12 ++ .../Set/prototype/isSubsetOf/compares-sets.js | 16 ++ .../prototype/isSubsetOf/has-is-callable.js | 36 ++++ .../Set/prototype/isSubsetOf/isSubsetOf.js | 20 ++ .../prototype/isSubsetOf/keys-is-callable.js | 33 +++ .../Set/prototype/isSubsetOf/length.js | 18 ++ .../Set/prototype/isSubsetOf/name.js | 18 ++ .../prototype/isSubsetOf/not-a-constructor.js | 22 ++ .../prototype/isSubsetOf/receiver-not-set.js | 44 ++++ .../isSubsetOf/require-internal-slot.js | 27 +++ .../prototype/isSubsetOf/set-like-array.js | 21 ++ .../isSubsetOf/set-like-class-mutation.js | 29 +++ .../isSubsetOf/set-like-class-order.js | 121 +++++++++++ .../prototype/isSubsetOf/size-is-a-number.js | 72 +++++++ .../isSubsetOf/subclass-receiver-methods.js | 37 ++++ .../isSupersetOf/allows-set-like-class.js | 29 +++ .../isSupersetOf/allows-set-like-object.js | 27 +++ .../prototype/isSupersetOf/array-throws.js | 17 ++ .../Set/prototype/isSupersetOf/builtins.js | 25 +++ .../isSupersetOf/called-with-object.js | 66 ++++++ .../prototype/isSupersetOf/compares-Map.js | 15 ++ .../isSupersetOf/compares-empty-sets.js | 22 ++ .../prototype/isSupersetOf/compares-itself.js | 11 + .../isSupersetOf/compares-same-sets.js | 12 ++ .../prototype/isSupersetOf/compares-sets.js | 16 ++ .../isSupersetOf/converts-negative-zero.js | 22 ++ .../prototype/isSupersetOf/has-is-callable.js | 36 ++++ .../prototype/isSupersetOf/isSupersetOf.js | 20 ++ .../isSupersetOf/keys-is-callable.js | 33 +++ .../Set/prototype/isSupersetOf/length.js | 18 ++ .../Set/prototype/isSupersetOf/name.js | 18 ++ .../isSupersetOf/not-a-constructor.js | 22 ++ .../isSupersetOf/receiver-not-set.js | 44 ++++ .../isSupersetOf/require-internal-slot.js | 27 +++ .../prototype/isSupersetOf/set-like-array.js | 21 ++ .../isSupersetOf/set-like-class-mutation.js | 30 +++ .../isSupersetOf/set-like-class-order.js | 179 ++++++++++++++++ .../isSupersetOf/size-is-a-number.js | 72 +++++++ .../isSupersetOf/subclass-receiver-methods.js | 37 ++++ .../symmetricDifference/add-not-called.js | 27 +++ .../allows-set-like-class.js | 34 ++++ .../allows-set-like-object.js | 32 +++ .../symmetricDifference/array-throws.js | 17 ++ .../prototype/symmetricDifference/builtins.js | 25 +++ .../symmetricDifference/called-with-object.js | 66 ++++++ .../symmetricDifference/combines-Map.js | 19 ++ .../combines-empty-sets.js | 32 +++ .../symmetricDifference/combines-itself.js | 16 ++ .../symmetricDifference/combines-same-sets.js | 18 ++ .../symmetricDifference/combines-sets.js | 16 ++ .../converts-negative-zero.js | 28 +++ .../symmetricDifference/has-is-callable.js | 36 ++++ .../symmetricDifference/keys-is-callable.js | 33 +++ .../prototype/symmetricDifference/length.js | 18 ++ .../Set/prototype/symmetricDifference/name.js | 18 ++ .../symmetricDifference/not-a-constructor.js | 22 ++ .../symmetricDifference/receiver-not-set.js | 44 ++++ .../require-internal-slot.js | 27 +++ .../symmetricDifference/result-order.js | 23 +++ .../symmetricDifference/set-like-array.js | 24 +++ .../set-like-class-mutation.js | 49 +++++ .../set-like-class-order.js | 166 +++++++++++++++ .../symmetricDifference/size-is-a-number.js | 72 +++++++ .../subclass-receiver-methods.js | 45 +++++ .../subclass-symbol-species.js | 29 +++ .../prototype/symmetricDifference/subclass.js | 23 +++ .../symmetricDifference.js | 20 ++ .../prototype/union/allows-set-like-object.js | 4 +- .../Set/prototype/union/combines-itself.js | 1 + .../Set/prototype/union/combines-same-sets.js | 2 + test/built-ins/Set/prototype/union/length.js | 2 +- test/built-ins/Set/prototype/union/name.js | 2 +- .../prototype/union/require-internal-slot.js | 24 ++- .../Set/prototype/union/result-order.js | 36 ++++ .../prototype/union/set-like-class-order.js | 85 +++++--- .../union/{union-properties.js => union.js} | 0 164 files changed, 5455 insertions(+), 39 deletions(-) create mode 100644 test/built-ins/Set/prototype/difference/add-not-called.js create mode 100644 test/built-ins/Set/prototype/difference/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/difference/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/difference/array-throws.js create mode 100644 test/built-ins/Set/prototype/difference/builtins.js create mode 100644 test/built-ins/Set/prototype/difference/called-with-object.js create mode 100644 test/built-ins/Set/prototype/difference/combines-Map.js create mode 100644 test/built-ins/Set/prototype/difference/combines-empty-sets.js create mode 100644 test/built-ins/Set/prototype/difference/combines-itself.js create mode 100644 test/built-ins/Set/prototype/difference/combines-same-sets.js create mode 100644 test/built-ins/Set/prototype/difference/combines-sets.js create mode 100644 test/built-ins/Set/prototype/difference/converts-negative-zero.js create mode 100644 test/built-ins/Set/prototype/difference/difference.js create mode 100644 test/built-ins/Set/prototype/difference/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/difference/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/difference/length.js create mode 100644 test/built-ins/Set/prototype/difference/name.js create mode 100644 test/built-ins/Set/prototype/difference/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/difference/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/difference/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/difference/result-order.js create mode 100644 test/built-ins/Set/prototype/difference/set-like-array.js create mode 100644 test/built-ins/Set/prototype/difference/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/difference/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/difference/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/difference/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/difference/subclass-symbol-species.js create mode 100644 test/built-ins/Set/prototype/difference/subclass.js create mode 100644 test/built-ins/Set/prototype/intersection/add-not-called.js create mode 100644 test/built-ins/Set/prototype/intersection/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/intersection/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/intersection/array-throws.js create mode 100644 test/built-ins/Set/prototype/intersection/builtins.js create mode 100644 test/built-ins/Set/prototype/intersection/called-with-object.js create mode 100644 test/built-ins/Set/prototype/intersection/combines-Map.js create mode 100644 test/built-ins/Set/prototype/intersection/combines-empty-sets.js create mode 100644 test/built-ins/Set/prototype/intersection/combines-itself.js create mode 100644 test/built-ins/Set/prototype/intersection/combines-same-sets.js create mode 100644 test/built-ins/Set/prototype/intersection/combines-sets.js create mode 100644 test/built-ins/Set/prototype/intersection/converts-negative-zero.js create mode 100644 test/built-ins/Set/prototype/intersection/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/intersection/intersection.js create mode 100644 test/built-ins/Set/prototype/intersection/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/intersection/length.js create mode 100644 test/built-ins/Set/prototype/intersection/name.js create mode 100644 test/built-ins/Set/prototype/intersection/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/intersection/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/intersection/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/intersection/result-order.js create mode 100644 test/built-ins/Set/prototype/intersection/set-like-array.js create mode 100644 test/built-ins/Set/prototype/intersection/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/intersection/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/intersection/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/intersection/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/intersection/subclass-symbol-species.js create mode 100644 test/built-ins/Set/prototype/intersection/subclass.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/array-throws.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/builtins.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/called-with-object.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/compares-Map.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/compares-empty-sets.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/compares-itself.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/compares-same-sets.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/compares-sets.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/converts-negative-zero.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/isDisjointFrom.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/length.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/name.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/set-like-array.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/isDisjointFrom/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/array-throws.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/builtins.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/called-with-object.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/compares-Map.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/compares-empty-sets.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/compares-itself.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/compares-same-sets.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/compares-sets.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/isSubsetOf.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/length.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/name.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/set-like-array.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/isSubsetOf/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/array-throws.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/builtins.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/called-with-object.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/compares-Map.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/compares-empty-sets.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/compares-itself.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/compares-same-sets.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/compares-sets.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/converts-negative-zero.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/isSupersetOf.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/length.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/name.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/set-like-array.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/isSupersetOf/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/add-not-called.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/allows-set-like-class.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/allows-set-like-object.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/array-throws.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/builtins.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/called-with-object.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/combines-Map.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/combines-empty-sets.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/combines-itself.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/combines-same-sets.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/combines-sets.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/has-is-callable.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/keys-is-callable.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/length.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/name.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/not-a-constructor.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/receiver-not-set.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/require-internal-slot.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/result-order.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/set-like-array.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/set-like-class-mutation.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/set-like-class-order.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/size-is-a-number.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/subclass-receiver-methods.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/subclass-symbol-species.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/subclass.js create mode 100644 test/built-ins/Set/prototype/symmetricDifference/symmetricDifference.js create mode 100644 test/built-ins/Set/prototype/union/result-order.js rename test/built-ins/Set/prototype/union/{union-properties.js => union.js} (100%) diff --git a/test/built-ins/Set/prototype/difference/add-not-called.js b/test/built-ins/Set/prototype/difference/add-not-called.js new file mode 100644 index 0000000000..1199203634 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/add-not-called.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference should not call Set.prototype.add +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1]; + +const originalAdd = Set.prototype.add; +let count = 0; +Set.prototype.add = function (...rest) { + count++; + return originalAdd.apply(this, rest); +}; + +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(count, 0, "Add is never called"); + +Set.prototype.add = originalAdd; diff --git a/test/built-ins/Set/prototype/difference/allows-set-like-class.js b/test/built-ins/Set/prototype/difference/allows-set-like-class.js new file mode 100644 index 0000000000..d1d4c69bfa --- /dev/null +++ b/test/built-ins/Set/prototype/difference/allows-set-like-class.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 2; + } + has(v) { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this"); + } + * keys() { + throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size"); + } +}; +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/allows-set-like-object.js b/test/built-ins/Set/prototype/difference/allows-set-like-object.js new file mode 100644 index 0000000000..00deeedf6f --- /dev/null +++ b/test/built-ins/Set/prototype/difference/allows-set-like-object.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: (v) => { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this"); + }, + keys: function* keys() { + throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size"); + }, +}; +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/array-throws.js b/test/built-ins/Set/prototype/difference/array-throws.js new file mode 100644 index 0000000000..8f39916e8d --- /dev/null +++ b/test/built-ins/Set/prototype/difference/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/difference/builtins.js b/test/built-ins/Set/prototype/difference/builtins.js new file mode 100644 index 0000000000..dcce82b912 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Tests that Set.prototype.difference meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.difference), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.difference), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.difference), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/difference/called-with-object.js b/test/built-ins/Set/prototype/difference/called-with-object.js new file mode 100644 index 0000000000..e56d0218d9 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.difference(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.difference(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.difference(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.difference(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.difference(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.difference(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.difference(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/difference/combines-Map.js b/test/built-ins/Set/prototype/difference/combines-Map.js new file mode 100644 index 0000000000..5dc07daa5d --- /dev/null +++ b/test/built-ins/Set/prototype/difference/combines-Map.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference combines with Map +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); +const expected = [1]; +const combined = s1.difference(m1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/combines-empty-sets.js b/test/built-ins/Set/prototype/difference/combines-empty-sets.js new file mode 100644 index 0000000000..ffddb954f2 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/combines-empty-sets.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference can combine empty Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); +let expected = []; +let combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); +expected = [1, 2]; +combined = s3.difference(s4); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s5 = new Set([]); +const s6 = new Set([]); +expected = []; +combined = s5.difference(s6); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/combines-itself.js b/test/built-ins/Set/prototype/difference/combines-itself.js new file mode 100644 index 0000000000..faa75d9be5 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/combines-itself.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference is successful when called on itself +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const expected = []; +const combined = s1.difference(s1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/difference/combines-same-sets.js b/test/built-ins/Set/prototype/difference/combines-same-sets.js new file mode 100644 index 0000000000..61ace24673 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/combines-same-sets.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference can combine Sets that have the same content +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); +const expected = []; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); +assert.sameValue(combined === s2, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/difference/combines-sets.js b/test/built-ins/Set/prototype/difference/combines-sets.js new file mode 100644 index 0000000000..76449bedba --- /dev/null +++ b/test/built-ins/Set/prototype/difference/combines-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference combines Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/converts-negative-zero.js b/test/built-ins/Set/prototype/difference/converts-negative-zero.js new file mode 100644 index 0000000000..f39e91c8c0 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/converts-negative-zero.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference converts -0𝔽 to +0𝔽 +info: | + 7.b.ii If nextValue is -0𝔽, set nextValue to +0𝔽. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const setlikeWithMinusZero = { + size: 1, + has: function () { + throw new Test262Error("Set.prototype.difference should not call its argument's has method when this.size > arg.size"); + }, + keys: function () { + // we use an array here because the Set constructor would normalize away -0 + return [-0].values(); + }, +}; + +const s1 = new Set([+0, 1]); +let expected = [1]; +let combined = s1.difference(setlikeWithMinusZero); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/difference.js b/test/built-ins/Set/prototype/difference/difference.js new file mode 100644 index 0000000000..f5e39a780b --- /dev/null +++ b/test/built-ins/Set/prototype/difference/difference.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.difference, + "function", + "`typeof Set.prototype.difference` is `'function'`" +); + +verifyProperty(Set.prototype, "difference", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/difference/has-is-callable.js b/test/built-ins/Set/prototype/difference/has-is-callable.js new file mode 100644 index 0000000000..95d6092a61 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/difference/keys-is-callable.js b/test/built-ins/Set/prototype/difference/keys-is-callable.js new file mode 100644 index 0000000000..4b6f152517 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/difference/length.js b/test/built-ins/Set/prototype/difference/length.js new file mode 100644 index 0000000000..a6391cebdb --- /dev/null +++ b/test/built-ins/Set/prototype/difference/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference length property +info: | + Set.prototype.difference ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.difference, "function"); + +verifyProperty(Set.prototype.difference, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/difference/name.js b/test/built-ins/Set/prototype/difference/name.js new file mode 100644 index 0000000000..1e15f5566b --- /dev/null +++ b/test/built-ins/Set/prototype/difference/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference name property +info: | + Set.prototype.difference ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.difference, "function"); + +verifyProperty(Set.prototype.difference, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "difference", +}); diff --git a/test/built-ins/Set/prototype/difference/not-a-constructor.js b/test/built-ins/Set/prototype/difference/not-a-constructor.js new file mode 100644 index 0000000000..dcfaf84765 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.difference), + false, + "isConstructor(Set.prototype.difference) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.difference(); + }, + "`new Set.prototype.difference()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/difference/receiver-not-set.js b/test/built-ins/Set/prototype/difference/receiver-not-set.js new file mode 100644 index 0000000000..30ff97db38 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.difference.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.difference.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/difference/require-internal-slot.js b/test/built-ins/Set/prototype/difference/require-internal-slot.js new file mode 100644 index 0000000000..738e6fb3e2 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const difference = Set.prototype.difference; + +assert.sameValue(typeof difference, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => difference.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => difference.call(null, realSet), "null"); +assert.throws(TypeError, () => difference.call(true, realSet), "true"); +assert.throws(TypeError, () => difference.call("", realSet), "empty string"); +assert.throws(TypeError, () => difference.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => difference.call(1, realSet), "1"); +assert.throws(TypeError, () => difference.call(1n, realSet), "1n"); +assert.throws(TypeError, () => difference.call({}, realSet), "plain object"); +assert.throws(TypeError, () => difference.call([], realSet), "array"); +assert.throws(TypeError, () => difference.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => difference.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/difference/result-order.js b/test/built-ins/Set/prototype/difference/result-order.js new file mode 100644 index 0000000000..113d5024a3 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/result-order.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference orders results as in this, regardless of sizes +features: [set-methods] +includes: [compareArray.js] +---*/ + +{ + const s1 = new Set([1, 2, 3, 4]); + const s2 = new Set([6, 5, 3, 2]); + + assert.compareArray([...s1.difference(s2)], [1, 4]); +} + +{ + const s1 = new Set([6, 5, 3, 2]); + const s2 = new Set([1, 2, 3, 4]); + + assert.compareArray([...s1.difference(s2)], [6, 5]); +} + +{ + const s1 = new Set([1, 2, 3, 4]); + const s2 = new Set([7, 6, 5, 3, 2]); + + assert.compareArray([...s1.difference(s2)], [1, 4]); +} + +{ + const s1 = new Set([7, 6, 5, 3, 2]); + const s2 = new Set([1, 2, 3, 4]); + + assert.compareArray([...s1.difference(s2)], [7, 6, 5]); +} + diff --git a/test/built-ins/Set/prototype/difference/set-like-array.js b/test/built-ins/Set/prototype/difference/set-like-array.js new file mode 100644 index 0000000000..06342363bc --- /dev/null +++ b/test/built-ins/Set/prototype/difference/set-like-array.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference consumes a set-like array as a set-like, not an array +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [5, 6]; +s2.size = 3; +s2.has = function (v) { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.difference should only call its argument's has method with contents of this"); +}; +s2.keys = function () { + throw new Test262Error("Set.prototype.difference should not call its argument's keys iterator when this.size ≤ arg.size"); +}; + +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/difference/set-like-class-mutation.js b/test/built-ins/Set/prototype/difference/set-like-class-mutation.js new file mode 100644 index 0000000000..6e86953a29 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/set-like-class-mutation.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference maintains values even when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c", "d", "e"]); + +function mutatingIterator() { + let index = 0; + let values = ["x", "b", "b"]; + return { + next() { + if (index === 0) { + baseSet.delete("b"); + baseSet.delete("c"); + baseSet.add("b"); + baseSet.add("d"); + } + return { + done: index >= values.length, + value: values[index++], + }; + }, + }; +} + +const evilSetLike = { + size: 3, + get has() { + baseSet.add("q"); + return function () { + throw new Test262Error("Set.prototype.difference should not invoke .has on its argument when this.size > other.size"); + }; + }, + keys() { + return mutatingIterator(); + }, +}; + +const combined = baseSet.difference(evilSetLike); +const expectedCombined = ["a", "c", "d", "e", "q"]; +assert.compareArray([...combined], expectedCombined); + +const expectedNewBase = ["a", "d", "e", "q", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/difference/set-like-class-order.js b/test/built-ins/Set/prototype/difference/set-like-class-order.js new file mode 100644 index 0000000000..1364b23ab0 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/set-like-class-order.js @@ -0,0 +1,140 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +function observableIterator() { + let values = ["a", "b", "c"]; + let index = 0; + return { + get next() { + observedOrder.push("getting next"); + return function () { + observedOrder.push("calling next"); + return { + get done() { + observedOrder.push("getting done"); + return index >= values.length; + }, + get value() { + observedOrder.push("getting value"); + return values[index++]; + }, + }; + }; + }, + }; +} + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 3; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function (v) { + observedOrder.push("calling has"); + return ["a", "b", "c"].indexOf(v) !== -1; + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + observedOrder.push("calling keys"); + return observableIterator(); + }; + } +} + +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "d"]); + const s2 = new MySetLike(); + const combined = s1.difference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // two calls to has + "calling has", + "calling has", + ]; + + assert.compareArray([...combined], ["d"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d"]); + const s2 = new MySetLike(); + const combined = s1.difference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // three calls to has + "calling has", + "calling has", + "calling has", + ]; + + assert.compareArray([...combined], ["d"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c", "d"]); + const s2 = new MySetLike(); + const combined = s1.difference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.compareArray([...combined], ["d"]); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/difference/size-is-a-number.js b/test/built-ins/Set/prototype/difference/size-is-a-number.js new file mode 100644 index 0000000000..64811bd554 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.difference(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/difference/subclass-receiver-methods.js b/test/built-ins/Set/prototype/difference/subclass-receiver-methods.js new file mode 100644 index 0000000000..287f7dd783 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/subclass-receiver-methods.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +includes: [compareArray.js] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/difference/subclass-symbol-species.js b/test/built-ins/Set/prototype/difference/subclass-symbol-species.js new file mode 100644 index 0000000000..a67a780bcd --- /dev/null +++ b/test/built-ins/Set/prototype/difference/subclass-symbol-species.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference works on subclasses of Set, but returns an instance of Set even when Symbol.species is overridden. +features: [set-methods] +includes: [compareArray.js] +---*/ +var count = 0; +class MySet extends Set { + static get [Symbol.species]() { + count++; + return Set; + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(count, 0, "Symbol.species is never called"); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/difference/subclass.js b/test/built-ins/Set/prototype/difference/subclass.js new file mode 100644 index 0000000000..4cad090955 --- /dev/null +++ b/test/built-ins/Set/prototype/difference/subclass.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.difference +description: Set.prototype.difference works on subclasses of Set, but returns an instance of Set +features: [set-methods] +includes: [compareArray.js] +---*/ + +class MySet extends Set {} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1]; +const combined = s1.difference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/intersection/add-not-called.js b/test/built-ins/Set/prototype/intersection/add-not-called.js new file mode 100644 index 0000000000..37d189e214 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/add-not-called.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection should not call Set.prototype.add +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; + +const originalAdd = Set.prototype.add; +let count = 0; +Set.prototype.add = function (...rest) { + count++; + return originalAdd.apply(this, rest); +}; + +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(count, 0, "Add is never called"); + +Set.prototype.add = originalAdd; diff --git a/test/built-ins/Set/prototype/intersection/allows-set-like-class.js b/test/built-ins/Set/prototype/intersection/allows-set-like-class.js new file mode 100644 index 0000000000..779d6a2967 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/allows-set-like-class.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 2; + } + has(v) { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this"); + } + * keys() { + throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size"); + } +}; +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/allows-set-like-object.js b/test/built-ins/Set/prototype/intersection/allows-set-like-object.js new file mode 100644 index 0000000000..f127d8399b --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/allows-set-like-object.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: (v) => { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this"); + }, + keys: function* keys() { + throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size"); + }, +}; +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/array-throws.js b/test/built-ins/Set/prototype/intersection/array-throws.js new file mode 100644 index 0000000000..7977fe7448 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/intersection/builtins.js b/test/built-ins/Set/prototype/intersection/builtins.js new file mode 100644 index 0000000000..b17bd6645c --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Tests that Set.prototype.intersection meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.intersection), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.intersection), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.intersection), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/intersection/called-with-object.js b/test/built-ins/Set/prototype/intersection/called-with-object.js new file mode 100644 index 0000000000..c4f70a8ec2 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.intersection(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.intersection(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.intersection(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.intersection(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.intersection(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.intersection(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.intersection(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/intersection/combines-Map.js b/test/built-ins/Set/prototype/intersection/combines-Map.js new file mode 100644 index 0000000000..b63c51ec70 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/combines-Map.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection combines with Map +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); +const expected = [2]; +const combined = s1.intersection(m1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/combines-empty-sets.js b/test/built-ins/Set/prototype/intersection/combines-empty-sets.js new file mode 100644 index 0000000000..2fe3223c00 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/combines-empty-sets.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection can combine empty Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); +let expected = []; +let combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); +expected = []; +combined = s3.intersection(s4); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s5 = new Set([]); +const s6 = new Set([]); +expected = []; +combined = s5.intersection(s6); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/combines-itself.js b/test/built-ins/Set/prototype/intersection/combines-itself.js new file mode 100644 index 0000000000..82b8b09152 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/combines-itself.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection is successful when called on itself +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const expected = [1, 2]; +const combined = s1.intersection(s1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/intersection/combines-same-sets.js b/test/built-ins/Set/prototype/intersection/combines-same-sets.js new file mode 100644 index 0000000000..7906f8ca1c --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/combines-same-sets.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection can combine Sets that have the same content +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); +const expected = [1, 2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); +assert.sameValue(combined === s2, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/intersection/combines-sets.js b/test/built-ins/Set/prototype/intersection/combines-sets.js new file mode 100644 index 0000000000..fe059f0613 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/combines-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection combines Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/converts-negative-zero.js b/test/built-ins/Set/prototype/intersection/converts-negative-zero.js new file mode 100644 index 0000000000..0e3624e6b0 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/converts-negative-zero.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection converts -0𝔽 to +0𝔽 +info: | + 7.c.ii.2 If nextValue is -0𝔽, set nextValue to +0𝔽. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const setlikeWithMinusZero = { + size: 1, + has: function () { + throw new Test262Error("Set.prototype.intersection should not invoke .has on its argument when this.size > arg.size"); + }, + keys: function () { + // we use an array here because the Set constructor would normalize away -0 + return [-0].values(); + }, +}; + +const s1 = new Set([0, 1, 2]); +let expected = [+0]; +let combined = s1.intersection(setlikeWithMinusZero); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/has-is-callable.js b/test/built-ins/Set/prototype/intersection/has-is-callable.js new file mode 100644 index 0000000000..2cce0b4f58 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/intersection/intersection.js b/test/built-ins/Set/prototype/intersection/intersection.js new file mode 100644 index 0000000000..6ffb0b5f33 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/intersection.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.intersection, + "function", + "`typeof Set.prototype.intersection` is `'function'`" +); + +verifyProperty(Set.prototype, "intersection", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/intersection/keys-is-callable.js b/test/built-ins/Set/prototype/intersection/keys-is-callable.js new file mode 100644 index 0000000000..a02200fdc1 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/intersection/length.js b/test/built-ins/Set/prototype/intersection/length.js new file mode 100644 index 0000000000..333cbc60df --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection length property +info: | + Set.prototype.intersection ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.intersection, "function"); + +verifyProperty(Set.prototype.intersection, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/intersection/name.js b/test/built-ins/Set/prototype/intersection/name.js new file mode 100644 index 0000000000..760e81d77e --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection name property +info: | + Set.prototype.intersection ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.intersection, "function"); + +verifyProperty(Set.prototype.intersection, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "intersection", +}); diff --git a/test/built-ins/Set/prototype/intersection/not-a-constructor.js b/test/built-ins/Set/prototype/intersection/not-a-constructor.js new file mode 100644 index 0000000000..44c614f88b --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.intersection), + false, + "isConstructor(Set.prototype.intersection) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.intersection(); + }, + "`new Set.prototype.intersection()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/intersection/receiver-not-set.js b/test/built-ins/Set/prototype/intersection/receiver-not-set.js new file mode 100644 index 0000000000..78b3ebbb05 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.intersection.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.intersection.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/intersection/require-internal-slot.js b/test/built-ins/Set/prototype/intersection/require-internal-slot.js new file mode 100644 index 0000000000..840386d3c6 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const intersection = Set.prototype.intersection; + +assert.sameValue(typeof intersection, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => intersection.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => intersection.call(null, realSet), "null"); +assert.throws(TypeError, () => intersection.call(true, realSet), "true"); +assert.throws(TypeError, () => intersection.call("", realSet), "empty string"); +assert.throws(TypeError, () => intersection.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => intersection.call(1, realSet), "1"); +assert.throws(TypeError, () => intersection.call(1n, realSet), "1n"); +assert.throws(TypeError, () => intersection.call({}, realSet), "plain object"); +assert.throws(TypeError, () => intersection.call([], realSet), "array"); +assert.throws(TypeError, () => intersection.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => intersection.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/intersection/result-order.js b/test/built-ins/Set/prototype/intersection/result-order.js new file mode 100644 index 0000000000..dcdb3bbcf1 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/result-order.js @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection result ordering +features: [set-methods] +includes: [compareArray.js] +---*/ + +// when this.size ≤ other.size, results are ordered as in this +{ + const s1 = new Set([1, 3, 5]); + const s2 = new Set([3, 2, 1]); + + assert.compareArray([...s1.intersection(s2)], [1, 3]); +} + +{ + const s1 = new Set([3, 2, 1]); + const s2 = new Set([1, 3, 5]); + + assert.compareArray([...s1.intersection(s2)], [3, 1]); +} + +{ + const s1 = new Set([1, 3, 5]); + const s2 = new Set([3, 2, 1, 0]); + + assert.compareArray([...s1.intersection(s2)], [1, 3]); +} + +{ + const s1 = new Set([3, 2, 1]); + const s2 = new Set([1, 3, 5, 7]); + + assert.compareArray([...s1.intersection(s2)], [3, 1]); +} + + +// when this.size > other.size, results are ordered as in other +{ + const s1 = new Set([3, 2, 1, 0]); + const s2 = new Set([1, 3, 5]); + + assert.compareArray([...s1.intersection(s2)], [1, 3]); +} + +{ + const s1 = new Set([1, 3, 5, 7]); + const s2 = new Set([3, 2, 1]); + + assert.compareArray([...s1.intersection(s2)], [3, 1]); +} diff --git a/test/built-ins/Set/prototype/intersection/set-like-array.js b/test/built-ins/Set/prototype/intersection/set-like-array.js new file mode 100644 index 0000000000..6b5865343d --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/set-like-array.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection consumes a set-like array as a set-like, not an array +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [5, 6]; +s2.size = 3; +s2.has = function (v) { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.intersection should only call its argument's has method with contents of this"); +}; +s2.keys = function () { + throw new Test262Error("Set.prototype.intersection should not call its argument's keys iterator when this.size ≤ arg.size"); +}; + +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/intersection/set-like-class-mutation.js b/test/built-ins/Set/prototype/intersection/set-like-class-mutation.js new file mode 100644 index 0000000000..318b3ce54b --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/set-like-class-mutation.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection maintains values even when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c", "d", "e"]); + +function mutatingIterator() { + let index = 0; + let values = ["x", "b", "b"]; + return { + next() { + if (index === 0) { + baseSet.delete("b"); + baseSet.delete("c"); + baseSet.add("b"); + baseSet.add("d"); + } + return { + done: index >= values.length, + value: values[index++], + }; + }, + }; +} + +const evilSetLike = { + size: 3, + get has() { + baseSet.add("q"); + return function () { + throw new Test262Error("Set.prototype.intersection should not invoke .has on its argument when this.size > other.size"); + }; + }, + keys() { + return mutatingIterator(); + }, +}; + +const combined = baseSet.intersection(evilSetLike); +const expectedCombined = ["b"]; +assert.compareArray([...combined], expectedCombined); + +const expectedNewBase = ["a", "d", "e", "q", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/intersection/set-like-class-order.js b/test/built-ins/Set/prototype/intersection/set-like-class-order.js new file mode 100644 index 0000000000..1a069b8b2d --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/set-like-class-order.js @@ -0,0 +1,140 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +function observableIterator() { + let values = ["a", "b", "c"]; + let index = 0; + return { + get next() { + observedOrder.push("getting next"); + return function () { + observedOrder.push("calling next"); + return { + get done() { + observedOrder.push("getting done"); + return index >= values.length; + }, + get value() { + observedOrder.push("getting value"); + return values[index++]; + }, + }; + }; + }, + }; +} + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 3; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function (v) { + observedOrder.push("calling has"); + return ["a", "b", "c"].indexOf(v) !== -1; + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + observedOrder.push("calling keys"); + return observableIterator(); + }; + } +} + +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "d"]); + const s2 = new MySetLike(); + const combined = s1.intersection(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // two calls to has + "calling has", + "calling has", + ]; + + assert.compareArray([...combined], ["a"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d"]); + const s2 = new MySetLike(); + const combined = s1.intersection(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // three calls to has + "calling has", + "calling has", + "calling has", + ]; + + assert.compareArray([...combined], ["a", "b"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c", "d"]); + const s2 = new MySetLike(); + const combined = s1.intersection(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.compareArray([...combined], ["a", "b", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/intersection/size-is-a-number.js b/test/built-ins/Set/prototype/intersection/size-is-a-number.js new file mode 100644 index 0000000000..796a53ebda --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.intersection(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/intersection/subclass-receiver-methods.js b/test/built-ins/Set/prototype/intersection/subclass-receiver-methods.js new file mode 100644 index 0000000000..b5094552b8 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/subclass-receiver-methods.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +includes: [compareArray.js] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/intersection/subclass-symbol-species.js b/test/built-ins/Set/prototype/intersection/subclass-symbol-species.js new file mode 100644 index 0000000000..b5846c4911 --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/subclass-symbol-species.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection works on subclasses of Set, but returns an instance of Set even when Symbol.species is overridden. +features: [set-methods] +includes: [compareArray.js] +---*/ +var count = 0; +class MySet extends Set { + static get [Symbol.species]() { + count++; + return Set; + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(count, 0, "Symbol.species is never called"); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/intersection/subclass.js b/test/built-ins/Set/prototype/intersection/subclass.js new file mode 100644 index 0000000000..953b04cb3d --- /dev/null +++ b/test/built-ins/Set/prototype/intersection/subclass.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.intersection +description: Set.prototype.intersection works on subclasses of Set, but returns an instance of Set +features: [set-methods] +includes: [compareArray.js] +---*/ + +class MySet extends Set {} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [2]; +const combined = s1.intersection(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-class.js b/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-class.js new file mode 100644 index 0000000000..865a8ab853 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-class.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 2; + } + has(v) { + if (v === 1 || v === 2) return false; + throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this"); + } + * keys() { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size"); + } +}; + +assert.sameValue(s1.isDisjointFrom(s2), true); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-object.js b/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-object.js new file mode 100644 index 0000000000..8a0c2f00aa --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-object.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: (v) => { + if (v === 1 || v === 2) return false; + throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this"); + }, + keys: function* keys() { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size"); + }, +}; + +assert.sameValue(s1.isDisjointFrom(s2), true); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/array-throws.js b/test/built-ins/Set/prototype/isDisjointFrom/array-throws.js new file mode 100644 index 0000000000..f746984c8c --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/builtins.js b/test/built-ins/Set/prototype/isDisjointFrom/builtins.js new file mode 100644 index 0000000000..1a165b8aa3 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Tests that Set.prototype.isDisjointFrom meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.isDisjointFrom), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.isDisjointFrom), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.isDisjointFrom), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/called-with-object.js b/test/built-ins/Set/prototype/isDisjointFrom/called-with-object.js new file mode 100644 index 0000000000..2eba532e52 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/compares-Map.js b/test/built-ins/Set/prototype/isDisjointFrom/compares-Map.js new file mode 100644 index 0000000000..fede2e53d7 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/compares-Map.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom compares with Map +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); + +assert.sameValue(s1.isDisjointFrom(m1), false); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/compares-empty-sets.js b/test/built-ins/Set/prototype/isDisjointFrom/compares-empty-sets.js new file mode 100644 index 0000000000..8473172611 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/compares-empty-sets.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom can compare empty Sets +features: [set-methods] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isDisjointFrom(s2), true); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); + +assert.sameValue(s3.isDisjointFrom(s4), true); + +const s5 = new Set([]); +const s6 = new Set([]); + +assert.sameValue(s5.isDisjointFrom(s6), true); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/compares-itself.js b/test/built-ins/Set/prototype/isDisjointFrom/compares-itself.js new file mode 100644 index 0000000000..1268d0fc99 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/compares-itself.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom is successful when called on itself +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); + +assert.sameValue(s1.isDisjointFrom(s1), false); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/compares-same-sets.js b/test/built-ins/Set/prototype/isDisjointFrom/compares-same-sets.js new file mode 100644 index 0000000000..0d9d7c4235 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/compares-same-sets.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom can compare Sets that have the same content +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isDisjointFrom(s2), false); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/compares-sets.js b/test/built-ins/Set/prototype/isDisjointFrom/compares-sets.js new file mode 100644 index 0000000000..2f4d266d81 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/compares-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom compares Sets +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); + +assert.sameValue(s1.isDisjointFrom(s2), false); + +const s3 = new Set([3]); + +assert.sameValue(s1.isDisjointFrom(s3), true); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/converts-negative-zero.js b/test/built-ins/Set/prototype/isDisjointFrom/converts-negative-zero.js new file mode 100644 index 0000000000..c4b22d8cbf --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/converts-negative-zero.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom converts -0𝔽 to +0𝔽 +features: [set-methods] +---*/ + +const setlikeWithMinusZero = { + size: 1, + has: function () { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's has method when this.size > arg.size"); + }, + keys: function () { + // we use an array here because the Set constructor would normalize away -0 + return [-0].values(); + }, +}; + +const s1 = new Set([+0, 1]); + +assert.sameValue(s1.isDisjointFrom(setlikeWithMinusZero), false); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/has-is-callable.js b/test/built-ins/Set/prototype/isDisjointFrom/has-is-callable.js new file mode 100644 index 0000000000..48dae8b61e --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/isDisjointFrom.js b/test/built-ins/Set/prototype/isDisjointFrom/isDisjointFrom.js new file mode 100644 index 0000000000..c08965de96 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/isDisjointFrom.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.isDisjointFrom, + "function", + "`typeof Set.prototype.isDisjointFrom` is `'function'`" +); + +verifyProperty(Set.prototype, "isDisjointFrom", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/keys-is-callable.js b/test/built-ins/Set/prototype/isDisjointFrom/keys-is-callable.js new file mode 100644 index 0000000000..0c38a86d58 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/length.js b/test/built-ins/Set/prototype/isDisjointFrom/length.js new file mode 100644 index 0000000000..58cf4168b4 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom length property +info: | + Set.prototype.isDisjointFrom ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isDisjointFrom, "function"); + +verifyProperty(Set.prototype.isDisjointFrom, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/name.js b/test/built-ins/Set/prototype/isDisjointFrom/name.js new file mode 100644 index 0000000000..6ecdc6554a --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom name property +info: | + Set.prototype.isDisjointFrom ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isDisjointFrom, "function"); + +verifyProperty(Set.prototype.isDisjointFrom, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "isDisjointFrom", +}); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/not-a-constructor.js b/test/built-ins/Set/prototype/isDisjointFrom/not-a-constructor.js new file mode 100644 index 0000000000..98345917c3 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.isDisjointFrom), + false, + "isConstructor(Set.prototype.isDisjointFrom) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.isDisjointFrom(); + }, + "`new Set.prototype.isDisjointFrom()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/receiver-not-set.js b/test/built-ins/Set/prototype/isDisjointFrom/receiver-not-set.js new file mode 100644 index 0000000000..89723a41f6 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.isDisjointFrom.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.isDisjointFrom.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/require-internal-slot.js b/test/built-ins/Set/prototype/isDisjointFrom/require-internal-slot.js new file mode 100644 index 0000000000..6895ebbc56 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const isDisjointFrom = Set.prototype.isDisjointFrom; + +assert.sameValue(typeof isDisjointFrom, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => isDisjointFrom.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => isDisjointFrom.call(null, realSet), "null"); +assert.throws(TypeError, () => isDisjointFrom.call(true, realSet), "true"); +assert.throws(TypeError, () => isDisjointFrom.call("", realSet), "empty string"); +assert.throws(TypeError, () => isDisjointFrom.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => isDisjointFrom.call(1, realSet), "1"); +assert.throws(TypeError, () => isDisjointFrom.call(1n, realSet), "1n"); +assert.throws(TypeError, () => isDisjointFrom.call({}, realSet), "plain object"); +assert.throws(TypeError, () => isDisjointFrom.call([], realSet), "array"); +assert.throws(TypeError, () => isDisjointFrom.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => isDisjointFrom.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/set-like-array.js b/test/built-ins/Set/prototype/isDisjointFrom/set-like-array.js new file mode 100644 index 0000000000..ba3f1213ea --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/set-like-array.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom consumes a set-like array as a set-like, not an array +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [5, 6]; +s2.size = 3; +s2.has = function (v) { + if (v === 1 || v === 2) return true; + throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this"); +}; +s2.keys = function () { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size"); +}; + +assert.sameValue(s1.isDisjointFrom(s2), false); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js b/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js new file mode 100644 index 0000000000..38730eb18d --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js @@ -0,0 +1,38 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom behavior when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c"]); + +const evilSetLike = { + size: 3, + has(v) { + if (v === "a") { + baseSet.delete("b"); + baseSet.delete("c"); + baseSet.add("b"); + return false; + } + if (v === "b") { + return false; + } + if (v === "c") { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's has method with values from this which have been deleted before visiting"); + } + throw new Test262Error("Set.prototype.isDisjointFrom should only call its argument's has method with contents of this"); + }, + * keys() { + throw new Test262Error("Set.prototype.isDisjointFrom should not call its argument's keys iterator when this.size ≤ arg.size"); + }, +}; + +const result = baseSet.isDisjointFrom(evilSetLike); +assert.sameValue(result, true); + +const expectedNewBase = ["a", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-order.js b/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-order.js new file mode 100644 index 0000000000..11306e1bc4 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/set-like-class-order.js @@ -0,0 +1,191 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +function observableIterator() { + let values = ["a", "b", "c"]; + let index = 0; + return { + get next() { + observedOrder.push("getting next"); + return function () { + observedOrder.push("calling next"); + return { + get done() { + observedOrder.push("getting done"); + return index >= values.length; + }, + get value() { + observedOrder.push("getting value"); + return values[index++]; + }, + }; + }; + }, + }; +} + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 3; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function (v) { + observedOrder.push("calling has"); + return ["a", "b", "c"].indexOf(v) !== -1; + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + observedOrder.push("calling keys"); + return observableIterator(); + }; + } +} + +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["x", "b"]); + const s2 = new MySetLike(); + const result = s1.isDisjointFrom(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // two calls to has + "calling has", + "calling has", + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument - stops eagerly +{ + observedOrder = []; + + const s1 = new Set(["x", "b", "y"]); + const s2 = new MySetLike(); + const result = s1.isDisjointFrom(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // two calls to has + "calling has", + "calling has", + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument - full run +{ + observedOrder = []; + + const s1 = new Set(["x", "y", "z"]); + const s2 = new MySetLike(); + const result = s1.isDisjointFrom(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // three calls to has + "calling has", + "calling has", + "calling has", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument - stops eagerly +{ + observedOrder = []; + + const s1 = new Set(["x", "b", "y", "z"]); + const s2 = new MySetLike(); + const result = s1.isDisjointFrom(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument - full run +{ + observedOrder = []; + + const s1 = new Set(["x", "y", "z", "w"]); + const s2 = new MySetLike(); + const result = s1.isDisjointFrom(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, done + "calling next", + "getting done", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/isDisjointFrom/size-is-a-number.js b/test/built-ins/Set/prototype/isDisjointFrom/size-is-a-number.js new file mode 100644 index 0000000000..cecb75ae4a --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.isDisjointFrom(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/isDisjointFrom/subclass-receiver-methods.js b/test/built-ins/Set/prototype/isDisjointFrom/subclass-receiver-methods.js new file mode 100644 index 0000000000..bb35d25241 --- /dev/null +++ b/test/built-ins/Set/prototype/isDisjointFrom/subclass-receiver-methods.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.isdisjointfrom +description: Set.prototype.isDisjointFrom works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const result = s1.isDisjointFrom(s2); +assert.sameValue(result, false); + +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-class.js b/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-class.js new file mode 100644 index 0000000000..c58a317913 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-class.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 3; + } + has(v) { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this"); + } + * keys() { + throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator"); + } +}; + +assert.sameValue(s1.isSubsetOf(s2), false); diff --git a/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-object.js b/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-object.js new file mode 100644 index 0000000000..d9416bf2c4 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/allows-set-like-object.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: (v) => { + if (v === 1) return false; + if (v === 2) return true; + throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this"); + }, + keys: function* keys() { + throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator"); + }, +}; + +assert.sameValue(s1.isSubsetOf(s2), false); diff --git a/test/built-ins/Set/prototype/isSubsetOf/array-throws.js b/test/built-ins/Set/prototype/isSubsetOf/array-throws.js new file mode 100644 index 0000000000..1702c45666 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/builtins.js b/test/built-ins/Set/prototype/isSubsetOf/builtins.js new file mode 100644 index 0000000000..8df9c8d0d2 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Tests that Set.prototype.isSubsetOf meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.isSubsetOf), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.isSubsetOf), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.isSubsetOf), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/called-with-object.js b/test/built-ins/Set/prototype/isSubsetOf/called-with-object.js new file mode 100644 index 0000000000..165dd38db7 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.isSubsetOf(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.isSubsetOf(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/compares-Map.js b/test/built-ins/Set/prototype/isSubsetOf/compares-Map.js new file mode 100644 index 0000000000..0a0d6f0b18 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/compares-Map.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf compares with Map +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); + +assert.sameValue(s1.isSubsetOf(m1), false); diff --git a/test/built-ins/Set/prototype/isSubsetOf/compares-empty-sets.js b/test/built-ins/Set/prototype/isSubsetOf/compares-empty-sets.js new file mode 100644 index 0000000000..46c286e8f8 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/compares-empty-sets.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf can compare empty Sets +features: [set-methods] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isSubsetOf(s2), true); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); + +assert.sameValue(s3.isSubsetOf(s4), false); + +const s5 = new Set([]); +const s6 = new Set([]); + +assert.sameValue(s5.isSubsetOf(s6), true); diff --git a/test/built-ins/Set/prototype/isSubsetOf/compares-itself.js b/test/built-ins/Set/prototype/isSubsetOf/compares-itself.js new file mode 100644 index 0000000000..31c59872a1 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/compares-itself.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf is successful when called on itself +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); + +assert.sameValue(s1.isSubsetOf(s1), true); diff --git a/test/built-ins/Set/prototype/isSubsetOf/compares-same-sets.js b/test/built-ins/Set/prototype/isSubsetOf/compares-same-sets.js new file mode 100644 index 0000000000..a05ba0e70c --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/compares-same-sets.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf can compare Sets that have the same content +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isSubsetOf(s2), true); diff --git a/test/built-ins/Set/prototype/isSubsetOf/compares-sets.js b/test/built-ins/Set/prototype/isSubsetOf/compares-sets.js new file mode 100644 index 0000000000..d8901c1b4d --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/compares-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf compares Sets +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); + +assert.sameValue(s1.isSubsetOf(s2), false); + +const s3 = new Set([1, 2, 3]); + +assert.sameValue(s1.isSubsetOf(s3), true); diff --git a/test/built-ins/Set/prototype/isSubsetOf/has-is-callable.js b/test/built-ins/Set/prototype/isSubsetOf/has-is-callable.js new file mode 100644 index 0000000000..2bdebd51ed --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/isSubsetOf.js b/test/built-ins/Set/prototype/isSubsetOf/isSubsetOf.js new file mode 100644 index 0000000000..333b6feee3 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/isSubsetOf.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.isSubsetOf, + "function", + "`typeof Set.prototype.isSubsetOf` is `'function'`" +); + +verifyProperty(Set.prototype, "isSubsetOf", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/isSubsetOf/keys-is-callable.js b/test/built-ins/Set/prototype/isSubsetOf/keys-is-callable.js new file mode 100644 index 0000000000..de8658f1a7 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/length.js b/test/built-ins/Set/prototype/isSubsetOf/length.js new file mode 100644 index 0000000000..03a10f668b --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf length property +info: | + Set.prototype.isSubsetOf ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isSubsetOf, "function"); + +verifyProperty(Set.prototype.isSubsetOf, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/isSubsetOf/name.js b/test/built-ins/Set/prototype/isSubsetOf/name.js new file mode 100644 index 0000000000..b886750345 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf name property +info: | + Set.prototype.isSubsetOf ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isSubsetOf, "function"); + +verifyProperty(Set.prototype.isSubsetOf, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "isSubsetOf", +}); diff --git a/test/built-ins/Set/prototype/isSubsetOf/not-a-constructor.js b/test/built-ins/Set/prototype/isSubsetOf/not-a-constructor.js new file mode 100644 index 0000000000..27f90f1ac6 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.isSubsetOf), + false, + "isConstructor(Set.prototype.isSubsetOf) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.isSubsetOf(); + }, + "`new Set.prototype.isSubsetOf()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/receiver-not-set.js b/test/built-ins/Set/prototype/isSubsetOf/receiver-not-set.js new file mode 100644 index 0000000000..33bdac5cb1 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.isSubsetOf.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.isSubsetOf.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/require-internal-slot.js b/test/built-ins/Set/prototype/isSubsetOf/require-internal-slot.js new file mode 100644 index 0000000000..42b8970cb1 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const isSubsetOf = Set.prototype.isSubsetOf; + +assert.sameValue(typeof isSubsetOf, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => isSubsetOf.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => isSubsetOf.call(null, realSet), "null"); +assert.throws(TypeError, () => isSubsetOf.call(true, realSet), "true"); +assert.throws(TypeError, () => isSubsetOf.call("", realSet), "empty string"); +assert.throws(TypeError, () => isSubsetOf.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => isSubsetOf.call(1, realSet), "1"); +assert.throws(TypeError, () => isSubsetOf.call(1n, realSet), "1n"); +assert.throws(TypeError, () => isSubsetOf.call({}, realSet), "plain object"); +assert.throws(TypeError, () => isSubsetOf.call([], realSet), "array"); +assert.throws(TypeError, () => isSubsetOf.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => isSubsetOf.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/isSubsetOf/set-like-array.js b/test/built-ins/Set/prototype/isSubsetOf/set-like-array.js new file mode 100644 index 0000000000..a5e586cf61 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/set-like-array.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf consumes a set-like array as a set-like, not an array +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [5, 6]; +s2.size = 3; +s2.has = function (v) { + if (v === 1) return true; + if (v === 2) return true; + throw new Test262Error("Set.prototype.isSubsetOf should only call its argument's has method with contents of this"); +}; +s2.keys = function () { + throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator when this.size ≤ arg.size"); +}; + +assert.sameValue(s1.isSubsetOf(s2), true); diff --git a/test/built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js b/test/built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js new file mode 100644 index 0000000000..92a5d886f3 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf behavior when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c"]); + +const evilSetLike = { + size: 3, + has(v) { + if (v === "a") { + baseSet.delete("c"); + } + return ["x", "a", "b"].includes(v); + }, + * keys() { + throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator"); + }, +}; + +const result = baseSet.isSubsetOf(evilSetLike); +assert.sameValue(result, true); + +const expectedNewBase = ["a", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/isSubsetOf/set-like-class-order.js b/test/built-ins/Set/prototype/isSubsetOf/set-like-class-order.js new file mode 100644 index 0000000000..57064af4b0 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/set-like-class-order.js @@ -0,0 +1,121 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 3; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function (v) { + observedOrder.push("calling has"); + return ["a", "b", "c"].indexOf(v) !== -1; + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + throw new Test262Error("Set.prototype.isSubsetOf should not call its argument's keys iterator"); + }; + } +} + +// this is smaller than argument - stops eagerly +{ + observedOrder = []; + + const s1 = new Set(["d", "a"]); + const s2 = new MySetLike(); + const result = s1.isSubsetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // one call to has + "calling has", + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is smaller than argument - full run +{ + observedOrder = []; + + const s1 = new Set(["a", "b"]); + const s2 = new MySetLike(); + const result = s1.isSubsetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // two calls to has + "calling has", + "calling has", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c"]); + const s2 = new MySetLike(); + const result = s1.isSubsetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // three calls to has + "calling has", + "calling has", + "calling has", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c", "d"]); + const s2 = new MySetLike(); + const result = s1.isSubsetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // no calls to has + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/isSubsetOf/size-is-a-number.js b/test/built-ins/Set/prototype/isSubsetOf/size-is-a-number.js new file mode 100644 index 0000000000..533d85224f --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.isSubsetOf(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/isSubsetOf/subclass-receiver-methods.js b/test/built-ins/Set/prototype/isSubsetOf/subclass-receiver-methods.js new file mode 100644 index 0000000000..2afeaab870 --- /dev/null +++ b/test/built-ins/Set/prototype/isSubsetOf/subclass-receiver-methods.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issubsetof +description: Set.prototype.isSubsetOf works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const result = s1.isSubsetOf(s2); +assert.sameValue(result, false); + +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-class.js b/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-class.js new file mode 100644 index 0000000000..3bff42a5fd --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-class.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 1; + } + has(v) { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's has method"); + } + * keys() { + yield 1; + } +}; + +assert.sameValue(s1.isSupersetOf(s2), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-object.js b/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-object.js new file mode 100644 index 0000000000..10a886ba49 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/allows-set-like-object.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 1, + has: (v) => { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's has method"); + }, + keys: function* keys() { + yield 1; + }, +}; + +assert.sameValue(s1.isSupersetOf(s2), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/array-throws.js b/test/built-ins/Set/prototype/isSupersetOf/array-throws.js new file mode 100644 index 0000000000..f924a2f597 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/builtins.js b/test/built-ins/Set/prototype/isSupersetOf/builtins.js new file mode 100644 index 0000000000..507c572c20 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Tests that Set.prototype.isSupersetOf meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.isSupersetOf), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.isSupersetOf), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.isSupersetOf), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/called-with-object.js b/test/built-ins/Set/prototype/isSupersetOf/called-with-object.js new file mode 100644 index 0000000000..dd541a5b50 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.isSupersetOf(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.isSupersetOf(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/compares-Map.js b/test/built-ins/Set/prototype/isSupersetOf/compares-Map.js new file mode 100644 index 0000000000..0bc5033179 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/compares-Map.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf compares with Map +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); + +assert.sameValue(s1.isSupersetOf(m1), false); diff --git a/test/built-ins/Set/prototype/isSupersetOf/compares-empty-sets.js b/test/built-ins/Set/prototype/isSupersetOf/compares-empty-sets.js new file mode 100644 index 0000000000..fca7aee7d9 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/compares-empty-sets.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf can compare empty Sets +features: [set-methods] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isSupersetOf(s2), false); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); + +assert.sameValue(s3.isSupersetOf(s4), true); + +const s5 = new Set([]); +const s6 = new Set([]); + +assert.sameValue(s5.isSupersetOf(s6), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/compares-itself.js b/test/built-ins/Set/prototype/isSupersetOf/compares-itself.js new file mode 100644 index 0000000000..13a1befe77 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/compares-itself.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf is successful when called on itself +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); + +assert.sameValue(s1.isSupersetOf(s1), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/compares-same-sets.js b/test/built-ins/Set/prototype/isSupersetOf/compares-same-sets.js new file mode 100644 index 0000000000..dbf626783a --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/compares-same-sets.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf can compare Sets that have the same content +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); + +assert.sameValue(s1.isSupersetOf(s2), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/compares-sets.js b/test/built-ins/Set/prototype/isSupersetOf/compares-sets.js new file mode 100644 index 0000000000..cbd044b88d --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/compares-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf compares Sets +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); + +assert.sameValue(s1.isSupersetOf(s2), false); + +const s3 = new Set([1]); + +assert.sameValue(s1.isSupersetOf(s3), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/converts-negative-zero.js b/test/built-ins/Set/prototype/isSupersetOf/converts-negative-zero.js new file mode 100644 index 0000000000..61c534caad --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/converts-negative-zero.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf converts -0𝔽 to +0𝔽 +features: [set-methods] +---*/ + +const setlikeWithMinusZero = { + size: 1, + has: function () { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's has method"); + }, + keys: function () { + // we use an array here because the Set constructor would normalize away -0 + return [-0].values(); + }, +}; + +const s1 = new Set([+0, 1]); + +assert.sameValue(s1.isSupersetOf(setlikeWithMinusZero), true); diff --git a/test/built-ins/Set/prototype/isSupersetOf/has-is-callable.js b/test/built-ins/Set/prototype/isSupersetOf/has-is-callable.js new file mode 100644 index 0000000000..6d940fe3a9 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/isSupersetOf.js b/test/built-ins/Set/prototype/isSupersetOf/isSupersetOf.js new file mode 100644 index 0000000000..5acf9cb06a --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/isSupersetOf.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.isSupersetOf, + "function", + "`typeof Set.prototype.isSupersetOf` is `'function'`" +); + +verifyProperty(Set.prototype, "isSupersetOf", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/isSupersetOf/keys-is-callable.js b/test/built-ins/Set/prototype/isSupersetOf/keys-is-callable.js new file mode 100644 index 0000000000..041aa13b4b --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/length.js b/test/built-ins/Set/prototype/isSupersetOf/length.js new file mode 100644 index 0000000000..e19d92f099 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf length property +info: | + Set.prototype.isSupersetOf ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isSupersetOf, "function"); + +verifyProperty(Set.prototype.isSupersetOf, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/isSupersetOf/name.js b/test/built-ins/Set/prototype/isSupersetOf/name.js new file mode 100644 index 0000000000..715df87654 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf name property +info: | + Set.prototype.isSupersetOf ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.isSupersetOf, "function"); + +verifyProperty(Set.prototype.isSupersetOf, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "isSupersetOf", +}); diff --git a/test/built-ins/Set/prototype/isSupersetOf/not-a-constructor.js b/test/built-ins/Set/prototype/isSupersetOf/not-a-constructor.js new file mode 100644 index 0000000000..924cea71ca --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.isSupersetOf), + false, + "isConstructor(Set.prototype.isSupersetOf) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.isSupersetOf(); + }, + "`new Set.prototype.isSupersetOf()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/receiver-not-set.js b/test/built-ins/Set/prototype/isSupersetOf/receiver-not-set.js new file mode 100644 index 0000000000..4a8c4aa21f --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.isSupersetOf.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.isSupersetOf.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/require-internal-slot.js b/test/built-ins/Set/prototype/isSupersetOf/require-internal-slot.js new file mode 100644 index 0000000000..fd26fde9ac --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const isSupersetOf = Set.prototype.isSupersetOf; + +assert.sameValue(typeof isSupersetOf, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => isSupersetOf.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => isSupersetOf.call(null, realSet), "null"); +assert.throws(TypeError, () => isSupersetOf.call(true, realSet), "true"); +assert.throws(TypeError, () => isSupersetOf.call("", realSet), "empty string"); +assert.throws(TypeError, () => isSupersetOf.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => isSupersetOf.call(1, realSet), "1"); +assert.throws(TypeError, () => isSupersetOf.call(1n, realSet), "1n"); +assert.throws(TypeError, () => isSupersetOf.call({}, realSet), "plain object"); +assert.throws(TypeError, () => isSupersetOf.call([], realSet), "array"); +assert.throws(TypeError, () => isSupersetOf.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => isSupersetOf.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/isSupersetOf/set-like-array.js b/test/built-ins/Set/prototype/isSupersetOf/set-like-array.js new file mode 100644 index 0000000000..bae1f37152 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/set-like-array.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf consumes a set-like array as a set-like, not an array +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [1]; +s2.size = 3; +s2.has = function (v) { + if (v === 1) return true; + if (v === 2) return true; + throw new Test262Error("Set.prototype.isSupersetOf should only call its argument's has method with contents of this"); +}; +s2.keys = function () { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's keys iterator when this.size ≤ arg.size"); +}; + +assert.sameValue(s1.isSupersetOf(s2), false); diff --git a/test/built-ins/Set/prototype/isSupersetOf/set-like-class-mutation.js b/test/built-ins/Set/prototype/isSupersetOf/set-like-class-mutation.js new file mode 100644 index 0000000000..816ac72eb7 --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/set-like-class-mutation.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf behavior when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c"]); + +const evilSetLike = { + size: 3, + has(v) { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's has method"); + }, + * keys() { + yield "a"; + baseSet.delete("b"); + baseSet.delete("c"); + baseSet.add("b"); + yield "b"; + }, +}; + +const result = baseSet.isSupersetOf(evilSetLike); +assert.sameValue(result, true); + +const expectedNewBase = ["a", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/isSupersetOf/set-like-class-order.js b/test/built-ins/Set/prototype/isSupersetOf/set-like-class-order.js new file mode 100644 index 0000000000..94329271af --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/set-like-class-order.js @@ -0,0 +1,179 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +function observableIterator() { + let values = ["a", "b", "c"]; + let index = 0; + return { + get next() { + observedOrder.push("getting next"); + return function () { + observedOrder.push("calling next"); + return { + get done() { + observedOrder.push("getting done"); + return index >= values.length; + }, + get value() { + observedOrder.push("getting value"); + return values[index++]; + }, + }; + }; + }, + }; +} + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 3; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function (v) { + throw new Test262Error("Set.prototype.isSupersetOf should not call its argument's has method"); + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + observedOrder.push("calling keys"); + return observableIterator(); + }; + } +} + +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b"]); + const s2 = new MySetLike(); + const result = s1.isSupersetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + // no iteration + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument - stops eagerly +{ + observedOrder = []; + + const s1 = new Set(["a", "x", "c"]); + const s2 = new MySetLike(); + const result = s1.isSupersetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + ]; + + assert.sameValue(result, false); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument - full run +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c"]); + const s2 = new MySetLike(); + const result = s1.isSupersetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "c", "d"]); + const s2 = new MySetLike(); + const result = s1.isSupersetOf(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.sameValue(result, true); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/isSupersetOf/size-is-a-number.js b/test/built-ins/Set/prototype/isSupersetOf/size-is-a-number.js new file mode 100644 index 0000000000..311294757f --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.isSupersetOf(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/isSupersetOf/subclass-receiver-methods.js b/test/built-ins/Set/prototype/isSupersetOf/subclass-receiver-methods.js new file mode 100644 index 0000000000..a96bceb5ad --- /dev/null +++ b/test/built-ins/Set/prototype/isSupersetOf/subclass-receiver-methods.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.issupersetof +description: Set.prototype.isSupersetOf works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const result = s1.isSupersetOf(s2); +assert.sameValue(result, false); + +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/add-not-called.js b/test/built-ins/Set/prototype/symmetricDifference/add-not-called.js new file mode 100644 index 0000000000..a8917a9d9a --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/add-not-called.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference should not call Set.prototype.add +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1, 3]; + +const originalAdd = Set.prototype.add; +let count = 0; +Set.prototype.add = function (...rest) { + count++; + return originalAdd.apply(this, rest); +}; + +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(count, 0, "Add is never called"); + +Set.prototype.add = originalAdd; diff --git a/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-class.js b/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-class.js new file mode 100644 index 0000000000..3b83017ba5 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-class.js @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: GetSetRecord allows instances of Set-like classes +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new class { + get size() { + return 2; + } + has(v) { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); + } + * keys() { + yield 2; + yield 3; + } +}; +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-object.js b/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-object.js new file mode 100644 index 0000000000..f092f02175 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/allows-set-like-object.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: GetSetRecord allows Set-like objects +info: | + 1. If obj is not an Object, throw a TypeError exception. + 2. Let rawSize be ? Get(obj, "size"). + ... + 7. Let has be ? Get(obj, "has"). + ... + 9. Let keys be ? Get(obj, "keys"). +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: (v) => { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); + }, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/array-throws.js b/test/built-ins/Set/prototype/symmetricDifference/array-throws.js new file mode 100644 index 0000000000..4481b1ff8b --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/array-throws.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference doesn't work with arrays +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [3]; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "Throws an error when an array is used" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/builtins.js b/test/built-ins/Set/prototype/symmetricDifference/builtins.js new file mode 100644 index 0000000000..3b5f9a65d3 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/builtins.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Tests that Set.prototype.symmetricDifference meets the requirements for built-in objects +features: [set-methods] +---*/ + +assert.sameValue( + Object.isExtensible(Set.prototype.symmetricDifference), + true, + "Built-in objects must be extensible." +); + +assert.sameValue( + Object.prototype.toString.call(Set.prototype.symmetricDifference), + "[object Function]", + "Object.prototype.toString" +); + +assert.sameValue( + Object.getPrototypeOf(Set.prototype.symmetricDifference), + Function.prototype, + "prototype" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/called-with-object.js b/test/built-ins/Set/prototype/symmetricDifference/called-with-object.js new file mode 100644 index 0000000000..87ef3ed20a --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/called-with-object.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws if obj is not an object +info: | + 1. If obj is not an Object, throw a TypeError exception. +features: [set-methods] +---*/ + +let s1 = new Set([1]); +assert.throws( + TypeError, + function () { + s1.symmetricDifference(1); + }, + "number" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(""); + }, + "string" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(1n); + }, + "bigint" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(false); + }, + "boolean" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(undefined); + }, + "undefined" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(null); + }, + "null" +); + +assert.throws( + TypeError, + function () { + s1.symmetricDifference(Symbol("test")); + }, + "symbol" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/combines-Map.js b/test/built-ins/Set/prototype/symmetricDifference/combines-Map.js new file mode 100644 index 0000000000..83d8c81816 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/combines-Map.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference combines with Map +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const m1 = new Map([ + [2, "two"], + [3, "three"], +]); +const expected = [1, 3]; +const combined = s1.symmetricDifference(m1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/combines-empty-sets.js b/test/built-ins/Set/prototype/symmetricDifference/combines-empty-sets.js new file mode 100644 index 0000000000..cb54d392c0 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/combines-empty-sets.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference can combine empty Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([]); +const s2 = new Set([1, 2]); +let expected = [1, 2]; +let combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s3 = new Set([1, 2]); +const s4 = new Set([]); +expected = [1, 2]; +combined = s3.symmetricDifference(s4); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); + +const s5 = new Set([]); +const s6 = new Set([]); +expected = []; +combined = s5.symmetricDifference(s6); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/combines-itself.js b/test/built-ins/Set/prototype/symmetricDifference/combines-itself.js new file mode 100644 index 0000000000..ee20b58802 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/combines-itself.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference is successful when called on itself +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const expected = []; +const combined = s1.symmetricDifference(s1); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/combines-same-sets.js b/test/built-ins/Set/prototype/symmetricDifference/combines-same-sets.js new file mode 100644 index 0000000000..8d1e1f2d95 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/combines-same-sets.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference can combine Sets that have the same content +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([1, 2]); +const expected = []; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); +assert.sameValue(combined === s2, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/combines-sets.js b/test/built-ins/Set/prototype/symmetricDifference/combines-sets.js new file mode 100644 index 0000000000..b995d7de1d --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/combines-sets.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference combines Sets +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js b/test/built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js new file mode 100644 index 0000000000..927743f157 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference converts -0𝔽 to +0𝔽 +info: | + 7.b.ii If nextValue is -0𝔽, set nextValue to +0𝔽. +features: [set-methods] +includes: [compareArray.js] +---*/ + +const setlikeWithMinusZero = { + size: 1, + has: function () { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); + }, + keys: function () { + // we use an array here because the Set constructor would normalize away -0 + return [-0].values(); + }, +}; + +const s1 = new Set([1, 2]); +let expected = [1, 2, +0]; +let combined = s1.symmetricDifference(setlikeWithMinusZero); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/has-is-callable.js b/test/built-ins/Set/prototype/symmetricDifference/has-is-callable.js new file mode 100644 index 0000000000..17e903e8c3 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/has-is-callable.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'has' property is not callable +info: | + 7. Let has be ? Get(obj, "has"). + 8. If IsCallable(has) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: undefined, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when has is undefined" +); + +s2.has = {}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when has is not callable" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/keys-is-callable.js b/test/built-ins/Set/prototype/symmetricDifference/keys-is-callable.js new file mode 100644 index 0000000000..fa2faac7dc --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/keys-is-callable.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object's 'keys' property is not callable +info: | + 9. Let keys be ? Get(obj, "keys"). + 10. If IsCallable(keys) is false, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: 2, + has: () => {}, + keys: undefined, +}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when keys is undefined" +); + +s2.keys = {}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when keys is not callable" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/length.js b/test/built-ins/Set/prototype/symmetricDifference/length.js new file mode 100644 index 0000000000..3d73327b0f --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/length.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference length property +info: | + Set.prototype.symmetricDifference ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.symmetricDifference, "function"); + +verifyProperty(Set.prototype.symmetricDifference, "length", { + enumerable: false, + writable: false, + configurable: true, + value: 1, +}); diff --git a/test/built-ins/Set/prototype/symmetricDifference/name.js b/test/built-ins/Set/prototype/symmetricDifference/name.js new file mode 100644 index 0000000000..a31b70efac --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/name.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference name property +info: | + Set.prototype.symmetricDifference ( other ) +includes: [propertyHelper.js] +features: [set-methods] +---*/ +assert.sameValue(typeof Set.prototype.symmetricDifference, "function"); + +verifyProperty(Set.prototype.symmetricDifference, "name", { + enumerable: false, + writable: false, + configurable: true, + value: "symmetricDifference", +}); diff --git a/test/built-ins/Set/prototype/symmetricDifference/not-a-constructor.js b/test/built-ins/Set/prototype/symmetricDifference/not-a-constructor.js new file mode 100644 index 0000000000..81e19f00e3 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/not-a-constructor.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference does not implement [[Construct]], is not new-able +includes: [isConstructor.js] +features: [Reflect.construct, set-methods] +---*/ + +assert.sameValue( + isConstructor(Set.prototype.symmetricDifference), + false, + "isConstructor(Set.prototype.symmetricDifference) must return false" +); + +assert.throws( + TypeError, + () => { + new Set.prototype.symmetricDifference(); + }, + "`new Set.prototype.symmetricDifference()` throws TypeError" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/receiver-not-set.js b/test/built-ins/Set/prototype/symmetricDifference/receiver-not-set.js new file mode 100644 index 0000000000..6b5156d262 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/receiver-not-set.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference throws when receiver is not a Set +features: [set-methods] +---*/ + +class MySetLike { + constructor() { + this.size = 2; + this.has = () => {}; + this.keys = function* keys() { + yield 2; + yield 3; + }; + } +} + +const s1 = new MySetLike(); +const s2 = new Set(); +assert.throws( + TypeError, + () => { + Set.prototype.symmetricDifference.call(s1, s2); + }, + "Set-like class" +); + +const s3 = { + size: 2, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + () => { + Set.prototype.symmetricDifference.call(s3, s2); + }, + "Set-like object" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/require-internal-slot.js b/test/built-ins/Set/prototype/symmetricDifference/require-internal-slot.js new file mode 100644 index 0000000000..27def7fd3b --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/require-internal-slot.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference RequireInternalSlot +info: | + 2. Perform ? RequireInternalSlot(O, [[SetData]]) +features: [set-methods] +---*/ + +const symmetricDifference = Set.prototype.symmetricDifference; + +assert.sameValue(typeof symmetricDifference, "function"); + +const realSet = new Set([]); + +assert.throws(TypeError, () => symmetricDifference.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => symmetricDifference.call(null, realSet), "null"); +assert.throws(TypeError, () => symmetricDifference.call(true, realSet), "true"); +assert.throws(TypeError, () => symmetricDifference.call("", realSet), "empty string"); +assert.throws(TypeError, () => symmetricDifference.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => symmetricDifference.call(1, realSet), "1"); +assert.throws(TypeError, () => symmetricDifference.call(1n, realSet), "1n"); +assert.throws(TypeError, () => symmetricDifference.call({}, realSet), "plain object"); +assert.throws(TypeError, () => symmetricDifference.call([], realSet), "array"); +assert.throws(TypeError, () => symmetricDifference.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => symmetricDifference.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/result-order.js b/test/built-ins/Set/prototype/symmetricDifference/result-order.js new file mode 100644 index 0000000000..c2f203d6bf --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/result-order.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference result ordering +features: [set-methods] +includes: [compareArray.js] +---*/ + +// results are ordered as in this, then as in other +{ + const s1 = new Set([1, 2, 3, 4]); + const s2 = new Set([6, 5, 4, 3]); + + assert.compareArray([...s1.symmetricDifference(s2)], [1, 2, 6, 5]); +} + +{ + const s1 = new Set([6, 5, 4, 3]); + const s2 = new Set([1, 2, 3, 4]); + + assert.compareArray([...s1.symmetricDifference(s2)], [6, 5, 1, 2]); +} diff --git a/test/built-ins/Set/prototype/symmetricDifference/set-like-array.js b/test/built-ins/Set/prototype/symmetricDifference/set-like-array.js new file mode 100644 index 0000000000..0c8c2e0083 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/set-like-array.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference consumes a set-like array as a set-like, not an array +features: [set-methods] +includes: [compareArray.js] +---*/ + +const s1 = new Set([1, 2]); +const s2 = [5]; +s2.size = 3; +s2.has = function (v) { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); +}; +s2.keys = function () { + return [2, 3, 4].values(); +}; + +const expected = [1, 3, 4]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/set-like-class-mutation.js b/test/built-ins/Set/prototype/symmetricDifference/set-like-class-mutation.js new file mode 100644 index 0000000000..b18dd568d6 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/set-like-class-mutation.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference maintains values even when a custom Set-like class mutates the receiver +features: [set-methods] +includes: [compareArray.js] +---*/ + +const baseSet = new Set(["a", "b", "c", "d", "e"]); + +function mutatingIterator() { + let index = 0; + let values = ["x", "b", "c", "c"]; + return { + next() { + if (index === 0) { + baseSet.delete("b"); + baseSet.delete("c"); + baseSet.add("b"); + baseSet.add("d"); + } + return { + done: index >= values.length, + value: values[index++], + }; + }, + }; +} + +const evilSetLike = { + size: 4, + get has() { + baseSet.add("q"); + return function () { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); + }; + }, + keys() { + return mutatingIterator(); + }, +}; + +const combined = baseSet.symmetricDifference(evilSetLike); +const expectedCombined = ["a", "c", "d", "e", "q", "x"]; +assert.compareArray([...combined], expectedCombined); + +const expectedNewBase = ["a", "d", "e", "q", "b"]; +assert.compareArray([...baseSet], expectedNewBase); diff --git a/test/built-ins/Set/prototype/symmetricDifference/set-like-class-order.js b/test/built-ins/Set/prototype/symmetricDifference/set-like-class-order.js new file mode 100644 index 0000000000..1a0a870afa --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/set-like-class-order.js @@ -0,0 +1,166 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference calls a Set-like class's methods in order +features: [set-methods] +includes: [compareArray.js] +---*/ + +let observedOrder = []; + +function observableIterator() { + let values = ["a", "b", "c"]; + let index = 0; + return { + get next() { + observedOrder.push("getting next"); + return function () { + observedOrder.push("calling next"); + return { + get done() { + observedOrder.push("getting done"); + return index >= values.length; + }, + get value() { + observedOrder.push("getting value"); + return values[index++]; + }, + }; + }; + }, + }; +} + +class MySetLike { + get size() { + observedOrder.push("getting size"); + return { + valueOf: function () { + observedOrder.push("ToNumber(size)"); + return 2; + }, + }; + } + get has() { + observedOrder.push("getting has"); + return function () { + throw new Test262Error("Set.prototype.symmetricDifference should not invoke .has on its argument"); + }; + } + get keys() { + observedOrder.push("getting keys"); + return function () { + observedOrder.push("calling keys"); + return observableIterator(); + }; + } +} + +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "d"]); + const s2 = new MySetLike(); + const combined = s1.symmetricDifference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.compareArray([...combined], ["d", "b", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d"]); + const s2 = new MySetLike(); + const combined = s1.symmetricDifference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.compareArray([...combined], ["d", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d", "e"]); + const s2 = new MySetLike(); + const combined = s1.symmetricDifference(s2); + + const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", + ]; + + assert.compareArray([...combined], ["d", "e", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/symmetricDifference/size-is-a-number.js b/test/built-ins/Set/prototype/symmetricDifference/size-is-a-number.js new file mode 100644 index 0000000000..037bf8bfbf --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/size-is-a-number.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-getsetrecord +description: GetSetRecord throws an exception if the Set-like object has a size that is coerced to NaN +info: | + 2. Let rawSize be ? Get(obj, "size"). + 3. Let numSize be ? ToNumber(rawSize). + 4. NOTE: If rawSize is undefined, then numSize will be NaN. + 5. If numSize is NaN, throw a TypeError exception. +features: [set-methods] +---*/ + +const s1 = new Set([1, 2]); +const s2 = { + size: undefined, + has: () => {}, + keys: function* keys() { + yield 2; + yield 3; + }, +}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when size is undefined" +); + +s2.size = NaN; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when size is NaN" +); + +let coercionCalls = 0; +s2.size = { + valueOf: function() { + ++coercionCalls; + return NaN; + }, +}; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when size coerces to NaN" +); +assert.sameValue(coercionCalls, 1, "GetSetRecord coerces size"); + +s2.size = 0n; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when size is a BigInt" +); + +s2.size = "string"; +assert.throws( + TypeError, + function () { + s1.symmetricDifference(s2); + }, + "GetSetRecord throws an error when size is a non-numeric string" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/subclass-receiver-methods.js b/test/built-ins/Set/prototype/symmetricDifference/subclass-receiver-methods.js new file mode 100644 index 0000000000..0715e9f145 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/subclass-receiver-methods.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference works on subclasses of Set, but never calls the receiver's size/has/keys methods +features: [set-methods] +includes: [compareArray.js] +---*/ + +let sizeCount = 0; +let hasCount = 0; +let keysCount = 0; + +class MySet extends Set { + size(...rest) { + sizeCount++; + return super.size(...rest); + } + + has(...rest) { + hasCount++; + return super.has(...rest); + } + + keys(...rest) { + keysCount++; + return super.keys(...rest); + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); +assert.sameValue(sizeCount, 0, "size should not be called on the receiver"); +assert.sameValue(hasCount, 0, "has should not be called on the receiver"); +assert.sameValue(keysCount, 0, "keys should not be called on the receiver"); diff --git a/test/built-ins/Set/prototype/symmetricDifference/subclass-symbol-species.js b/test/built-ins/Set/prototype/symmetricDifference/subclass-symbol-species.js new file mode 100644 index 0000000000..20cc9c93f9 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/subclass-symbol-species.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference works on subclasses of Set, but returns an instance of Set even when Symbol.species is overridden. +features: [set-methods] +includes: [compareArray.js] +---*/ +var count = 0; +class MySet extends Set { + static get [Symbol.species]() { + count++; + return Set; + } +} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(count, 0, "Symbol.species is never called"); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/subclass.js b/test/built-ins/Set/prototype/symmetricDifference/subclass.js new file mode 100644 index 0000000000..fe35ba7d59 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/subclass.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference works on subclasses of Set, but returns an instance of Set +features: [set-methods] +includes: [compareArray.js] +---*/ + +class MySet extends Set {} + +const s1 = new MySet([1, 2]); +const s2 = new Set([2, 3]); +const expected = [1, 3]; +const combined = s1.symmetricDifference(s2); + +assert.compareArray([...combined], expected); +assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue( + combined instanceof MySet, + false, + "The returned object is a Set, not a subclass" +); diff --git a/test/built-ins/Set/prototype/symmetricDifference/symmetricDifference.js b/test/built-ins/Set/prototype/symmetricDifference/symmetricDifference.js new file mode 100644 index 0000000000..f189d2add5 --- /dev/null +++ b/test/built-ins/Set/prototype/symmetricDifference/symmetricDifference.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.symmetricdifference +description: Set.prototype.symmetricDifference properties +includes: [propertyHelper.js] +features: [set-methods] +---*/ + +assert.sameValue( + typeof Set.prototype.symmetricDifference, + "function", + "`typeof Set.prototype.symmetricDifference` is `'function'`" +); + +verifyProperty(Set.prototype, "symmetricDifference", { + enumerable: false, + writable: true, + configurable: true, +}); diff --git a/test/built-ins/Set/prototype/union/allows-set-like-object.js b/test/built-ins/Set/prototype/union/allows-set-like-object.js index 596d18352b..bea950cce1 100644 --- a/test/built-ins/Set/prototype/union/allows-set-like-object.js +++ b/test/built-ins/Set/prototype/union/allows-set-like-object.js @@ -17,7 +17,9 @@ includes: [compareArray.js] const s1 = new Set([1, 2]); const s2 = { size: 2, - has: () => {}, + has: () => { + throw new Test262Error("Set.prototype.union should not invoke .has on its argument"); + }, keys: function* keys() { yield 2; yield 3; diff --git a/test/built-ins/Set/prototype/union/combines-itself.js b/test/built-ins/Set/prototype/union/combines-itself.js index e136c3c44f..238e64500f 100644 --- a/test/built-ins/Set/prototype/union/combines-itself.js +++ b/test/built-ins/Set/prototype/union/combines-itself.js @@ -13,3 +13,4 @@ const combined = s1.union(s1); assert.compareArray([...combined], expected); assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/union/combines-same-sets.js b/test/built-ins/Set/prototype/union/combines-same-sets.js index e2690d8f0e..9189e7483d 100644 --- a/test/built-ins/Set/prototype/union/combines-same-sets.js +++ b/test/built-ins/Set/prototype/union/combines-same-sets.js @@ -14,3 +14,5 @@ const combined = s1.union(s2); assert.compareArray([...combined], expected); assert.sameValue(combined instanceof Set, true, "The returned object is a Set"); +assert.sameValue(combined === s1, false, "The returned object is a new object"); +assert.sameValue(combined === s2, false, "The returned object is a new object"); diff --git a/test/built-ins/Set/prototype/union/length.js b/test/built-ins/Set/prototype/union/length.js index 81ad5f4d9f..8b5a83e7cf 100644 --- a/test/built-ins/Set/prototype/union/length.js +++ b/test/built-ins/Set/prototype/union/length.js @@ -4,7 +4,7 @@ esid: sec-set.prototype.union description: Set.prototype.union length property info: | - Set.prototype.union ( other )] + Set.prototype.union ( other ) includes: [propertyHelper.js] features: [set-methods] ---*/ diff --git a/test/built-ins/Set/prototype/union/name.js b/test/built-ins/Set/prototype/union/name.js index 27b9edf38f..85a54ee362 100644 --- a/test/built-ins/Set/prototype/union/name.js +++ b/test/built-ins/Set/prototype/union/name.js @@ -4,7 +4,7 @@ esid: sec-set.prototype.union description: Set.prototype.union name property info: | - Set.prototype.union ( other )] + Set.prototype.union ( other ) includes: [propertyHelper.js] features: [set-methods] ---*/ diff --git a/test/built-ins/Set/prototype/union/require-internal-slot.js b/test/built-ins/Set/prototype/union/require-internal-slot.js index 1b035b2355..b4f16b9bfe 100644 --- a/test/built-ins/Set/prototype/union/require-internal-slot.js +++ b/test/built-ins/Set/prototype/union/require-internal-slot.js @@ -12,14 +12,16 @@ const union = Set.prototype.union; assert.sameValue(typeof union, "function"); -assert.throws(TypeError, () => union.call(undefined), "undefined"); -assert.throws(TypeError, () => union.call(null), "null"); -assert.throws(TypeError, () => union.call(true), "true"); -assert.throws(TypeError, () => union.call(""), "empty string"); -assert.throws(TypeError, () => union.call(Symbol()), "symbol"); -assert.throws(TypeError, () => union.call(1), "1"); -assert.throws(TypeError, () => union.call(1n), "1n"); -assert.throws(TypeError, () => union.call({}), "plain object"); -assert.throws(TypeError, () => union.call([]), "array"); -assert.throws(TypeError, () => union.call(new Map()), "map"); -assert.throws(TypeError, () => union.call(Set.prototype), "Set.prototype"); +const realSet = new Set([]); + +assert.throws(TypeError, () => union.call(undefined, realSet), "undefined"); +assert.throws(TypeError, () => union.call(null, realSet), "null"); +assert.throws(TypeError, () => union.call(true, realSet), "true"); +assert.throws(TypeError, () => union.call("", realSet), "empty string"); +assert.throws(TypeError, () => union.call(Symbol(), realSet), "symbol"); +assert.throws(TypeError, () => union.call(1, realSet), "1"); +assert.throws(TypeError, () => union.call(1n, realSet), "1n"); +assert.throws(TypeError, () => union.call({}, realSet), "plain object"); +assert.throws(TypeError, () => union.call([], realSet), "array"); +assert.throws(TypeError, () => union.call(new Map(), realSet), "map"); +assert.throws(TypeError, () => union.call(Set.prototype, realSet), "Set.prototype"); diff --git a/test/built-ins/Set/prototype/union/result-order.js b/test/built-ins/Set/prototype/union/result-order.js new file mode 100644 index 0000000000..30c1f2ce6a --- /dev/null +++ b/test/built-ins/Set/prototype/union/result-order.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-set.prototype.union +description: Set.prototype.union result ordering +features: [set-methods] +includes: [compareArray.js] +---*/ + +{ + const s1 = new Set([1, 2]); + const s2 = new Set([2, 3]); + + assert.compareArray([...s1.union(s2)], [1, 2, 3]); +} + +{ + const s1 = new Set([2, 3]); + const s2 = new Set([1, 2]); + + assert.compareArray([...s1.union(s2)], [2, 3, 1]); +} + +{ + const s1 = new Set([1, 2]); + const s2 = new Set([3]); + + assert.compareArray([...s1.union(s2)], [1, 2, 3]); +} + +{ + const s1 = new Set([3]); + const s2 = new Set([1, 2]); + + assert.compareArray([...s1.union(s2)], [3, 1, 2]); +} diff --git a/test/built-ins/Set/prototype/union/set-like-class-order.js b/test/built-ins/Set/prototype/union/set-like-class-order.js index 3c204046ec..1b3a0539d3 100644 --- a/test/built-ins/Set/prototype/union/set-like-class-order.js +++ b/test/built-ins/Set/prototype/union/set-like-class-order.js @@ -7,29 +7,10 @@ features: [set-methods] includes: [compareArray.js] ---*/ -const observedOrder = []; -const expectedOrder = [ - "getting size", - "ToNumber(size)", - "getting has", - "getting keys", - "calling keys", - "getting next", - // first iteration, has value - "calling next", - "getting done", - "getting value", - // second iteration, has value - "calling next", - "getting done", - "getting value", - // third iteration, no value; ends - "calling next", - "getting done", -]; +let observedOrder = []; function observableIterator() { - let values = ["a", "b"]; + let values = ["a", "b", "c"]; let index = 0; return { get next() { @@ -76,8 +57,62 @@ class MySetLike { } } -const s1 = new Set([1, 2]); -const s2 = new MySetLike(); -const combined = s1.union(s2); +const expectedOrder = [ + "getting size", + "ToNumber(size)", + "getting has", + "getting keys", + "calling keys", + "getting next", + // first iteration, has value + "calling next", + "getting done", + "getting value", + // second iteration, has value + "calling next", + "getting done", + "getting value", + // third iteration, has value + "calling next", + "getting done", + "getting value", + // fourth iteration, no value; ends + "calling next", + "getting done", +]; -assert.compareArray(observedOrder, expectedOrder); +// this is smaller than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "d"]); + const s2 = new MySetLike(); + const combined = s1.union(s2); + + assert.compareArray([...combined], ["a", "d", "b", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is same size as argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d"]); + const s2 = new MySetLike(); + const combined = s1.union(s2); + + assert.compareArray([...combined], ["a", "b", "d", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} + +// this is larger than argument +{ + observedOrder = []; + + const s1 = new Set(["a", "b", "d", "e"]); + const s2 = new MySetLike(); + const combined = s1.union(s2); + + assert.compareArray([...combined], ["a", "b", "d", "e", "c"]); + assert.compareArray(observedOrder, expectedOrder); +} diff --git a/test/built-ins/Set/prototype/union/union-properties.js b/test/built-ins/Set/prototype/union/union.js similarity index 100% rename from test/built-ins/Set/prototype/union/union-properties.js rename to test/built-ins/Set/prototype/union/union.js