Skip to content

Commit

Permalink
refactor(pass-style): Make @fast-check/ava a devDependency (#2645)
Browse files Browse the repository at this point in the history
Rather than importing `fc` directly to export arbitraries, @endo/pass-style/tools.js now returns them from an exported `makeArbitraries` function that requires `fc` as an argument.
  • Loading branch information
gibson042 authored Dec 3, 2024
2 parents c6e795e + 31864d4 commit 882e912
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 135 deletions.
4 changes: 3 additions & 1 deletion packages/marshal/test/encodePassable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import test from '@endo/ses-ava/prepare-endo.js';

import { fc } from '@fast-check/ava';
import { Remotable } from '@endo/pass-style';
import { arbPassable } from '@endo/pass-style/tools.js';
import { makeArbitraries } from '@endo/pass-style/tools.js';
import { assert, Fail, q, b } from '@endo/errors';

import {
Expand All @@ -15,6 +15,8 @@ import {
import { compareRank, makeFullOrderComparatorKit } from '../src/rankOrder.js';
import { unsortedSample } from './_marshal-test-data.js';

const { arbPassable } = makeArbitraries(fc);

const statelessEncodePassableLegacy = makeEncodePassable();

const makeSimplePassableKit = ({ statelessSuffix } = {}) => {
Expand Down
4 changes: 3 additions & 1 deletion packages/marshal/test/rankOrder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import test from '@endo/ses-ava/prepare-endo.js';
// eslint-disable-next-line import/no-extraneous-dependencies
import { fc } from '@fast-check/ava';
import { makeTagged } from '@endo/pass-style';
import { arbPassable } from '@endo/pass-style/tools.js';
import { makeArbitraries } from '@endo/pass-style/tools.js';

import { q } from '@endo/errors';
import {
Expand All @@ -18,6 +18,8 @@ import {
} from '../src/rankOrder.js';
import { unsortedSample, sortedSample } from './_marshal-test-data.js';

const { arbPassable } = makeArbitraries(fc);

test('compareRank is reflexive', async t => {
await fc.assert(
fc.property(arbPassable, x => {
Expand Down
4 changes: 2 additions & 2 deletions packages/pass-style/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@
"@endo/env-options": "workspace:^",
"@endo/errors": "workspace:^",
"@endo/eventual-send": "workspace:^",
"@endo/promise-kit": "workspace:^",
"@fast-check/ava": "^1.1.5"
"@endo/promise-kit": "workspace:^"
},
"devDependencies": {
"@endo/init": "workspace:^",
"@endo/ses-ava": "workspace:^",
"@fast-check/ava": "^1.1.5",
"ava": "^6.1.3",
"babel-eslint": "^10.1.0",
"eslint": "^8.57.0",
Expand Down
8 changes: 2 additions & 6 deletions packages/pass-style/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@
// including testing done by other packages. Code dependent only on this
// package from production purposes, such as production code in importing
// packages, should avoid importing tools.
// Note that locally, the depenencies of tools are still listed as
// Note that locally, the dependencies of tools are still listed as
// `dependencies` rather than `devDependencies`.

export {
exampleAlice,
exampleBob,
exampleCarol,
arbString,
arbKeyLeaf,
arbLeaf,
arbKey,
arbPassable,
makeArbitraries,
} from './tools/arb-passable.js';
251 changes: 132 additions & 119 deletions packages/pass-style/tools/arb-passable.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// @ts-check
import '../src/types.js';
import { fc } from '@fast-check/ava';
import { Far } from '../src/make-far.js';
import { makeTagged } from '../src/makeTagged.js';

Expand All @@ -12,130 +11,144 @@ export const exampleAlice = Far('alice', {});
export const exampleBob = Far('bob', {});
export const exampleCarol = Far('carol', {});

export const arbString = fc.oneof(fc.string(), fc.fullUnicodeString());
/** @param {typeof import('@fast-check/ava').fc} fc */
export const makeArbitraries = fc => {
const arbString = fc.oneof(fc.string(), fc.fullUnicodeString());

const keyableLeaves = [
fc.constantFrom(null, undefined, false, true),
arbString,
arbString.map(s => Symbol.for(s)),
// primordial symbols and registered lookalikes
fc.constantFrom(
...Object.getOwnPropertyNames(Symbol).flatMap(k => {
const v = Symbol[k];
if (typeof v !== 'symbol') return [];
return [v, Symbol.for(k), Symbol.for(`@@${k}`)];
}),
),
fc.bigInt(),
fc.integer(),
fc.constantFrom(-0, NaN, Infinity, -Infinity),
fc.record({}),
fc.constantFrom(exampleAlice, exampleBob, exampleCarol),
];
const keyableLeaves = [
fc.constantFrom(null, undefined, false, true),
arbString,
arbString.map(s => Symbol.for(s)),
// primordial symbols and registered lookalikes
fc.constantFrom(
...Object.getOwnPropertyNames(Symbol).flatMap(k => {
const v = Symbol[k];
if (typeof v !== 'symbol') return [];
return [v, Symbol.for(k), Symbol.for(`@@${k}`)];
}),
),
fc.bigInt(),
fc.integer(),
fc.constantFrom(-0, NaN, Infinity, -Infinity),
fc.record({}),
fc.constantFrom(exampleAlice, exampleBob, exampleCarol),
];

export const arbKeyLeaf = fc.oneof(...keyableLeaves);
const arbKeyLeaf = fc.oneof(...keyableLeaves);

export const arbLeaf = fc.oneof(
...keyableLeaves,
arbString.map(s => Error(s)),
// unresolved promise
fc.constant(new Promise(() => {})),
);
const arbLeaf = fc.oneof(
...keyableLeaves,
arbString.map(s => Error(s)),
// unresolved promise
fc.constant(new Promise(() => {})),
);

const { keyDag } = fc.letrec(tie => {
return {
keyDag: fc.oneof(
{ withCrossShrink: true },
arbKeyLeaf,
fc.array(tie('keyDag')),
fc.dictionary(
arbString.filter(s => s !== 'then'),
tie('keyDag'),
const { keyDag } = fc.letrec(tie => {
return {
keyDag: fc.oneof(
{ withCrossShrink: true },
arbKeyLeaf,
fc.array(tie('keyDag')),
fc.dictionary(
arbString.filter(s => s !== 'then'),
tie('keyDag'),
),
),
),
};
});
};
});

const { arbDag } = fc.letrec(tie => {
return {
arbDag: fc.oneof(
{ withCrossShrink: true },
arbLeaf,
fc.array(tie('arbDag')),
fc.dictionary(
arbString.filter(s => s !== 'then'),
tie('arbDag'),
),
// A promise for a passable.
tie('arbDag').map(v => Promise.resolve(v)),
// A tagged value, either of arbitrary type with arbitrary payload
// or of known type with arbitrary or explicitly valid payload.
// Ordered by increasing complexity.
fc
.oneof(
fc.record({ type: arbString, payload: tie('arbDag') }),
fc.record({
type: fc.constantFrom('copySet'),
payload: fc.oneof(
tie('arbDag'),
// copySet valid payload is an array of unique passables.
// TODO: A valid copySet payload must be a reverse sorted array,
// so we should generate some of those as well.
fc.uniqueArray(tie('arbDag')),
),
}),
fc.record({
type: fc.constantFrom('copyBag'),
payload: fc.oneof(
tie('arbDag'),
// copyBag valid payload is an array of [passable, count] tuples
// in which each passable is unique.
// TODO: A valid copyBag payload must be a reverse sorted array,
// so we should generate some of those as well.
fc.uniqueArray(fc.tuple(tie('arbDag'), fc.bigInt()), {
selector: entry => entry[0],
}),
),
}),
fc.record({
type: fc.constantFrom('copyMap'),
payload: fc.oneof(
tie('arbDag'),
// copyMap valid payload is a
// `{ keys: Passable[], values: Passable[]}`
// record in which keys are unique and both arrays have the
// same length.
// TODO: In a valid copyMap payload, the keys must be a
// reverse sorted array, so we should generate some of
// those as well.
fc
.uniqueArray(
fc.record({ key: tie('arbDag'), value: tie('arbDag') }),
{ selector: entry => entry.key },
)
.map(entries => ({
keys: entries.map(({ key }) => key),
values: entries.map(({ value }) => value),
})),
),
}),
)
.map(({ type, payload }) =>
makeTagged(
type,
/** @type {import('../src/types.js').Passable} */ (payload),
),
const { arbDag } = fc.letrec(tie => {
return {
arbDag: fc.oneof(
{ withCrossShrink: true },
arbLeaf,
fc.array(tie('arbDag')),
fc.dictionary(
arbString.filter(s => s !== 'then'),
tie('arbDag'),
),
),
};
});
// A promise for a passable.
tie('arbDag').map(v => Promise.resolve(v)),
// A tagged value, either of arbitrary type with arbitrary payload
// or of known type with arbitrary or explicitly valid payload.
// Ordered by increasing complexity.
fc
.oneof(
fc.record({ type: arbString, payload: tie('arbDag') }),
fc.record({
type: fc.constantFrom('copySet'),
payload: fc.oneof(
tie('arbDag'),
// copySet valid payload is an array of unique passables.
// TODO: A valid copySet payload must be a reverse sorted array,
// so we should generate some of those as well.
fc.uniqueArray(tie('arbDag')),
),
}),
fc.record({
type: fc.constantFrom('copyBag'),
payload: fc.oneof(
tie('arbDag'),
// copyBag valid payload is an array of [passable, count] tuples
// in which each passable is unique.
// TODO: A valid copyBag payload must be a reverse sorted array,
// so we should generate some of those as well.
fc.uniqueArray(fc.tuple(tie('arbDag'), fc.bigInt()), {
selector: entry => entry[0],
}),
),
}),
fc.record({
type: fc.constantFrom('copyMap'),
payload: fc.oneof(
tie('arbDag'),
// copyMap valid payload is a
// `{ keys: Passable[], values: Passable[]}`
// record in which keys are unique and both arrays have the
// same length.
// TODO: In a valid copyMap payload, the keys must be a
// reverse sorted array, so we should generate some of
// those as well.
fc
.uniqueArray(
fc.record({ key: tie('arbDag'), value: tie('arbDag') }),
{ selector: entry => entry.key },
)
.map(entries => ({
keys: entries.map(({ key }) => key),
values: entries.map(({ value }) => value),
})),
),
}),
)
.map(({ type, payload }) => {
const passable = /** @type {import('../src/types.js').Passable} */ (
payload
);
return makeTagged(type, passable);
}),
),
};
});

/**
* A factory for arbitrary keys.
*/
export const arbKey = keyDag.map(x => harden(x));
/**
* A factory for arbitrary keys.
*/
const arbKey = keyDag.map(x => harden(x));

/**
* A factory for arbitrary passables.
*/
export const arbPassable = arbDag.map(x => harden(x));
/**
* A factory for arbitrary passables.
*/
const arbPassable = arbDag.map(x => harden(x));

return {
exampleAlice,
exampleBob,
exampleCarol,
arbString,
arbKeyLeaf,
arbLeaf,
arbKey,
arbPassable,
};
};
9 changes: 3 additions & 6 deletions packages/patterns/test/copySet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import test from '@endo/ses-ava/prepare-endo.js';

import { fc } from '@fast-check/ava';
import { makeTagged, getTag, passStyleOf } from '@endo/marshal';
import {
arbKey,
exampleAlice,
exampleBob,
exampleCarol,
} from '@endo/pass-style/tools.js';
import { makeArbitraries } from '@endo/pass-style/tools.js';
import { Fail, q } from '@endo/errors';
import {
isCopySet,
Expand All @@ -28,6 +23,8 @@ import { M, matches } from '../src/patterns/patternMatchers.js';

import '../src/types.js';

const { arbKey, exampleAlice, exampleBob, exampleCarol } = makeArbitraries(fc);

/** @import { Key } from '../src/types.js'; */

const assertIsCopySet = (t, s) => {
Expand Down

0 comments on commit 882e912

Please sign in to comment.