From a4b7609b963ac64588313e6531f591ea3fd1d71a Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Sun, 11 Feb 2024 09:46:20 -0800 Subject: [PATCH] test(marshal,exo): stop exporting from test-*.js files --- .../exo/test/test-non-enumerable-methods.js | 58 +-- packages/marshal/test/marshal-test-data.js | 389 ++++++++++++++++++ packages/marshal/test/test-encodePassable.js | 8 +- packages/marshal/test/test-marshal-capdata.js | 152 +------ packages/marshal/test/test-marshal-justin.js | 86 +--- .../marshal/test/test-marshal-smallcaps.js | 2 +- .../marshal/test/test-marshal-stringify.js | 2 +- packages/marshal/test/test-rankOrder.js | 165 +------- 8 files changed, 406 insertions(+), 456 deletions(-) create mode 100644 packages/marshal/test/marshal-test-data.js diff --git a/packages/exo/test/test-non-enumerable-methods.js b/packages/exo/test/test-non-enumerable-methods.js index c25d7cec63..24b28bd3ad 100644 --- a/packages/exo/test/test-non-enumerable-methods.js +++ b/packages/exo/test/test-non-enumerable-methods.js @@ -2,66 +2,12 @@ import { test } from './prepare-test-env-ava.js'; // eslint-disable-next-line import/order +import { objectMetaMap } from '@endo/common/object-meta-map.js'; import { getInterfaceMethodKeys, M } from '@endo/patterns'; import { defineExoClass } from '../src/exo-makers.js'; import { GET_INTERFACE_GUARD } from '../src/get-interface.js'; -const { getPrototypeOf, getOwnPropertyDescriptors, create, fromEntries } = - Object; - -const { ownKeys } = Reflect; - -/** - * Borrowed from https://github.com/endojs/endo/pull/1815 to avoid - * depending on it being merged. TODO If it is merged, then delete this - * copy and import `objectMetaMap` instead. - * - * Like `objectMap`, but at the reflective level of property descriptors - * rather than property values. - * - * Except for hardening, the edge case behavior is mostly the opposite of - * the `objectMap` edge cases. - * * No matter how mutable the original object, the returned object is - * hardened. - * * All own properties of the original are mapped, even if symbol-named - * or non-enumerable. - * * If any of the original properties were accessors, the descriptor - * containing the getter and setter are given to `metaMapFn`. - * * The own properties of the returned are according to the descriptors - * returned by `metaMapFn`. - * * The returned object will always be a plain object whose state is - * only these mapped own properties. It will inherit from the third - * argument if provided, defaulting to `Object.prototype` if omitted. - * - * Because a property descriptor is distinct from `undefined`, we bundle - * mapping and filtering together. When the `metaMapFn` returns `undefined`, - * that property is omitted from the result. - * - * @template {Record} O - * @param {O} original - * @param {( - * desc: TypedPropertyDescriptor, - * key: keyof O - * ) => (PropertyDescriptor | undefined)} metaMapFn - * @param {any} [proto] - * @returns {any} - */ -export const objectMetaMap = ( - original, - metaMapFn, - proto = Object.prototype, -) => { - const descs = getOwnPropertyDescriptors(original); - const keys = ownKeys(original); - - const descEntries = /** @type {[PropertyKey,PropertyDescriptor][]} */ ( - keys - .map(key => [key, metaMapFn(descs[key], key)]) - .filter(([_key, optDesc]) => optDesc !== undefined) - ); - return harden(create(proto, fromEntries(descEntries))); -}; -harden(objectMetaMap); +const { getPrototypeOf } = Object; const UpCounterI = M.interface('UpCounter', { incr: M.call() diff --git a/packages/marshal/test/marshal-test-data.js b/packages/marshal/test/marshal-test-data.js new file mode 100644 index 0000000000..b4be1d07a5 --- /dev/null +++ b/packages/marshal/test/marshal-test-data.js @@ -0,0 +1,389 @@ +import { makeTagged } from '@endo/pass-style'; +import { + exampleAlice, + exampleBob, + exampleCarol, +} from '@endo/pass-style/tools.js'; + +/** + * A list of `[plain, encoding]` pairs, where plain serializes to the + * stringification of `encoding`, which unserializes to something deepEqual + * to `plain`. + */ +export const roundTripPairs = harden([ + // Simple JSON data encodes as itself + [ + [1, 2], + [1, 2], + ], + [{ foo: 1 }, { foo: 1 }], + [{}, {}], + [ + { a: 1, b: 2 }, + { a: 1, b: 2 }, + ], + [ + { a: 1, b: { c: 3 } }, + { a: 1, b: { c: 3 } }, + ], + [true, true], + [1, 1], + ['abc', 'abc'], + [null, null], + + // proto problems + // The one case where JSON is not a semantic subset of JS + // Fails before https://github.com/endojs/endo/issues/1303 fix + [{ ['__proto__']: {} }, { ['__proto__']: {} }], + // Conflicts with non-overwrite-enable inherited frozen data property + // Fails before https://github.com/endojs/endo/issues/1303 fix + [{ isPrototypeOf: {} }, { isPrototypeOf: {} }], + + // Scalars not represented in JSON + [undefined, { '@qclass': 'undefined' }], + [NaN, { '@qclass': 'NaN' }], + [Infinity, { '@qclass': 'Infinity' }], + [-Infinity, { '@qclass': '-Infinity' }], + [4n, { '@qclass': 'bigint', digits: '4' }], + // Does not fit into a number + [9007199254740993n, { '@qclass': 'bigint', digits: '9007199254740993' }], + + // Well known symbols + [Symbol.asyncIterator, { '@qclass': 'symbol', name: '@@asyncIterator' }], + [Symbol.match, { '@qclass': 'symbol', name: '@@match' }], + // Registered symbols + [Symbol.for('foo'), { '@qclass': 'symbol', name: 'foo' }], + // Registered symbol hilbert hotel + [Symbol.for('@@foo'), { '@qclass': 'symbol', name: '@@@@foo' }], + + // Normal json reviver cannot make properties with undefined values + [[undefined], [{ '@qclass': 'undefined' }]], + [{ foo: undefined }, { foo: { '@qclass': 'undefined' } }], + + // tagged + [ + makeTagged('x', 8), + { + '@qclass': 'tagged', + tag: 'x', + payload: 8, + }, + ], + [ + makeTagged('x', undefined), + { + '@qclass': 'tagged', + tag: 'x', + payload: { '@qclass': 'undefined' }, + }, + ], + + // errors + [ + Error(), + { + '@qclass': 'error', + message: '', + name: 'Error', + }, + ], + [ + ReferenceError('msg'), + { + '@qclass': 'error', + message: 'msg', + name: 'ReferenceError', + }, + ], + [ + ReferenceError('#msg'), + { + '@qclass': 'error', + message: '#msg', + name: 'ReferenceError', + }, + ], + + // Hilbert hotel + [ + { '@qclass': 8 }, + { + '@qclass': 'hilbert', + original: 8, + }, + ], + [ + { '@qclass': '@qclass' }, + { + '@qclass': 'hilbert', + original: '@qclass', + }, + ], + [ + { '@qclass': { '@qclass': 8 } }, + { + '@qclass': 'hilbert', + original: { + '@qclass': 'hilbert', + original: 8, + }, + }, + ], + [ + { + '@qclass': { + '@qclass': 8, + foo: 'foo1', + }, + bar: { '@qclass': undefined }, + }, + { + '@qclass': 'hilbert', + original: { + '@qclass': 'hilbert', + original: 8, + rest: { foo: 'foo1' }, + }, + rest: { + bar: { + '@qclass': 'hilbert', + original: { '@qclass': 'undefined' }, + }, + }, + }, + ], +]); + +/** + * Based on roundTripPairs from round-trip-pairs.js + * + * A list of `[body, justinSrc]` pairs, where the body parses into + * an encoding that decodes to a Justin expression that evaluates to something + * that has the same encoding. + * + * @type {([string, string] | [string, string, unknown[]])[]} + */ +export const jsonJustinPairs = harden([ + // Justin is the same as the JSON encoding but without unnecessary quoting + ['[1,2]', '[1,2]'], + ['{"foo":1}', '{foo:1}'], + ['{"a":1,"b":2}', '{a:1,b:2}'], + ['{"a":1,"b":{"c":3}}', '{a:1,b:{c:3}}'], + ['true', 'true'], + ['1', '1'], + ['"abc"', '"abc"'], + ['null', 'null'], + + // Primitives not representable in JSON + ['{"@qclass":"undefined"}', 'undefined'], + ['{"@qclass":"NaN"}', 'NaN'], + ['{"@qclass":"Infinity"}', 'Infinity'], + ['{"@qclass":"-Infinity"}', '-Infinity'], + ['{"@qclass":"bigint","digits":"4"}', '4n'], + ['{"@qclass":"bigint","digits":"9007199254740993"}', '9007199254740993n'], + ['{"@qclass":"symbol","name":"@@asyncIterator"}', 'Symbol.asyncIterator'], + ['{"@qclass":"symbol","name":"@@match"}', 'Symbol.match'], + ['{"@qclass":"symbol","name":"foo"}', 'Symbol.for("foo")'], + ['{"@qclass":"symbol","name":"@@@@foo"}', 'Symbol.for("@@foo")'], + + // Arrays and objects + ['[{"@qclass":"undefined"}]', '[undefined]'], + ['{"foo":{"@qclass":"undefined"}}', '{foo:undefined}'], + ['{"@qclass":"error","message":"","name":"Error"}', 'Error("")'], + [ + '{"@qclass":"error","message":"msg","name":"ReferenceError"}', + 'ReferenceError("msg")', + ], + + // The one case where JSON is not a semantic subset of JS + ['{"__proto__":8}', '{["__proto__"]:8}'], + + // The Hilbert Hotel is always tricky + ['{"@qclass":"hilbert","original":8}', '{"@qclass":8}'], + ['{"@qclass":"hilbert","original":"@qclass"}', '{"@qclass":"@qclass"}'], + [ + '{"@qclass":"hilbert","original":{"@qclass":"hilbert","original":8}}', + '{"@qclass":{"@qclass":8}}', + ], + [ + '{"@qclass":"hilbert","original":{"@qclass":"hilbert","original":8,"rest":{"foo":"foo1"}},"rest":{"bar":{"@qclass":"hilbert","original":{"@qclass":"undefined"}}}}', + '{"@qclass":{"@qclass":8,foo:"foo1"},bar:{"@qclass":undefined}}', + ], + + // tagged + ['{"@qclass":"tagged","tag":"x","payload":8}', 'makeTagged("x",8)'], + [ + '{"@qclass":"tagged","tag":"x","payload":{"@qclass":"undefined"}}', + 'makeTagged("x",undefined)', + ], + + // Slots + [ + '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0}]', + '[slot(0,"Alleged: for testing Justin")]', + ], + // More Slots + [ + '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0},{"@qclass":"slot","iface":"Remotable","index":1}]', + '[slotToVal("hello","Alleged: for testing Justin"),slotToVal(null,"Remotable")]', + ['hello', null], + ], + // Tests https://github.com/endojs/endo/issues/1185 fix + [ + '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0},{"@qclass":"slot","index":0}]', + '[slot(0,"Alleged: for testing Justin"),slot(0)]', + ], +]); + +/** + * An unordered copyArray of some passables + */ +export const unsortedSample = harden([ + makeTagged('copySet', [ + ['b', 3], + ['a', 4], + ]), + 'foo', + 3n, + 'barr', + undefined, + [5, { foo: 4 }], + 2, + null, + [5, { foo: 4, bar: null }], + exampleBob, + 0, + makeTagged('copySet', [ + ['a', 4], + ['b', 3], + ]), + NaN, + true, + undefined, + -Infinity, + [5], + exampleAlice, + [], + Symbol.for('foo'), + Error('not erroneous'), + Symbol.for('@@foo'), + [5, { bar: 5 }], + Symbol.for(''), + false, + exampleCarol, + -0, + {}, + [5, undefined], + -3, + makeTagged('copyMap', [ + ['a', 4], + ['b', 3], + ]), + true, + 'bar', + [5, null], + new Promise(() => {}), // forever unresolved + makeTagged('nonsense', [ + ['a', 4], + ['b', 3], + ]), + Infinity, + Symbol.isConcatSpreadable, + [5, { foo: 4, bar: undefined }], + Promise.resolve('fulfillment'), + [5, { foo: 4 }], + // The promises should be of the same rank, in which case + // the singleton array should be earlier. But if the encoded + // gives the earlier promise an earlier encoding (as it used to), + // then the encoded forms will not be order preserving. + [Promise.resolve(null), 'x'], + [Promise.resolve(null)], +]); + +const rejectedP = Promise.reject(Error('broken')); +rejectedP.catch(() => {}); // Suppress unhandled rejection warning/error + +/** + * The correctly stable rank sorting of `sample` + */ +export const sortedSample = harden([ + // All errors are tied. + Error('different'), + + {}, + + // Lexicographic tagged: tag then payload + makeTagged('copyMap', [ + ['a', 4], + ['b', 3], + ]), + makeTagged('copySet', [ + ['a', 4], + ['b', 3], + ]), + // Doesn't care if a valid copySet + makeTagged('copySet', [ + ['b', 3], + ['a', 4], + ]), + // Doesn't care if a recognized tagged tag + makeTagged('nonsense', [ + ['a', 4], + ['b', 3], + ]), + + // All promises are tied. + rejectedP, + rejectedP, + + // Lexicographic arrays. Shorter beats longer. + // Lexicographic records by reverse sorted property name, then by values + // in that order. + [], + [Promise.resolve(null)], + [Promise.resolve(null), 'x'], + [5], + [5, { bar: 5 }], + [5, { foo: 4 }], + [5, { foo: 4 }], + [5, { foo: 4, bar: null }], + [5, { foo: 4, bar: undefined }], + [5, null], + [5, undefined], + + false, + true, + true, + + // -0 is equivalent enough to 0. NaN after all numbers. + -Infinity, + -3, + -0, + 0, + 2, + Infinity, + NaN, + + 3n, + + // All remotables are tied for the same rank and the sort is stable, + // so their relative order is preserved + exampleBob, + exampleAlice, + exampleCarol, + + // Lexicographic strings. Shorter beats longer. + // TODO Probe UTF-16 vs Unicode vs UTF-8 (Moddable) ordering. + 'bar', + 'barr', + 'foo', + + null, + Symbol.for(''), + Symbol.for('@@foo'), + Symbol.isConcatSpreadable, + Symbol.for('foo'), + + undefined, + undefined, +]); diff --git a/packages/marshal/test/test-encodePassable.js b/packages/marshal/test/test-encodePassable.js index 1acd3da4a9..5447deba8e 100644 --- a/packages/marshal/test/test-encodePassable.js +++ b/packages/marshal/test/test-encodePassable.js @@ -17,7 +17,7 @@ import { makeDecodePassable, } from '../src/encodePassable.js'; import { compareRank, makeComparatorKit } from '../src/rankOrder.js'; -import { sample } from './test-rankOrder.js'; +import { unsortedSample } from './marshal-test-data.js'; const buffers = { __proto__: null, @@ -420,9 +420,9 @@ test('Passable encoding corresponds to rankOrder', async t => { // the heuristic generation of fuzzing test cases, it always checks everything // in `sample`. test('Also test against all enumerated in sample', t => { - for (let i = 0; i < sample.length; i += 1) { - for (let j = i; j < sample.length; j += 1) { - orderInvariants(sample[i], sample[j]); + for (let i = 0; i < unsortedSample.length; i += 1) { + for (let j = i; j < unsortedSample.length; j += 1) { + orderInvariants(unsortedSample[i], unsortedSample[j]); } } // Ensure at least one ava assertion. diff --git a/packages/marshal/test/test-marshal-capdata.js b/packages/marshal/test/test-marshal-capdata.js index a828ac7e2f..5cacdc1d7b 100644 --- a/packages/marshal/test/test-marshal-capdata.js +++ b/packages/marshal/test/test-marshal-capdata.js @@ -1,8 +1,9 @@ import { test } from './prepare-test-env-ava.js'; // eslint-disable-next-line import/order -import { passStyleOf, makeTagged, Far } from '@endo/pass-style'; +import { passStyleOf, Far } from '@endo/pass-style'; import { makeMarshal } from '../src/marshal.js'; +import { roundTripPairs } from './marshal-test-data.js'; const { freeze, isFrozen, create, prototype: objectPrototype } = Object; @@ -13,155 +14,6 @@ const harden = /** @type {import('ses').Harden & { isFake?: boolean }} */ ( // this only includes the tests that do not use liveSlots -/** - * A list of `[plain, encoding]` pairs, where plain serializes to the - * stringification of `encoding`, which unserializes to something deepEqual - * to `plain`. - */ -export const roundTripPairs = harden([ - // Simple JSON data encodes as itself - [ - [1, 2], - [1, 2], - ], - [{ foo: 1 }, { foo: 1 }], - [{}, {}], - [ - { a: 1, b: 2 }, - { a: 1, b: 2 }, - ], - [ - { a: 1, b: { c: 3 } }, - { a: 1, b: { c: 3 } }, - ], - [true, true], - [1, 1], - ['abc', 'abc'], - [null, null], - - // proto problems - // The one case where JSON is not a semantic subset of JS - // Fails before https://github.com/endojs/endo/issues/1303 fix - [{ ['__proto__']: {} }, { ['__proto__']: {} }], - // Conflicts with non-overwrite-enable inherited frozen data property - // Fails before https://github.com/endojs/endo/issues/1303 fix - [{ isPrototypeOf: {} }, { isPrototypeOf: {} }], - - // Scalars not represented in JSON - [undefined, { '@qclass': 'undefined' }], - [NaN, { '@qclass': 'NaN' }], - [Infinity, { '@qclass': 'Infinity' }], - [-Infinity, { '@qclass': '-Infinity' }], - [4n, { '@qclass': 'bigint', digits: '4' }], - // Does not fit into a number - [9007199254740993n, { '@qclass': 'bigint', digits: '9007199254740993' }], - - // Well known symbols - [Symbol.asyncIterator, { '@qclass': 'symbol', name: '@@asyncIterator' }], - [Symbol.match, { '@qclass': 'symbol', name: '@@match' }], - // Registered symbols - [Symbol.for('foo'), { '@qclass': 'symbol', name: 'foo' }], - // Registered symbol hilbert hotel - [Symbol.for('@@foo'), { '@qclass': 'symbol', name: '@@@@foo' }], - - // Normal json reviver cannot make properties with undefined values - [[undefined], [{ '@qclass': 'undefined' }]], - [{ foo: undefined }, { foo: { '@qclass': 'undefined' } }], - - // tagged - [ - makeTagged('x', 8), - { - '@qclass': 'tagged', - tag: 'x', - payload: 8, - }, - ], - [ - makeTagged('x', undefined), - { - '@qclass': 'tagged', - tag: 'x', - payload: { '@qclass': 'undefined' }, - }, - ], - - // errors - [ - Error(), - { - '@qclass': 'error', - message: '', - name: 'Error', - }, - ], - [ - ReferenceError('msg'), - { - '@qclass': 'error', - message: 'msg', - name: 'ReferenceError', - }, - ], - [ - ReferenceError('#msg'), - { - '@qclass': 'error', - message: '#msg', - name: 'ReferenceError', - }, - ], - - // Hilbert hotel - [ - { '@qclass': 8 }, - { - '@qclass': 'hilbert', - original: 8, - }, - ], - [ - { '@qclass': '@qclass' }, - { - '@qclass': 'hilbert', - original: '@qclass', - }, - ], - [ - { '@qclass': { '@qclass': 8 } }, - { - '@qclass': 'hilbert', - original: { - '@qclass': 'hilbert', - original: 8, - }, - }, - ], - [ - { - '@qclass': { - '@qclass': 8, - foo: 'foo1', - }, - bar: { '@qclass': undefined }, - }, - { - '@qclass': 'hilbert', - original: { - '@qclass': 'hilbert', - original: 8, - rest: { foo: 'foo1' }, - }, - rest: { - bar: { - '@qclass': 'hilbert', - original: { '@qclass': 'undefined' }, - }, - }, - }, - ], -]); - /** * @param {import('../src/types.js').MakeMarshalOptions} [opts] */ diff --git a/packages/marshal/test/test-marshal-justin.js b/packages/marshal/test/test-marshal-justin.js index 0980037304..8ff70d1eea 100644 --- a/packages/marshal/test/test-marshal-justin.js +++ b/packages/marshal/test/test-marshal-justin.js @@ -4,90 +4,10 @@ import { test } from './prepare-test-env-ava.js'; import { Remotable, makeTagged } from '@endo/pass-style'; import { makeMarshal } from '../src/marshal.js'; import { decodeToJustin } from '../src/marshal-justin.js'; +import { jsonJustinPairs } from './marshal-test-data.js'; // this only includes the tests that do not use liveSlots -/** - * Based on roundTripPairs from test-marshal-capdata.js - * - * A list of `[body, justinSrc]` pairs, where the body parses into - * an encoding that decodes to a Justin expression that evaluates to something - * that has the same encoding. - * - * @type {([string, string] | [string, string, unknown[]])[]} - */ -export const jsonPairs = harden([ - // Justin is the same as the JSON encoding but without unnecessary quoting - ['[1,2]', '[1,2]'], - ['{"foo":1}', '{foo:1}'], - ['{"a":1,"b":2}', '{a:1,b:2}'], - ['{"a":1,"b":{"c":3}}', '{a:1,b:{c:3}}'], - ['true', 'true'], - ['1', '1'], - ['"abc"', '"abc"'], - ['null', 'null'], - - // Primitives not representable in JSON - ['{"@qclass":"undefined"}', 'undefined'], - ['{"@qclass":"NaN"}', 'NaN'], - ['{"@qclass":"Infinity"}', 'Infinity'], - ['{"@qclass":"-Infinity"}', '-Infinity'], - ['{"@qclass":"bigint","digits":"4"}', '4n'], - ['{"@qclass":"bigint","digits":"9007199254740993"}', '9007199254740993n'], - ['{"@qclass":"symbol","name":"@@asyncIterator"}', 'Symbol.asyncIterator'], - ['{"@qclass":"symbol","name":"@@match"}', 'Symbol.match'], - ['{"@qclass":"symbol","name":"foo"}', 'Symbol.for("foo")'], - ['{"@qclass":"symbol","name":"@@@@foo"}', 'Symbol.for("@@foo")'], - - // Arrays and objects - ['[{"@qclass":"undefined"}]', '[undefined]'], - ['{"foo":{"@qclass":"undefined"}}', '{foo:undefined}'], - ['{"@qclass":"error","message":"","name":"Error"}', 'Error("")'], - [ - '{"@qclass":"error","message":"msg","name":"ReferenceError"}', - 'ReferenceError("msg")', - ], - - // The one case where JSON is not a semantic subset of JS - ['{"__proto__":8}', '{["__proto__"]:8}'], - - // The Hilbert Hotel is always tricky - ['{"@qclass":"hilbert","original":8}', '{"@qclass":8}'], - ['{"@qclass":"hilbert","original":"@qclass"}', '{"@qclass":"@qclass"}'], - [ - '{"@qclass":"hilbert","original":{"@qclass":"hilbert","original":8}}', - '{"@qclass":{"@qclass":8}}', - ], - [ - '{"@qclass":"hilbert","original":{"@qclass":"hilbert","original":8,"rest":{"foo":"foo1"}},"rest":{"bar":{"@qclass":"hilbert","original":{"@qclass":"undefined"}}}}', - '{"@qclass":{"@qclass":8,foo:"foo1"},bar:{"@qclass":undefined}}', - ], - - // tagged - ['{"@qclass":"tagged","tag":"x","payload":8}', 'makeTagged("x",8)'], - [ - '{"@qclass":"tagged","tag":"x","payload":{"@qclass":"undefined"}}', - 'makeTagged("x",undefined)', - ], - - // Slots - [ - '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0}]', - '[slot(0,"Alleged: for testing Justin")]', - ], - // More Slots - [ - '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0},{"@qclass":"slot","iface":"Remotable","index":1}]', - '[slotToVal("hello","Alleged: for testing Justin"),slotToVal(null,"Remotable")]', - ['hello', null], - ], - // Tests https://github.com/endojs/endo/issues/1185 fix - [ - '[{"@qclass":"slot","iface":"Alleged: for testing Justin","index":0},{"@qclass":"slot","index":0}]', - '[slot(0,"Alleged: for testing Justin"),slot(0)]', - ], -]); - const fakeJustinCompartment = () => { const slots = []; const slotVals = new Map(); @@ -124,7 +44,7 @@ test('serialize decodeToJustin eval round trip pairs', t => { // TODO make Justin work with smallcaps serializeBodyFormat: 'capdata', }); - for (const [body, justinSrc, slots] of jsonPairs) { + for (const [body, justinSrc, slots] of jsonJustinPairs) { const c = fakeJustinCompartment(); const encoding = JSON.parse(body); const justinExpr = decodeToJustin(encoding, false, slots); @@ -148,7 +68,7 @@ test('serialize decodeToJustin indented eval round trip', t => { // TODO make Justin work with smallcaps serializeBodyFormat: 'capdata', }); - for (const [body, _, slots] of jsonPairs) { + for (const [body, _, slots] of jsonJustinPairs) { const c = fakeJustinCompartment(); t.log(body); const encoding = JSON.parse(body); diff --git a/packages/marshal/test/test-marshal-smallcaps.js b/packages/marshal/test/test-marshal-smallcaps.js index d413e87b68..78bce8cc6b 100644 --- a/packages/marshal/test/test-marshal-smallcaps.js +++ b/packages/marshal/test/test-marshal-smallcaps.js @@ -4,7 +4,7 @@ import { test } from './prepare-test-env-ava.js'; import { Far, makeTagged, passStyleOf } from '@endo/pass-style'; import { makeMarshal } from '../src/marshal.js'; -import { roundTripPairs } from './test-marshal-capdata.js'; +import { roundTripPairs } from './marshal-test-data.js'; const { freeze, isFrozen, create, prototype: objectPrototype } = Object; diff --git a/packages/marshal/test/test-marshal-stringify.js b/packages/marshal/test/test-marshal-stringify.js index 4b33c9b302..3d6490502b 100644 --- a/packages/marshal/test/test-marshal-stringify.js +++ b/packages/marshal/test/test-marshal-stringify.js @@ -3,7 +3,7 @@ import { test } from './prepare-test-env-ava.js'; // eslint-disable-next-line import/order import { Far } from '@endo/pass-style'; import { stringify, parse } from '../src/marshal-stringify.js'; -import { roundTripPairs } from './test-marshal-capdata.js'; +import { roundTripPairs } from './marshal-test-data.js'; const { isFrozen } = Object; diff --git a/packages/marshal/test/test-rankOrder.js b/packages/marshal/test/test-rankOrder.js index 048734b2c5..94e6999abd 100644 --- a/packages/marshal/test/test-rankOrder.js +++ b/packages/marshal/test/test-rankOrder.js @@ -5,12 +5,7 @@ import { test } from './prepare-test-env-ava.js'; // eslint-disable-next-line import/no-extraneous-dependencies import { fc } from '@fast-check/ava'; import { makeTagged } from '@endo/pass-style'; -import { - exampleAlice, - exampleBob, - exampleCarol, - arbPassable, -} from '@endo/pass-style/tools.js'; +import { arbPassable } from '@endo/pass-style/tools.js'; import { q } from '@endo/errors'; import { @@ -22,6 +17,7 @@ import { getIndexCover, assertRankSorted, } from '../src/rankOrder.js'; +import { unsortedSample, sortedSample } from './marshal-test-data.js'; test('compareRank is reflexive', async t => { await fc.assert( @@ -107,163 +103,10 @@ test('compareRank is transitive', async t => { ); }); -/** - * An unordered copyArray of some passables - */ -export const sample = harden([ - makeTagged('copySet', [ - ['b', 3], - ['a', 4], - ]), - 'foo', - 3n, - 'barr', - undefined, - [5, { foo: 4 }], - 2, - null, - [5, { foo: 4, bar: null }], - exampleBob, - 0, - makeTagged('copySet', [ - ['a', 4], - ['b', 3], - ]), - NaN, - true, - undefined, - -Infinity, - [5], - exampleAlice, - [], - Symbol.for('foo'), - Error('not erroneous'), - Symbol.for('@@foo'), - [5, { bar: 5 }], - Symbol.for(''), - false, - exampleCarol, - -0, - {}, - [5, undefined], - -3, - makeTagged('copyMap', [ - ['a', 4], - ['b', 3], - ]), - true, - 'bar', - [5, null], - new Promise(() => {}), // forever unresolved - makeTagged('nonsense', [ - ['a', 4], - ['b', 3], - ]), - Infinity, - Symbol.isConcatSpreadable, - [5, { foo: 4, bar: undefined }], - Promise.resolve('fulfillment'), - [5, { foo: 4 }], - // The promises should be of the same rank, in which case - // the singleton array should be earlier. But if the encoded - // gives the earlier promise an earlier encoding (as it used to), - // then the encoded forms will not be order preserving. - [Promise.resolve(null), 'x'], - [Promise.resolve(null)], -]); - -const rejectedP = Promise.reject(Error('broken')); -rejectedP.catch(() => {}); // Suppress unhandled rejection warning/error - -/** - * The correctly stable rank sorting of `sample` - */ -const sortedSample = harden([ - // All errors are tied. - Error('different'), - - {}, - - // Lexicographic tagged: tag then payload - makeTagged('copyMap', [ - ['a', 4], - ['b', 3], - ]), - makeTagged('copySet', [ - ['a', 4], - ['b', 3], - ]), - // Doesn't care if a valid copySet - makeTagged('copySet', [ - ['b', 3], - ['a', 4], - ]), - // Doesn't care if a recognized tagged tag - makeTagged('nonsense', [ - ['a', 4], - ['b', 3], - ]), - - // All promises are tied. - rejectedP, - rejectedP, - - // Lexicographic arrays. Shorter beats longer. - // Lexicographic records by reverse sorted property name, then by values - // in that order. - [], - [Promise.resolve(null)], - [Promise.resolve(null), 'x'], - [5], - [5, { bar: 5 }], - [5, { foo: 4 }], - [5, { foo: 4 }], - [5, { foo: 4, bar: null }], - [5, { foo: 4, bar: undefined }], - [5, null], - [5, undefined], - - false, - true, - true, - - // -0 is equivalent enough to 0. NaN after all numbers. - -Infinity, - -3, - -0, - 0, - 2, - Infinity, - NaN, - - 3n, - - // All remotables are tied for the same rank and the sort is stable, - // so their relative order is preserved - exampleBob, - exampleAlice, - exampleCarol, - - // Lexicographic strings. Shorter beats longer. - // TODO Probe UTF-16 vs Unicode vs UTF-8 (Moddable) ordering. - 'bar', - 'barr', - 'foo', - - null, - Symbol.for(''), - Symbol.for('@@foo'), - Symbol.isConcatSpreadable, - Symbol.for('foo'), - - undefined, - undefined, -]); - test('compare and sort by rank', t => { assertRankSorted(sortedSample, compareRank); - t.false(isRankSorted(sample, compareRank)); - const sorted = sortByRank(sample, compareRank); + t.false(isRankSorted(unsortedSample, compareRank)); + const sorted = sortByRank(unsortedSample, compareRank); t.is( compareRank(sorted, sortedSample), 0,