import {Option, getOrElse, isSome, none, some} from 'fp-ts/lib/Option';
import {LazyArg, pipe} from 'fp-ts/lib/function';
import Prando from 'prando';
type Result = 'OK' | 'Falsified' | 'Exception';
type ShrinkResult = {counterexample: A; shrinks: number};
type TestResult = {
seed: number | null;
result: Result;
counterexample: ShrinkResult | null;
};
type Predicate = (a: A) => boolean;
export type Gen = (rng: Prando) => (size: number) => A;
type Shrinker = (a: A) => A[];
type Property = (seed: number, size: number) => TestResult;
const MAX_SUCCESS = 100;
export 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 generate(gen: Gen): A {
let rng = new Prando(Math.random() * 1000);
return gen(rng)(100);
}
export function property(
generator: Gen,
predicate: Predicate,
shrinker?: Shrinker,
): Property {
return (seed: number, size: number) => {
let rng = new Prando(seed);
let gen = generator(rng);
let i = 0;
for (; i < MAX_SUCCESS; i++) {
let x = gen(size);
if (!predicate(x)) {
let counterexample = {counterexample: x, shrinks: 0};
if (shrinker !== undefined) {
counterexample = findMinimalCounterExample(x, predicate, shrinker);
}
return {seed, result: 'Falsified', tests: i + 1, counterexample};
}
size++;
}
return {seed, result: 'OK', tests: i, counterexample: null};
};
}
// basic generators
export function listOf(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;
};
};
}
/// generates a map with exactly size elements
export function mapOf(genK: Gen, genV: Gen): Gen