From 8bcaac40ee1afb8c36f98beabe0348ae3713d44d Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Sat, 25 Jan 2025 18:31:41 +0100 Subject: Generate a random list --- pbt/ts/src/property.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 pbt/ts/src/property.ts (limited to 'pbt/ts/src/property.ts') diff --git a/pbt/ts/src/property.ts b/pbt/ts/src/property.ts new file mode 100644 index 0000000..e8d3a21 --- /dev/null +++ b/pbt/ts/src/property.ts @@ -0,0 +1,53 @@ +import Prando from "prando"; + +type Result = 'OK' | 'Falsified' | 'Exception'; + +type ShrinkResult = { counterexample: A, shrinks: number }; + +type TestResult = { + result: Result, + counterexample: ShrinkResult | null +}; + +type Predicate = (a: A) => boolean; + +export type Gen = (rng: Prando) => ((size: number) => A); + +type Shrinker = (a: A) => [A]; + +const MAX_SUCCESS = 100; + +function findMinimalCounterExample(x: A, + predicate: Predicate, + shrinker: Shrinker, + depth: number = 0): ShrinkResult { + let xs = shrinker(x); + let counterexample = x; + let shrinks = depth; + for (let y of xs) { + if (!predicate(y)) { + let shrink = findMinimalCounterExample(y, predicate, shrinker, depth + 1); + if (shrink.shrinks > depth) { + counterexample = shrink.counterexample; + shrinks = shrink.shrinks; + } + } + } + return { counterexample, shrinks }; +} + +export function property(rng: Prando, + size: number, + predicate: Predicate, + generator: Gen, + shrinker: Shrinker): TestResult { + let gen = generator(rng); + for (let i = 0; i < MAX_SUCCESS; i++) { + let x = gen(size); + if (!predicate(x)) { + let counterexample = findMinimalCounterExample(x, predicate, shrinker); + return { result: 'Falsified', counterexample }; + } + } + return { result: 'OK', counterexample: null }; +} -- cgit v1.2.3