| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /*
- * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
- * - test/built-ins/Set/prototype/union/combines-sets.js
- * - test/built-ins/Set/prototype/union/combines-empty-sets.js
- * - test/built-ins/Set/prototype/union/combines-itself.js
- * - test/built-ins/Set/prototype/union/combines-same-sets.js
- * - test/built-ins/Set/prototype/union/combines-Map.js
- * - test/built-ins/Set/prototype/union/result-order.js
- * - test/built-ins/Set/prototype/union/appends-new-values.js
- * - test/built-ins/Set/prototype/union/allows-set-like-object.js
- * - test/built-ins/Set/prototype/union/size-is-a-number.js
- * - test/built-ins/Set/prototype/union/has-is-callable.js
- * - test/built-ins/Set/prototype/union/keys-is-callable.js
- * - test/built-ins/Set/prototype/union/array-throws.js
- * - test/built-ins/Set/prototype/union/converts-negative-zero.js
- * - test/built-ins/Set/prototype/union/set-like-class-mutation.js
- * - test/built-ins/Set/prototype/intersection/combines-sets.js
- * - test/built-ins/Set/prototype/intersection/combines-itself.js
- * - test/built-ins/Set/prototype/intersection/result-order.js
- * - test/built-ins/Set/prototype/intersection/size-is-a-number.js
- * - test/built-ins/Set/prototype/intersection/converts-negative-zero.js
- * - test/built-ins/Set/prototype/intersection/combines-Map.js
- * - test/built-ins/Set/prototype/intersection/allows-set-like-object.js
- * - test/built-ins/Set/prototype/intersection/set-like-class-mutation.js
- * - test/built-ins/Set/prototype/difference/combines-sets.js
- * - test/built-ins/Set/prototype/difference/combines-itself.js
- * - test/built-ins/Set/prototype/difference/result-order.js
- * - test/built-ins/Set/prototype/difference/size-is-a-number.js
- * - test/built-ins/Set/prototype/difference/converts-negative-zero.js
- * - test/built-ins/Set/prototype/difference/combines-Map.js
- * - test/built-ins/Set/prototype/difference/allows-set-like-object.js
- * - test/built-ins/Set/prototype/difference/set-like-class-mutation.js
- * - test/built-ins/Set/prototype/symmetricDifference/combines-sets.js
- * - test/built-ins/Set/prototype/symmetricDifference/combines-itself.js
- * - test/built-ins/Set/prototype/symmetricDifference/result-order.js
- * - test/built-ins/Set/prototype/symmetricDifference/size-is-a-number.js
- * - test/built-ins/Set/prototype/symmetricDifference/converts-negative-zero.js
- * - test/built-ins/Set/prototype/symmetricDifference/set-like-class-mutation.js
- * - test/built-ins/Set/prototype/symmetricDifference/combines-Map.js
- * - test/built-ins/Set/prototype/symmetricDifference/allows-set-like-object.js
- * - test/built-ins/Set/prototype/isSubsetOf/compares-sets.js
- * - test/built-ins/Set/prototype/isSubsetOf/size-is-a-number.js
- * - test/built-ins/Set/prototype/isSubsetOf/compares-Map.js
- * - test/built-ins/Set/prototype/isSubsetOf/allows-set-like-object.js
- * - test/built-ins/Set/prototype/isSubsetOf/compares-itself.js
- * - test/built-ins/Set/prototype/isSubsetOf/compares-same-sets.js
- * - test/built-ins/Set/prototype/isSubsetOf/set-like-class-mutation.js
- * - test/built-ins/Set/prototype/isSupersetOf/compares-sets.js
- * - test/built-ins/Set/prototype/isSupersetOf/size-is-a-number.js
- * - test/built-ins/Set/prototype/isSupersetOf/compares-Map.js
- * - test/built-ins/Set/prototype/isSupersetOf/allows-set-like-object.js
- * - test/built-ins/Set/prototype/isSupersetOf/compares-itself.js
- * - test/built-ins/Set/prototype/isSupersetOf/compares-same-sets.js
- * - test/built-ins/Set/prototype/isSupersetOf/converts-negative-zero.js
- * - test/built-ins/Set/prototype/isDisjointFrom/compares-sets.js
- * - test/built-ins/Set/prototype/isDisjointFrom/size-is-a-number.js
- * - test/built-ins/Set/prototype/isDisjointFrom/compares-Map.js
- * - test/built-ins/Set/prototype/isDisjointFrom/allows-set-like-object.js
- * - test/built-ins/Set/prototype/isDisjointFrom/compares-itself.js
- * - test/built-ins/Set/prototype/isDisjointFrom/compares-same-sets.js
- * - test/built-ins/Set/prototype/isDisjointFrom/converts-negative-zero.js
- * - test/built-ins/Set/prototype/isDisjointFrom/set-like-class-mutation.js
- *
- * Copyright (C) 2023 Anthony Frehner. All rights reserved.
- * Copyright (C) 2023 Anthony Frehner and Kevin Gibbons. All rights reserved.
- * Copyright (C) 2023 Kevin Gibbons. All rights reserved.
- * Copyright (C) 2023 Kevin Gibbons, Anthony Frehner. All rights reserved.
- * Test262 portions are governed by the BSD license in LICENSE.test262.
- * Set-like `keys` methods return arrays instead of iterator objects because
- * CodeMode materializes supported iterators and does not support generators.
- */
- import { describe, expect, test } from "bun:test"
- import { Effect } from "effect"
- import { CodeMode } from "../src/index.js"
- const value = async (code: string) => {
- const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
- if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
- return result.value
- }
- describe("Set composition Test262 parity", () => {
- test("combines Sets without mutating either operand", async () => {
- expect(
- await value(`
- const left = new Set([1, 2])
- const right = new Set([2, 3])
- const union = left.union(right)
- const intersection = left.intersection(right)
- const difference = left.difference(right)
- const symmetric = left.symmetricDifference(right)
- return [
- [...union], union instanceof Set,
- [...intersection], intersection instanceof Set,
- [...difference], difference instanceof Set,
- [...symmetric], symmetric instanceof Set,
- [...left], [...right],
- [...left.union(left)], left.union(left) !== left,
- [...left.intersection(left)], left.intersection(left) !== left,
- [...left.difference(left)], left.difference(left) !== left,
- [...left.symmetricDifference(left)], left.symmetricDifference(left) !== left,
- [...left.union(new Set())], [...new Set().union(left)],
- ]
- `),
- ).toEqual([
- [1, 2, 3],
- true,
- [2],
- true,
- [1],
- true,
- [1, 3],
- true,
- [1, 2],
- [2, 3],
- [1, 2],
- true,
- [1, 2],
- true,
- [],
- true,
- [],
- true,
- [1, 2],
- [1, 2],
- ])
- })
- test("preserves the specified result order", async () => {
- expect(
- await value(`
- return [
- [...new Set([1, 3, 5]).intersection(new Set([3, 2, 1]))],
- [...new Set([3, 2, 1, 0]).intersection(new Set([1, 3, 5]))],
- [...new Set([1, 2, 3, 4]).difference(new Set([6, 5, 3, 2]))],
- [...new Set([6, 5, 3, 2]).difference(new Set([1, 2, 3, 4]))],
- [...new Set([1, 2, 3, 4]).symmetricDifference(new Set([6, 5, 4, 3]))],
- [...new Set([2, 3]).union(new Set([1, 2]))],
- [...new Set([1, 2, -3]).union(new Set([-1, 0]))],
- ]
- `),
- ).toEqual([
- [1, 3],
- [1, 3],
- [1, 4],
- [6, 5],
- [1, 2, 6, 5],
- [2, 3, 1],
- [1, 2, -3, -1, 0],
- ])
- })
- test("accepts Maps as Set-like operands", async () => {
- expect(
- await value(`
- const set = new Set([1, 2])
- const map = new Map([[2, "two"], [3, "three"]])
- return [
- [...set.union(map)],
- [...set.intersection(map)],
- [...set.difference(map)],
- [...set.symmetricDifference(map)],
- ]
- `),
- ).toEqual([[1, 2, 3], [2], [1], [1, 3]])
- })
- test("accepts supported Set-like objects and uses the size-selected method", async () => {
- expect(
- await value(`
- const keysOnly = {
- size: 1,
- has: () => { throw new Error("has should not be called") },
- keys: () => [2, 3],
- }
- const hasOnly = {
- size: 2,
- has: (value) => value === 2,
- keys: () => { throw new Error("keys should not be called") },
- }
- const set = new Set([1, 2])
- return [
- [...set.union(keysOnly)],
- [...set.intersection(hasOnly)],
- [...set.difference(hasOnly)],
- [...set.symmetricDifference(keysOnly)],
- ]
- `),
- ).toEqual([[1, 2, 3], [2], [1], [1, 3]])
- })
- test("normalizes negative zero", async () => {
- expect(
- await value(`
- const setlike = {
- size: 1,
- has: () => { throw new Error("has should not be called") },
- keys: () => [-0],
- }
- return [
- 1 / new Set([1]).union(setlike).values()[1] === Infinity,
- 1 / new Set([0, 1, 2]).intersection(setlike).values()[0] === Infinity,
- [...new Set([0, 1]).difference(setlike)],
- 1 / new Set([1, 2]).symmetricDifference(setlike).values()[2] === Infinity,
- ]
- `),
- ).toEqual([true, true, [1], true])
- })
- test("handles duplicate Set-like keys using receiver membership", async () => {
- expect(
- await value(`
- const other = {
- size: 4,
- has: () => { throw new Error("has should not be called") },
- keys: () => [2, 2, 3, 3],
- }
- return [...new Set([1, 2]).symmetricDifference(other)]
- `),
- ).toEqual([1, 3])
- })
- test("copies receiver data before Set-like key iteration mutates it", async () => {
- expect(
- await value(`
- const unionBase = new Set(["a", "b", "c", "d", "e"])
- const unionOther = {
- size: 2,
- has: () => { throw new Error("has should not be called") },
- keys: () => {
- unionBase.delete("b")
- unionBase.delete("c")
- unionBase.add("b")
- return ["x", "y"]
- },
- }
- const union = unionBase.union(unionOther)
- const intersectionBase = new Set(["a", "b", "c", "d", "e"])
- const intersectionOther = {
- size: 3,
- has: () => { throw new Error("has should not be called") },
- keys: () => {
- intersectionBase.delete("b")
- intersectionBase.delete("c")
- intersectionBase.add("b")
- return ["x", "b", "b"]
- },
- }
- const intersection = intersectionBase.intersection(intersectionOther)
- const differenceBase = new Set(["a", "b", "c", "d", "e"])
- const differenceOther = {
- size: 3,
- has: () => { throw new Error("has should not be called") },
- keys: () => {
- differenceBase.delete("b")
- differenceBase.delete("c")
- differenceBase.add("b")
- return ["x", "b", "b"]
- },
- }
- const difference = differenceBase.difference(differenceOther)
- const symmetricBase = new Set(["a", "b", "c", "d", "e"])
- const symmetricOther = {
- size: 4,
- has: () => { throw new Error("has should not be called") },
- keys: () => {
- symmetricBase.delete("b")
- symmetricBase.delete("c")
- symmetricBase.add("b")
- return ["x", "b", "c", "c"]
- },
- }
- const symmetric = symmetricBase.symmetricDifference(symmetricOther)
- return [
- [...union], [...unionBase],
- [...intersection], [...intersectionBase],
- [...difference], [...differenceBase],
- [...symmetric], [...symmetricBase],
- ]
- `),
- ).toEqual([
- ["a", "b", "c", "d", "e", "x", "y"],
- ["a", "d", "e", "b"],
- ["b"],
- ["a", "d", "e", "b"],
- ["a", "c", "d", "e"],
- ["a", "d", "e", "b"],
- ["a", "c", "d", "e", "x"],
- ["a", "d", "e", "b"],
- ])
- })
- })
- describe("Set relation Test262 parity", () => {
- test("compares Sets", async () => {
- expect(
- await value(`
- const set = new Set([1, 2])
- return [
- set.isSubsetOf(new Set([2, 3])), set.isSubsetOf(new Set([1, 2, 3])),
- set.isSupersetOf(new Set([2, 3])), set.isSupersetOf(new Set([1])),
- set.isDisjointFrom(new Set([2, 3])), set.isDisjointFrom(new Set([3])),
- new Set().isSubsetOf(set), set.isSupersetOf(new Set()), new Set().isDisjointFrom(new Set()),
- set.isSubsetOf(set), set.isSupersetOf(set), set.isDisjointFrom(set),
- set.isSubsetOf(new Set([1, 2])), set.isSupersetOf(new Set([1, 2])),
- ]
- `),
- ).toEqual([false, true, false, true, false, true, true, true, true, true, true, false, true, true])
- })
- test("compares Maps and supported Set-like objects", async () => {
- expect(
- await value(`
- const set = new Set([1, 2])
- const map = new Map([[1, "one"], [2, "two"], [3, "three"]])
- const hasOnly = {
- size: 2,
- has: (value) => value === 1 || value === 2,
- keys: () => { throw new Error("keys should not be called") },
- }
- const keysOnly = {
- size: 1,
- has: () => { throw new Error("has should not be called") },
- keys: () => [1],
- }
- return [
- set.isSubsetOf(map), set.isSupersetOf(map), set.isDisjointFrom(map),
- set.isSubsetOf(hasOnly), set.isSupersetOf(keysOnly), set.isDisjointFrom(hasOnly),
- ]
- `),
- ).toEqual([true, false, false, true, true, false])
- })
- test("normalizes negative zero from Set-like keys", async () => {
- expect(
- await value(`
- const setlike = {
- size: 1,
- has: () => { throw new Error("has should not be called") },
- keys: () => [-0],
- }
- return [
- new Set([0, 1]).isSupersetOf(setlike),
- new Set([0, 1]).isDisjointFrom(setlike),
- ]
- `),
- ).toEqual([true, false])
- })
- test("observes live receiver mutation while calling Set-like has", async () => {
- expect(
- await value(`
- const subset = new Set(["a", "b", "c"])
- const subsetOther = {
- size: 3,
- has: (item) => {
- if (item === "a") subset.delete("c")
- return ["x", "a", "b"].includes(item)
- },
- keys: () => { throw new Error("keys should not be called") },
- }
- const disjoint = new Set(["a", "b", "c"])
- const disjointOther = {
- size: 3,
- has: (item) => {
- if (item === "a") {
- disjoint.delete("b")
- disjoint.delete("c")
- disjoint.add("b")
- }
- if (item === "c") throw new Error("deleted value should not be visited")
- return false
- },
- keys: () => { throw new Error("keys should not be called") },
- }
- return [
- subset.isSubsetOf(subsetOther), [...subset],
- disjoint.isDisjointFrom(disjointOther), [...disjoint],
- ]
- `),
- ).toEqual([true, ["a", "b"], true, ["a", "b"]])
- })
- })
- describe("Set-like validation Test262 parity", () => {
- test("rejects arrays, invalid sizes, and non-callable methods with TypeError", async () => {
- expect(
- await value(`
- const set = new Set([1])
- const names = []
- let coercionCalls = 0
- const invalid = [
- [],
- { size: undefined, has: () => false, keys: () => [] },
- { size: NaN, has: () => false, keys: () => [] },
- { size: { valueOf: () => { coercionCalls += 1; return NaN } }, has: () => false, keys: () => [] },
- { size: "string", has: () => false, keys: () => [] },
- { size: 0, has: undefined, keys: () => [] },
- { size: 0, has: {}, keys: () => [] },
- { size: 0, has: () => false, keys: undefined },
- { size: 0, has: () => false, keys: {} },
- ]
- for (const other of invalid) {
- try { set.union(other) } catch (error) { names.push(error.name) }
- }
- return [names, coercionCalls]
- `),
- ).toEqual([
- [
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- "TypeError",
- ],
- 1,
- ])
- })
- test("validates Set-like records for every method", async () => {
- expect(
- await value(`
- const set = new Set([1])
- const invalid = { size: NaN, has: () => false, keys: () => [] }
- const names = []
- const operations = [
- () => set.union(invalid),
- () => set.intersection(invalid),
- () => set.difference(invalid),
- () => set.symmetricDifference(invalid),
- () => set.isSubsetOf(invalid),
- () => set.isSupersetOf(invalid),
- () => set.isDisjointFrom(invalid),
- ]
- for (const operation of operations) {
- try { operation() } catch (error) { names.push(error.name) }
- }
- return names
- `),
- ).toEqual(["TypeError", "TypeError", "TypeError", "TypeError", "TypeError", "TypeError", "TypeError"])
- })
- test("requires Set-like keys to return CodeMode's materialized iterator representation", async () => {
- expect(
- await value(`
- const set = new Set([1])
- const names = []
- for (const keys of [() => "ab", () => new Set([2]), () => new Map([[2, true]])]) {
- try { set.union({ size: 1, has: () => false, keys }) } catch (error) { names.push(error.name) }
- }
- return names
- `),
- ).toEqual(["TypeError", "TypeError", "TypeError"])
- })
- })
|