diff options
Diffstat (limited to 'pbt/ts/src/property.ts')
| -rw-r--r-- | pbt/ts/src/property.ts | 53 |
1 files changed, 53 insertions, 0 deletions
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<A> = { counterexample: A, shrinks: number }; + +type TestResult<A> = { + result: Result, + counterexample: ShrinkResult<A> | null +}; + +type Predicate<A> = (a: A) => boolean; + +export type Gen<A> = (rng: Prando) => ((size: number) => A); + +type Shrinker<A> = (a: A) => [A]; + +const MAX_SUCCESS = 100; + +function findMinimalCounterExample<A>(x: A, + predicate: Predicate<A>, + shrinker: Shrinker<A>, + depth: number = 0): ShrinkResult<A> { + 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<A>(rng: Prando, + size: number, + predicate: Predicate<A>, + generator: Gen<A>, + shrinker: Shrinker<A>): TestResult<A> { + 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 }; +} |
