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/index.ts | 40 ++++++++++++++++++++++++------------- pbt/ts/src/property.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 pbt/ts/src/property.ts (limited to 'pbt/ts/src') diff --git a/pbt/ts/src/index.ts b/pbt/ts/src/index.ts index 3a71ccf..46454f0 100644 --- a/pbt/ts/src/index.ts +++ b/pbt/ts/src/index.ts @@ -1,20 +1,32 @@ -type Result = 'OK' | 'Falsified' | 'Exception'; +import "dotenv/config"; +import Prando from 'prando'; +import { Gen } from "./property"; -type TestResult = { - result: Result, - seed: number, - counterexample: A | null -}; +let genint: Gen = (rng: Prando) => + (size: number) => + rng.nextInt(-size, size); -type Predicate = (a: A) => boolean; -type Gen = (s: number) => (() => A); +function genlist(gen: Gen): Gen { + return (rng: Prando) => { + let g = gen(rng); + return (size: number) => { + let result = []; + for (let i = 0; i < size; i++) { + result.push(g(size)); + } + return result; + }; + }; +} -type Shrinker = (a: A) => [A]; +function generate(gen: Gen): A { + let rng = new Prando(Math.random() * 1000); + return gen(rng)(100); +} -function property(seed: number, - predicate: Predicate, - generator: Gen, - shrinker: Shrinker): TestResult { - return {result: 'OK', seed, counterexample: null}; +async function main() { + console.log('list: ' + generate(genlist(genint))); } + +main(); 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