From 8736fba2ab94cd307905585f81f87d4df9eef3e4 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Mon, 1 Dec 2025 09:08:49 +0100 Subject: more checks on shrinking --- pbt/ts/src/property.ts | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'pbt/ts/src/property.ts') diff --git a/pbt/ts/src/property.ts b/pbt/ts/src/property.ts index 7b3ee59..500c423 100644 --- a/pbt/ts/src/property.ts +++ b/pbt/ts/src/property.ts @@ -20,7 +20,7 @@ type Property = (seed: number, size: number) => TestResult; const MAX_SUCCESS = 100; -function findMinimalCounterExample( +export function findMinimalCounterExample( x: A, predicate: Predicate, shrinker: Shrinker, @@ -117,6 +117,7 @@ export function mapOf1(genK: Gen, genV: Gen): Gen> { }; } +/// Select randomly one element from given list export function elements(arr: A[]): Gen { return (rng: Prando) => (_size: number) => { let index = rng.nextInt(0, arr.length - 1); @@ -124,6 +125,7 @@ export function elements(arr: A[]): Gen { }; } +/// Select one key-value pair from given map export function keyValueOf(map: Map): Gen<[K, V]> { let keys = Array.from(map.keys()); return (rng: Prando) => { @@ -136,12 +138,27 @@ export function keyValueOf(map: Map): Gen<[K, V]> { }; } +/// Choose number between some bounds. export function choose(min: number, max: number): Gen { return (rng: Prando) => (size: number) => rng.nextInt(min, max); } -export let someInt: Gen = (rng: Prando) => (size: number) => - rng.nextInt(-size, size); +/// Choose some random integer. +export let someInt: Gen = (rng: Prando) => (size: number) => { + const bound = Math.pow(2, size); + return rng.nextInt(-bound, bound); +}; + +/// Choose some random positive integer. +export let positiveInt: Gen = (rng: Prando) => (size: number) => { + const bound = Math.pow(2, Math.round(Math.sqrt(size))); + return rng.nextInt(0, bound); +}; + +/// Generate a pair of generated values +export function pairOf(gen: Gen): Gen<[A, A]> { + return (rng: Prando) => (size: number) => [gen(rng)(size), gen(rng)(size)]; +} // basic shrinkers export function shrinklist(arr: A[]): A[][] { @@ -162,6 +179,28 @@ export function shrinklist(arr: A[]): A[][] { return result; } +export function shrinkPositive(i: number): number[] { + if (i == 0) return []; + + return [i - 1, Math.round(Math.sqrt(i))]; +} + +// shrink pair +export function shrinkPair( + shrinkA: Shrinker, + shrinkB: Shrinker, +): Shrinker<[A, B]> { + return ([x, y]) => { + const shrinkx = shrinkA(x); + const shrinky = shrinkB(y); + // assumes x >= y + const resulta: [A, B][] = shrinkx.map(e => [e, y]); + const resultb: [A, B][] = shrinky.map(e => [x, e]); + const resultboth: [A, B][] = shrinkx.map((e, i) => [e, shrinky[i]]); + return resulta.concat(resultb, resultboth); + }; +} + export const Property = { run(prop: Property): TestResult { let seed = Math.floor(Math.random() * 1000000); -- cgit v1.3.1