diff options
Diffstat (limited to 'pbt')
| -rw-r--r-- | pbt/ts/src/index.test.ts | 12 | ||||
| -rw-r--r-- | pbt/ts/src/index.ts | 4 | ||||
| -rw-r--r-- | pbt/ts/src/property.ts | 39 |
3 files changed, 51 insertions, 4 deletions
diff --git a/pbt/ts/src/index.test.ts b/pbt/ts/src/index.test.ts index b102fe3..f3a51e1 100644 --- a/pbt/ts/src/index.test.ts +++ b/pbt/ts/src/index.test.ts @@ -1,5 +1,8 @@ -import {prop_reverse_with_two_lists} from './index'; -import {Property} from './property'; +import { + prop_reverse_is_self_inverse, + prop_reverse_with_two_lists, +} from './index'; +import {Property, classify} from './property'; describe('testing lists reversal', () => { test('reverse of two appended lists is append of each list reversed', async () => { @@ -8,4 +11,9 @@ describe('testing lists reversal', () => { ); expect(reverse_append_result.result).toBe('OK'); }); + test('reverse is self-inverse', async () => { + const result = await Property.run(prop_reverse_is_self_inverse); + console.log(classify(result)); + expect(result.result).toBe('OK'); + }); }); diff --git a/pbt/ts/src/index.ts b/pbt/ts/src/index.ts index 2f59f0e..7a58ffe 100644 --- a/pbt/ts/src/index.ts +++ b/pbt/ts/src/index.ts @@ -63,4 +63,8 @@ export const prop_reverse_is_self_inverse = property( listOf(someInt), reverse_is_self_inverse, shrinklist, + l => { + const tens = Math.floor(l.length / 10); + return '< ' + 10 * (tens + 1); + }, ); diff --git a/pbt/ts/src/property.ts b/pbt/ts/src/property.ts index 635e731..ea7a62b 100644 --- a/pbt/ts/src/property.ts +++ b/pbt/ts/src/property.ts @@ -9,6 +9,7 @@ type ShrinkResult<A> = {counterexample: A; shrinks: number}; type TestResult<A> = { seed: number | null; result: Result; + labels: string[]; counterexample: ShrinkResult<A> | null; }; @@ -18,6 +19,28 @@ export type Gen<A> = (rng: Prando) => (size: number) => A; type Shrinker<A> = (a: A) => A[]; +/// Label test input with some arbitrary string for classification purpose +type Label<A> = (a: A) => string; + +/// classify test result +export function classify<A>(results: TestResult<A>): [number, string][] { + if (results.labels.length == 0) return []; + + const distribution = new Map<string, number>(); + const result: [number, string][] = []; + results.labels.sort(); + results.labels.forEach(s => { + const num = distribution.get(s) || 0; + distribution.set(s, num + 1); + }); + + distribution.forEach((v, k) => { + result.push([(v / results.labels.length) * 100.0, k]); + }); + + return result; +} + type Property<A> = (seed: number, size: number) => Promise<TestResult<A>>; const MAX_SUCCESS = 100; @@ -57,10 +80,13 @@ export async function property<A>( generator: Gen<A>, predicate: Predicate<A>, shrinker?: Shrinker<A>, + label?: Label<A>, ): Promise<Property<A>> { return async (seed: number, size: number) => { const rng = new Prando(seed); const gen = generator(rng); + const labels: string[] = []; + let i = 0; for (; i < MAX_SUCCESS; i++) { const x = gen(size); @@ -73,11 +99,20 @@ export async function property<A>( shrinker, ); } - return {seed, result: 'Falsified', tests: i + 1, counterexample}; + return { + seed, + result: 'Falsified', + tests: i + 1, + labels: [], + counterexample, + }; + } + if (label !== undefined) { + labels.push(label(x)); } size++; } - return {seed, result: 'OK', tests: i, counterexample: null}; + return {seed, result: 'OK', tests: i, labels, counterexample: null}; }; } |
