From 367db9d2e65836693520b4a51700e3c73745197f Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 9 Jul 2026 16:14:23 -0500 Subject: [PATCH] test(codemode): add promise conformance corpus --- .../codemode/test/promise-test262.test.ts | 696 ++++++++++++++++++ packages/codemode/test/test262-array.md | 77 -- packages/codemode/test/test262-string.md | 69 -- 3 files changed, 696 insertions(+), 146 deletions(-) create mode 100644 packages/codemode/test/promise-test262.test.ts delete mode 100644 packages/codemode/test/test262-array.md delete mode 100644 packages/codemode/test/test262-string.md diff --git a/packages/codemode/test/promise-test262.test.ts b/packages/codemode/test/promise-test262.test.ts new file mode 100644 index 0000000000..78335b43ce --- /dev/null +++ b/packages/codemode/test/promise-test262.test.ts @@ -0,0 +1,696 @@ +/* + * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75. + * Every test names its upstream source; test.failing cases are executable conformance + * targets for intended Promise behavior that CodeMode does not implement yet. + * + * Copyright 2014 Cubane Canada, Inc. All rights reserved. + * Copyright 2015 Microsoft Corporation. All rights reserved. + * Copyright 2016 Microsoft, Inc. All rights reserved. + * Copyright 2017 Caitlin Potter. All rights reserved. + * Copyright (C) 2016-2020 the V8 project authors. All rights reserved. + * Copyright (C) 2018-2020 Rick Waldron. All rights reserved. + * Copyright (C) 2019 Leo Balter. All rights reserved. + * Test262 portions are governed by the BSD license in LICENSE.test262. + */ +import { describe, expect, test } from "bun:test" +import { Effect } from "effect" +import { CodeMode } from "../src/index.js" + +const execute = (code: string) => + Effect.runPromise(CodeMode.execute({ code, tools: {}, limits: { timeoutMs: 1_000 } })) + +const value = async (code: string) => { + const result = await execute(code) + if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) + return result.value +} + +describe("Test262 Promise statics", () => { + test("statics are callable and return promises", async () => { + // Sources: + // test/built-ins/Promise/all/S25.4.4.1_A1.1_T1.js + // test/built-ins/Promise/allSettled/is-function.js + // test/built-ins/Promise/allSettled/returns-promise.js + // test/built-ins/Promise/race/S25.4.4.3_A1.1_T1.js + // test/built-ins/Promise/resolve/S25.4.4.5_A1.1_T1.js + // test/built-ins/Promise/reject/S25.4.4.4_A1.1_T1.js + expect( + await value(` + const values = [ + Promise.all([]), + Promise.allSettled([]), + Promise.race([undefined]), + Promise.resolve(), + Promise.reject(), + ] + const callable = [ + typeof Promise.all, + typeof Promise.allSettled, + typeof Promise.race, + typeof Promise.resolve, + typeof Promise.reject, + ] + try { await values[4] } catch {} + return [callable, values.map((item) => item instanceof Promise)] + `), + ).toEqual([ + ["function", "function", "function", "function", "function"], + [true, true, true, true, true], + ]) + }) + + test("Promise.all returns fresh arrays for empty and settled inputs", async () => { + // Sources: + // test/built-ins/Promise/all/S25.4.4.1_A2.1_T1.js + // test/built-ins/Promise/all/S25.4.4.1_A2.3_T1.js + // test/built-ins/Promise/all/S25.4.4.1_A2.3_T2.js + // test/built-ins/Promise/all/S25.4.4.1_A2.3_T3.js + // test/built-ins/Promise/all/S25.4.4.1_A7.1_T1.js + expect( + await value(` + const input = [] + const emptyPromise = Promise.all(input) + const empty = await emptyPromise + const onePromise = Promise.all([Promise.resolve(3)]) + const one = await onePromise + return [ + emptyPromise instanceof Promise, + empty instanceof Array, + empty.length, + empty !== input, + onePromise instanceof Promise, + one instanceof Array, + one.length, + one[0], + ] + `), + ).toEqual([true, true, 0, true, true, true, 1, 3]) + }) + + test("Promise.all adopts values and preserves input order and identity", async () => { + // Sources: + // test/built-ins/Promise/all/resolve-non-thenable.js + // test/built-ins/Promise/all/S25.4.4.1_A8.2_T1.js + // test/built-ins/Promise/all/S25.4.4.1_A8.2_T2.js + const result = await value(` + const first = { id: 1 } + const second = { id: 2 } + const values = await Promise.all([Promise.resolve(3), first, Promise.resolve(second)]) + const observe = async (promise) => { + try { await promise; return "fulfilled" } catch (reason) { return reason } + } + return [ + values.length, + values[0], + values[1] === first, + values[2] === second, + await observe(Promise.all([Promise.reject(1), Promise.resolve(2)])), + await observe(Promise.all([Promise.resolve(1), Promise.reject(2)])), + ] + `) + expect(result).toEqual([3, 3, true, true, 1, 2]) + }) + + test("Promise.allSettled returns fresh arrays and ordered outcome records", async () => { + // Sources: + // test/built-ins/Promise/allSettled/resolves-empty-array.js + // test/built-ins/Promise/allSettled/resolves-to-array.js + // test/built-ins/Promise/allSettled/resolved-all-fulfilled.js + // test/built-ins/Promise/allSettled/resolved-all-rejected.js + // test/built-ins/Promise/allSettled/resolved-all-mixed.js + // test/built-ins/Promise/allSettled/resolve-non-thenable.js + expect( + await value(` + const input = [] + const empty = await Promise.allSettled(input) + const reason = { id: 4 } + const object = { id: 5 } + const outcomes = await Promise.allSettled([ + Promise.resolve(1), + Promise.reject(2), + 3, + Promise.reject(reason), + object, + ]) + return [ + empty instanceof Array, + empty.length, + empty !== input, + outcomes, + outcomes[4].value === object, + outcomes.map((item) => Object.keys(item)), + ] + `), + ).toEqual([ + true, + 0, + true, + [ + { status: "fulfilled", value: 1 }, + { status: "rejected", reason: 2 }, + { status: "fulfilled", value: 3 }, + { status: "rejected", reason: { id: 4 } }, + { status: "fulfilled", value: { id: 5 } }, + ], + true, + [ + ["status", "value"], + ["status", "reason"], + ["status", "value"], + ["status", "reason"], + ["status", "value"], + ], + ]) + }) + + test("Promise.race preserves fulfillment, rejection, and iterable order", async () => { + // Sources: + // test/built-ins/Promise/race/S25.4.4.3_A6.2_T1.js + // test/built-ins/Promise/race/S25.4.4.3_A7.1_T1.js + // test/built-ins/Promise/race/S25.4.4.3_A7.2_T1.js + // test/built-ins/Promise/race/S25.4.4.3_A7.3_T1.js + // test/built-ins/Promise/race/S25.4.4.3_A7.3_T2.js + expect( + await value(` + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + return await Promise.all([ + observe(Promise.race([23])), + observe(Promise.race([Promise.reject(7)])), + observe(Promise.race([Promise.resolve(1), Promise.resolve(2)])), + observe(Promise.race([Promise.reject(3), Promise.resolve(4)])), + ]) + `), + ).toEqual([ + ["fulfilled", 23], + ["rejected", 7], + ["fulfilled", 1], + ["rejected", 3], + ]) + }) + + test("combinators consume supported string iterables", async () => { + // Sources: + // test/built-ins/Promise/all/iter-arg-is-string-resolve.js + // test/built-ins/Promise/allSettled/iter-arg-is-string-resolve.js + // test/built-ins/Promise/race/iter-arg-is-string-resolve.js + expect( + await value(` + return [ + await Promise.all("abc"), + await Promise.allSettled("ab"), + await Promise.race("abc"), + ] + `), + ).toEqual([ + ["a", "b", "c"], + [ + { status: "fulfilled", value: "a" }, + { status: "fulfilled", value: "b" }, + ], + "a", + ]) + }) + + test("Promise.resolve adopts values and preserves sandbox-promise identity", async () => { + // Sources: + // test/built-ins/Promise/resolve/S25.4.4.5_A2.1_T1.js + // test/built-ins/Promise/resolve/resolve-non-obj.js + // test/built-ins/Promise/resolve/resolve-non-thenable.js + expect( + await value(` + const object = { id: 1 } + const promise = Promise.resolve(1) + return [ + await Promise.resolve(23), + await Promise.resolve(Promise.resolve(24)), + (await Promise.resolve(object)) === object, + [promise].includes(Promise.resolve(promise)), + ] + `), + ).toEqual([23, 24, true, true]) + }) + + test("Promise.reject preserves primitive and object reasons", async () => { + // Sources: + // test/built-ins/Promise/reject/S25.4.4.4_A2.1_T1.js + const result = await value(` + const object = { reason: true } + const reasons = [undefined, null, false, true, 0, "", 42, object] + const observe = async (reason) => { + try { await Promise.reject(reason); return false } catch (caught) { return caught === reason } + } + return await Promise.all(reasons.map(observe)) + `) + expect(result).toEqual([true, true, true, true, true, true, true, true]) + }) +}) + +describe("Test262 async functions and await", () => { + test("declaration, expression, and arrow forms return promises", async () => { + // Sources: + // test/language/statements/async-function/declaration-returns-promise.js + // test/language/expressions/async-function/expression-returns-promise.js + // test/language/expressions/async-arrow-function/arrow-returns-promise.js + expect( + await value(` + async function declaration() { return 1 } + const expression = async function() { return 2 } + const arrow = async () => 3 + const promises = [declaration(), expression(), arrow()] + return [promises.map((item) => item instanceof Promise), await Promise.all(promises)] + `), + ).toEqual([[true, true, true], [1, 2, 3]]) + }) + + test("async bodies adopt returns and reject throws before and after await", async () => { + // Sources: + // test/language/statements/async-function/evaluation-body.js + // test/language/statements/async-function/evaluation-body-that-returns.js + // test/language/statements/async-function/evaluation-body-that-returns-after-await.js + // test/language/statements/async-function/evaluation-body-that-throws.js + // test/language/statements/async-function/evaluation-body-that-throws-after-await.js + expect( + await value(` + const order = [] + const plain = async () => { order.push("body"); return 42 } + const afterAwait = async () => { await Promise.resolve(); return 43 } + const throwsBefore = async () => { throw 1 } + const throwsAfter = async () => { await Promise.resolve(); throw 2 } + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + const first = plain() + return [ + order, + await observe(first), + await observe(afterAwait()), + await observe(throwsBefore()), + await observe(throwsAfter()), + ] + `), + ).toEqual([ + ["body"], + ["fulfilled", 42], + ["fulfilled", 43], + ["rejected", 1], + ["rejected", 2], + ]) + }) + + test("default-parameter throws reject instead of escaping the call", async () => { + // Source: test/language/statements/async-function/evaluation-default-that-throws.js + expect( + await value(` + const fail = () => { throw new Error("default") } + const run = async (value = fail()) => value + let returned = false + try { + const promise = run() + returned = promise instanceof Promise + await promise + return [returned, "fulfilled"] + } catch (error) { + return [returned, error.message] + } + `), + ).toEqual([true, "default"]) + }) + + test("async try/finally completion records override earlier completion", async () => { + // Sources: the try-{return,throw,reject}-finally-{return,throw,reject}.js matrix under + // test/language/statements/async-function, test/language/expressions/async-function, + // and test/language/expressions/async-arrow-function. + expect( + await value(` + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + const returnReturn = async () => { try { return "early" } finally { return await Promise.resolve("override") } } + const returnThrow = async () => { try { return "early" } finally { throw "override" } } + const returnReject = async () => { try { return "early" } finally { await Promise.reject("override") } } + const throwReturn = async () => { try { throw "early" } finally { return await Promise.resolve("override") } } + const throwThrow = async () => { try { throw "early" } finally { throw "override" } } + const throwReject = async () => { try { throw "early" } finally { await Promise.reject("override") } } + const rejectReturn = async () => { try { await Promise.reject("early") } finally { return await Promise.resolve("override") } } + const rejectThrow = async () => { try { await Promise.reject("early") } finally { throw "override" } } + const rejectReject = async () => { try { await Promise.reject("early") } finally { await Promise.reject("override") } } + return await Promise.all([ + observe(returnReturn()), observe(returnThrow()), observe(returnReject()), + observe(throwReturn()), observe(throwThrow()), observe(throwReject()), + observe(rejectReturn()), observe(rejectThrow()), observe(rejectReject()), + ]) + `), + ).toEqual([ + ["fulfilled", "override"], + ["rejected", "override"], + ["rejected", "override"], + ["fulfilled", "override"], + ["rejected", "override"], + ["rejected", "override"], + ["fulfilled", "override"], + ["rejected", "override"], + ["rejected", "override"], + ]) + }) + + test("await preserves an object whose then property is not callable", async () => { + // Source: test/language/expressions/await/await-awaits-thenable-not-callable.js + expect( + await value(` + const thenable = { then: 42 } + return (await thenable) === thenable + `), + ).toBe(true) + }) +}) + +describe("Test262 expected Promise conformance", () => { + for (const name of ["all", "allSettled", "race"] as const) { + test.failing(`Promise.${name} rejects invalid input with TypeError`, async () => { + // Sources: + // test/built-ins/Promise/all/S25.4.4.1_A3.1_T1.js + // test/built-ins/Promise/all/S25.4.4.1_A3.1_T2.js + // test/built-ins/Promise/allSettled/iter-arg-is-number-reject.js + // test/built-ins/Promise/race/iter-arg-is-number-reject.js + expect( + await value(` + try { + const promise = Promise.${name}(42) + const returned = promise instanceof Promise + await promise + return [returned, "fulfilled"] + } catch (error) { + return [true, error.name] + } + `), + ).toEqual([true, "TypeError"]) + }) + } + + test.failing("Promise.all consumes sparse positions as undefined", async () => { + // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior) + expect( + await value(` + const input = [] + input[1] = 1 + const result = await Promise.all(input) + return [result.length, result[0] === undefined, result[1]] + `), + ).toEqual([2, true, 1]) + }) + + test.failing("Promise.allSettled consumes sparse positions as undefined", async () => { + // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior) + expect( + await value(` + const input = [] + input[1] = 1 + const result = await Promise.allSettled(input) + return [result.length, result[0].status, result[0].value === undefined, result[1]] + `), + ).toEqual([2, "fulfilled", true, { status: "fulfilled", value: 1 }]) + }) + + test.failing("Promise.race consumes a sparse first position as undefined", async () => { + // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior) + expect( + await value(` + const input = [] + input[1] = 1 + return (await Promise.race(input)) === undefined + `), + ).toBe(true) + }) + + test.failing("Promise.all settles after reactions attached to its inputs", async () => { + // Sources: + // test/built-ins/Promise/all/S25.4.4.1_A7.2_T1.js + // test/built-ins/Promise/all/S25.4.4.1_A8.1_T1.js + expect( + await value(` + const sequence = [1] + const input = Promise.resolve(1) + const aggregate = Promise.all([input]) + aggregate.then(() => sequence.push(4)) + input.then(() => sequence.push(3)).then(() => sequence.push(5)) + sequence.push(2) + await aggregate + await Promise.resolve() + return sequence + `), + ).toEqual([1, 2, 3, 4, 5]) + }) + + test.failing("Promise.allSettled settles after reactions attached to its inputs", async () => { + // Sources: + // test/built-ins/Promise/allSettled/resolved-sequence.js + // test/built-ins/Promise/allSettled/resolved-sequence-extra-ticks.js + // test/built-ins/Promise/allSettled/resolved-sequence-mixed.js + // test/built-ins/Promise/allSettled/resolved-sequence-with-rejections.js + expect( + await value(` + const sequence = [1] + const input = Promise.resolve(1) + const aggregate = Promise.allSettled([input]) + aggregate.then(() => sequence.push(4)) + input.then(() => sequence.push(3)).then(() => sequence.push(5)) + sequence.push(2) + await aggregate + await Promise.resolve() + return sequence + `), + ).toEqual([1, 2, 3, 4, 5]) + }) + + test.failing("Promise.race settles in a reaction after its winning input", async () => { + // Sources: + // test/built-ins/Promise/race/S25.4.4.3_A6.1_T1.js + // test/built-ins/Promise/race/resolved-sequence-extra-ticks.js + expect( + await value(` + const sequence = [1] + const race = Promise.race([1]) + race.then(() => sequence.push(4)) + Promise.resolve().then(() => sequence.push(3)).then(() => sequence.push(5)) + sequence.push(2) + await race + await Promise.resolve() + return sequence + `), + ).toEqual([1, 2, 3, 4, 5]) + }) + + test.failing("then reactions route and propagate fulfillment and rejection", async () => { + // Sources: + // test/built-ins/Promise/prototype/then/prfm-fulfilled.js + // test/built-ins/Promise/prototype/then/prfm-rejected.js + // test/built-ins/Promise/prototype/then/rxn-handler-identity.js + // test/built-ins/Promise/prototype/then/rxn-handler-thrower.js + // test/built-ins/Promise/prototype/then/rxn-handler-fulfilled-return-normal.js + // test/built-ins/Promise/prototype/then/rxn-handler-fulfilled-return-abrupt.js + // test/built-ins/Promise/prototype/then/rxn-handler-rejected-return-normal.js + // test/built-ins/Promise/prototype/then/rxn-handler-rejected-return-abrupt.js + expect( + await value(` + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + return await Promise.all([ + observe(Promise.resolve(1).then((value) => value + 1)), + observe(Promise.reject(2).then(undefined, (reason) => reason + 1)), + observe(Promise.resolve(3).then(undefined)), + observe(Promise.reject(4).then(undefined)), + observe(Promise.resolve(5).then(() => { throw 6 })), + observe(Promise.reject(7).then(undefined, () => { throw 8 })), + ]) + `), + ).toEqual([ + ["fulfilled", 2], + ["fulfilled", 3], + ["fulfilled", 3], + ["rejected", 4], + ["rejected", 6], + ["rejected", 8], + ]) + }) + + test.failing("then reactions preserve breadth-first queue order", async () => { + // Source: test/built-ins/Promise/prototype/then/S25.4.4_A1.1_T1.js + expect( + await value(` + const sequence = [1] + const promise = Promise.resolve() + const first = promise.then(() => sequence.push(3)).then(() => sequence.push(5)).then(() => sequence.push(7)) + const second = promise.then(() => sequence.push(4)).then(() => sequence.push(6)).then(() => sequence.push(8)) + sequence.push(2) + await Promise.all([first, second]) + return sequence + `), + ).toEqual([1, 2, 3, 4, 5, 6, 7, 8]) + }) + + test.failing("then rejects direct self-resolution for fulfilled and rejected sources", async () => { + // Sources: + // test/built-ins/Promise/prototype/then/resolve-settled-fulfilled-self.js + // test/built-ins/Promise/prototype/then/resolve-settled-rejected-self.js + // test/built-ins/Promise/prototype/then/resolve-pending-fulfilled-self.js + // test/built-ins/Promise/prototype/then/resolve-pending-rejected-self.js + expect( + await value(` + const observe = async (promise) => { + try { await promise; return "fulfilled" } catch (reason) { return reason.name } + } + let fulfilled + let rejected + fulfilled = Promise.resolve().then(() => fulfilled) + rejected = Promise.reject().then(undefined, () => rejected) + return await Promise.all([observe(fulfilled), observe(rejected)]) + `), + ).toEqual(["TypeError", "TypeError"]) + }) + + test.failing("catch delegates rejection handling and preserves fulfillment", async () => { + // Sources: + // test/built-ins/Promise/prototype/catch/S25.4.5.1_A2.1_T1.js + // test/built-ins/Promise/prototype/catch/S25.4.5.1_A3.1_T1.js + // test/built-ins/Promise/prototype/catch/S25.4.5.1_A3.1_T2.js + expect( + await value(` + return [ + await Promise.resolve(1).catch(() => 2), + await Promise.reject(3).catch((reason) => reason + 1), + ] + `), + ).toEqual([1, 4]) + }) + + test.failing("finally preserves or replaces the original settlement", async () => { + // Sources: + // test/built-ins/Promise/prototype/finally/resolution-value-no-override.js + // test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js + // test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js + expect( + await value(` + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + return await Promise.all([ + observe(Promise.resolve(1).finally(() => 2)), + observe(Promise.reject(3).finally(() => 4)), + observe(Promise.reject(5).finally(() => { throw 6 })), + ]) + `), + ).toEqual([ + ["fulfilled", 1], + ["rejected", 3], + ["rejected", 6], + ]) + }) + + test.failing("await always resumes in a later reaction and interleaves async functions", async () => { + // Sources: + // test/language/expressions/await/async-await-interleaved.js + // test/language/expressions/await/await-non-promise.js + expect( + await value(` + const sequence = [] + const first = async () => { sequence.push("first:1"); await 0; sequence.push("first:2") } + const second = async () => { sequence.push("second:1"); await 0; sequence.push("second:2") } + await Promise.all([first(), second()]) + return sequence + `), + ).toEqual(["first:1", "second:1", "first:2", "second:2"]) + }) + + test.failing("an async function rejects when it resolves with its own promise", async () => { + // Adapted from the self-resolution requirement represented by: + // test/built-ins/Promise/resolve-self.js + // test/built-ins/Promise/resolve/S25.4.4.5_A4.1_T1.js + expect( + await value(` + let promise + const run = async () => { + await Promise.resolve() + return promise + } + promise = run() + try { + await promise + return "fulfilled" + } catch (error) { + return error.name + } + `), + ).toBe("TypeError") + }) + + test.failing("Promise.resolve recursively assimilates callable thenables", async () => { + // Source: test/built-ins/Promise/resolve/resolve-thenable.js + expect( + await value(` + const value = { id: 1 } + const nested = { then: (resolve) => resolve(value) } + const thenable = { then: (resolve) => resolve(nested) } + return (await Promise.resolve(thenable)) === value + `), + ).toBe(true) + }) + + test.failing("Promise combinators assimilate callable thenable inputs", async () => { + // Sources: + // test/built-ins/Promise/all/reject-immed.js + // test/built-ins/Promise/all/reject-ignored-immed.js + // test/built-ins/Promise/allSettled/reject-ignored-immed.js + // test/built-ins/Promise/race/resolve-thenable.js + expect( + await value(` + const fulfills = { then: (resolve) => resolve(1) } + const rejects = { then: (_, reject) => reject(2) } + const resolvesFirst = { then: (resolve, reject) => { resolve(3); reject(4) } } + const observe = async (promise) => { + try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] } + } + return [ + await observe(Promise.all([fulfills, rejects])), + await Promise.allSettled([fulfills, resolvesFirst]), + await observe(Promise.race([rejects])), + ] + `), + ).toEqual([ + ["rejected", 2], + [ + { status: "fulfilled", value: 1 }, + { status: "fulfilled", value: 3 }, + ], + ["rejected", 2], + ]) + }) + + test.failing("await assimilates callable thenables", async () => { + // Source: test/language/expressions/await/await-awaits-thenables.js + expect( + await value(` + const thenable = { then: (resolve) => resolve(42) } + return await thenable + `), + ).toBe(42) + }) + + test.failing("await rejects when a callable thenable throws", async () => { + // Source: test/language/expressions/await/await-awaits-thenables-that-throw.js + expect( + await value(` + const error = { id: 1 } + const thenable = { then: () => { throw error } } + try { + await thenable + return false + } catch (caught) { + return caught === error + } + `), + ).toBe(true) + }) +}) diff --git a/packages/codemode/test/test262-array.md b/packages/codemode/test/test262-array.md deleted file mode 100644 index b8e7e07a89..0000000000 --- a/packages/codemode/test/test262-array.md +++ /dev/null @@ -1,77 +0,0 @@ -# Test262 Array Coverage - -The Array tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 35 -exposed instance methods and three static methods using actual arrays, accepted argument types, deterministic behavior, -and CodeMode's materialized collection conventions. Each executable case names its exact upstream source path. -`LICENSE.test262` contains the upstream BSD terms. - -This is coverage of CodeMode's bounded Array surface, not a claim of ECMAScript or Test262 conformance. One upstream -file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions -were adapted. - -## Inventory - -The 38 relevant upstream API directories contain 2,837 files. The executable suite adapts assertions from 83 distinct -sources. - -| API | Upstream files | Adapted sources | -| ------------------------------- | -------------: | --------------: | -| `Array.prototype.map` | 216 | 3 | -| `Array.prototype.filter` | 242 | 3 | -| `Array.prototype.find` | 23 | 4 | -| `Array.prototype.findIndex` | 23 | 3 | -| `Array.prototype.findLast` | 24 | 3 | -| `Array.prototype.findLastIndex` | 24 | 3 | -| `Array.prototype.some` | 219 | 2 | -| `Array.prototype.every` | 218 | 2 | -| `Array.prototype.includes` | 30 | 2 | -| `Array.prototype.join` | 23 | 2 | -| `Array.prototype.reduce` | 260 | 3 | -| `Array.prototype.reduceRight` | 260 | 3 | -| `Array.prototype.flatMap` | 24 | 2 | -| `Array.prototype.forEach` | 190 | 2 | -| `Array.prototype.sort` | 54 | 3 | -| `Array.prototype.toSorted` | 21 | 4 | -| `Array.prototype.slice` | 71 | 1 | -| `Array.prototype.concat` | 69 | 3 | -| `Array.prototype.indexOf` | 201 | 2 | -| `Array.prototype.lastIndexOf` | 198 | 2 | -| `Array.prototype.at` | 13 | 3 | -| `Array.prototype.flat` | 19 | 2 | -| `Array.prototype.reverse` | 18 | 1 | -| `Array.prototype.toReversed` | 17 | 2 | -| `Array.prototype.with` | 21 | 2 | -| `Array.prototype.push` | 24 | 1 | -| `Array.prototype.pop` | 23 | 1 | -| `Array.prototype.shift` | 20 | 1 | -| `Array.prototype.unshift` | 22 | 1 | -| `Array.prototype.splice` | 81 | 3 | -| `Array.prototype.fill` | 22 | 3 | -| `Array.prototype.copyWithin` | 39 | 2 | -| `Array.prototype.keys` | 12 | 1 | -| `Array.prototype.values` | 12 | 1 | -| `Array.prototype.entries` | 12 | 1 | -| `Array.from` | 47 | 3 | -| `Array.isArray` | 29 | 2 | -| `Array.of` | 16 | 1 | - -## Exclusions - -Assertions are not adapted when they test behavior outside CodeMode's documented Array surface: - -- Function metadata, property descriptors, constructibility, prototype mutation, species constructors, or cross-realm - identity. -- Generic receivers, detached methods, `.call`, `.apply`, boxed values, custom coercion objects, Symbols, BigInts, - proxies, accessors, frozen arrays, typed arrays, or ArrayBuffers. -- `Array.from` mappers, custom iterables, constructor substitution, and iterator-closing behavior. -- Native iterator identity, `.next()`, completion records, or live iterator mutation. CodeMode deliberately materializes - `keys`, `values`, and `entries` as arrays. -- Sparse-array assertions that depend on literal elisions or inherited indexed properties. CodeMode's confined data - model does not preserve those prototype and hole semantics at every boundary. -- Argument coercions outside the accepted schema-like surface. Numeric positions must be numbers and `join` separators - must be strings. -- Exact native error brands where CodeMode exposes a safe runtime error instead. -- Async/effectful callbacks, circular-data rejection, sandbox-value identity, diagnostics, and host-boundary behavior. - Those remain covered by CodeMode-specific tests. - -Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript Array semantics. diff --git a/packages/codemode/test/test262-string.md b/packages/codemode/test/test262-string.md deleted file mode 100644 index 94d01f83d9..0000000000 --- a/packages/codemode/test/test262-string.md +++ /dev/null @@ -1,69 +0,0 @@ -# Test262 String Coverage - -The String tests adapt Test262 at revision `250f204f23a9249ff204be2baec29600faae7b75`. They cover CodeMode's 32 -exposed instance methods and two static methods using primitive receivers, accepted argument types, and deterministic -behavior. Each executable case names its exact upstream source path. `LICENSE.test262` contains the upstream BSD terms. - -This is coverage of CodeMode's bounded String surface, not a claim of ECMAScript or Test262 conformance. One upstream -file may contain both adapted and inapplicable assertions, so a cited source means only that the represented assertions -were adapted. - -## Inventory - -The relevant upstream directories contain 1,048 files: 1,009 core built-in files, 29 Annex B files for exposed methods, -and 10 Intl `localeCompare` files. The executable suite adapts assertions from 298 distinct sources. - -| API | Upstream files | Adapted sources | -| --- | ---: | ---: | -| `String.fromCharCode` | 17 | 6 | -| `String.fromCodePoint` | 11 | 4 | -| `String.prototype.at` | 11 | 5 | -| `String.prototype.charAt` | 30 | 9 | -| `String.prototype.charCodeAt` | 25 | 4 | -| `String.prototype.codePointAt` | 16 | 6 | -| `String.prototype.concat` | 22 | 1 | -| `String.prototype.endsWith` | 27 | 13 | -| `String.prototype.includes` | 27 | 12 | -| `String.prototype.indexOf` | 47 | 8 | -| `String.prototype.lastIndexOf` | 25 | 1 | -| `String.prototype.localeCompare` | 23 | 1 | -| `String.prototype.match` | 52 | 9 | -| `String.prototype.matchAll` | 26 | 1 | -| `String.prototype.normalize` | 14 | 3 | -| `String.prototype.padEnd` | 13 | 4 | -| `String.prototype.padStart` | 13 | 4 | -| `String.prototype.repeat` | 16 | 4 | -| `String.prototype.replace` | 56 | 16 | -| `String.prototype.replaceAll` | 46 | 12 | -| `String.prototype.search` | 44 | 10 | -| `String.prototype.slice` | 38 | 11 | -| `String.prototype.split` | 121 | 50 | -| `String.prototype.startsWith` | 21 | 7 | -| `String.prototype.substr` | 15 | 6 | -| `String.prototype.substring` | 46 | 12 | -| `String.prototype.toLowerCase` | 30 | 5 | -| `String.prototype.toString` | 7 | 1 | -| `String.prototype.toUpperCase` | 26 | 3 | -| `String.prototype.trim` | 129 | 66 | -| `String.prototype.trimEnd` | 23 | 2 | -| `String.prototype.trimLeft` | 4 | 0 | -| `String.prototype.trimRight` | 4 | 0 | -| `String.prototype.trimStart` | 23 | 2 | - -## Exclusions - -Assertions are not adapted when they test behavior outside CodeMode's documented String surface: - -- Function metadata, property descriptors, constructibility, prototype mutation, or cross-realm identity. -- The `trimLeft`/`trimRight` Test262 files assert prototype function identity, which CodeMode does not expose. Their - supported call behavior remains covered by CodeMode-specific tests. -- Boxed strings, generic receivers, custom coercion objects, Symbols, BigInts, or argument types CodeMode rejects. -- Symbol-based RegExp dispatch, custom matchers, species constructors, or iterator protocol details. CodeMode materializes - `matchAll` results instead of exposing iterators. -- Locale selection and options. CodeMode deliberately uses the host default locale and ignores those arguments. -- Test262 harness behavior or setup syntax unavailable in the confined interpreter. -- Function-replacer behavior that is covered by CodeMode-specific tests for sequential callbacks, async tool calls, - result coercion, diagnostics, and sandbox boundaries. -- Assertions requiring an exact native error type when CodeMode deliberately exposes only its safe runtime error. - -Handwritten tests remain where they specify CodeMode behavior rather than ordinary ECMAScript String semantics.