Last active
September 22, 2019 14:24
-
-
Save qlonik/9a297285284d71f7da47022f120ef4ad to your computer and use it in GitHub Desktop.
JSVerify connector for ava written in TS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given prettyPrintResult, isArbitraryLike and TETS_PRINT_LIMIT as defined in `property-test.ts` exist, | |
// Here are two variants tha could be used from JS. I tried typing them to be used in TS, but | |
// it is very hard to type these functions. | |
export async function jsc_macro_1(t, ...args) { | |
const logsMap = new Map() | |
const defaultOpts = { quiet: true } | |
const passedOpts = typeof args[0] !== 'function' ? args.shift() : {} | |
const opts = Object.assign(defaultOpts, passedOpts) | |
const propertyFn = args.shift() | |
const arbitraries = args | |
const result = await jsc.check(jsc.forall(...arbitraries, async (...args) => { | |
const attempt = await t.try(propertyFn, ...args) | |
attempt.discard() | |
logsMap.set(attempt.title, attempt.logs) | |
return attempt.passed || attempt.errors[0] | |
}), opts) | |
if (typeof result === 'object') { | |
t.log(prettyPrintResult(result)) | |
const attempt = await t.try(propertyFn, ...result.counterexample) | |
return attempt.commit() | |
} else if (result === true) { | |
if ('tests' in opts && opts.tests && opts.tests <= TEST_PRINT_LIMIT) { | |
// print all logs if number of tests is specified to be small | |
for (let [title, logs] of logsMap) { | |
t.log(`Attempt title: ${title}`) | |
for (let logLine of logs) { | |
t.log(logLine) | |
} | |
t.log('') | |
} | |
} else { | |
// print randomly selected log | |
const logs = [...logsMap.values()] | |
const rnd = jsc.random(0, logs.length - 1) | |
for (let logLine of logs[rnd]) { | |
t.log(logLine) | |
} | |
} | |
} else { | |
t.fail('Unknown result returned from JSVerify') | |
} | |
} | |
export function jsc_macro_2(...args) { | |
const defaultOpts = { quiet: true } | |
const passedOpts = !isArbitraryLike(args[0]) ? args.shift() : {} | |
const opts = Object.assign(defaultOpts, passedOpts) | |
return async (t, propertyFn) => { | |
const logsMap = new Map() | |
const arbitraries = args | |
const result = await jsc.check(jsc.forall(...arbitraries, async (...args) => { | |
const attempt = await t.try(propertyFn, ...args) | |
attempt.discard() | |
logsMap.set(attempt.title, attempt.logs) | |
return attempt.passed || attempt.errors[0] | |
}), opts) | |
if (typeof result === 'object') { | |
t.log(prettyPrintResult(result)) | |
const attempt = await t.try(propertyFn, ...result.counterexample) | |
return attempt.commit() | |
} else if (result === true) { | |
if ('tests' in opts && opts.tests && opts.tests <= TEST_PRINT_LIMIT) { | |
// print all logs if number of tests is specified to be small | |
for (let [title, logs] of logsMap) { | |
t.log(`Attempt title: ${title}`) | |
for (let logLine of logs) { | |
t.log(logLine) | |
} | |
t.log('') | |
} | |
} else { | |
// print randomly selected log | |
const logs = [...logsMap.values()] | |
const rnd = jsc.random(0, logs.length - 1) | |
for (let logLine of logs[rnd]) { | |
t.log(logLine) | |
} | |
} | |
} else { | |
t.fail('Unknown result returned from JSVerify') | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable ava/use-test, no-unused-vars */ | |
import { ExecutionContext, Implementation, ImplementationResult, Macro, UntitledMacro } from 'ava' | |
import { stripIndent } from 'common-tags' | |
import jsc, { Arbitrary, ArbitraryLike, Options, Result } from 'jsverify' | |
import { inspect } from 'util' | |
const TEST_PRINT_LIMIT = 3 | |
const isArbitraryLike = (o: unknown): o is ArbitraryLike<unknown> => { | |
return 'generator' in o | |
} | |
const prettyPrintResult = (r: Result<unknown>): string => { | |
const rngState = inspect(r.rngState, { colors: true }) | |
const tests = inspect(r.tests, { colors: true }) | |
const shrinks = inspect(r.shrinks, { colors: true }) | |
const inspectedCounterEx = inspect(r.counterexample, { depth: null, colors: true }) | |
const counterEx = inspectedCounterEx.includes('\n') | |
? '\n ' + | |
inspectedCounterEx | |
.slice(2, -2) | |
.split('\n') | |
.map((s) => ' ' + s) | |
.join('\n') + | |
'\n ' | |
: inspectedCounterEx | |
return stripIndent` | |
{ rngState: ${rngState} }, | |
Tests: ${tests} | |
Shrinks: ${shrinks} | |
Fails with: ${counterEx} | |
` | |
} | |
export const check: CheckFn = function check(...args: unknown[]): Implementation { | |
const defaultOpts = { quiet: true } | |
const passedOpts = !isArbitraryLike(args[0]) ? args.shift() : {} | |
const opts = Object.assign(defaultOpts, passedOpts) as Options | |
const propertyFn = args.pop() as UntitledMacro<unknown[]> | |
const arbitraries = args as Arbitrary<unknown>[] | |
return async function jsc$test(test) { | |
const logsMap = new Map<string, string[]>() | |
const prop = jsc.forall(...arbitraries, async (...args: unknown[]) => { | |
const attempt = await test.try(propertyFn, ...args) | |
attempt.discard() | |
logsMap.set(attempt.title, attempt.logs) | |
return attempt.passed || attempt.errors[0] | |
}) | |
const result = await jsc.check(prop, opts) | |
if (typeof result === 'object') { | |
test.log(prettyPrintResult(result)) | |
const attempt = await test.try(propertyFn, ...result.counterexample) | |
return attempt.commit() | |
} else if (result === true) { | |
if ('tests' in opts && opts.tests && opts.tests <= TEST_PRINT_LIMIT) { | |
// print all logs if number of tests is specified to be small | |
for (let [title, logs] of logsMap) { | |
test.log(`Attempt title: ${title}`) | |
for (let logLine of logs) { | |
test.log(logLine) | |
} | |
test.log('') | |
} | |
} else { | |
// print randomly selected log | |
const logs = [...logsMap.values()] | |
const rnd = jsc.random(0, logs.length - 1) | |
for (let logLine of logs[rnd]) { | |
test.log(logLine) | |
} | |
} | |
} else { | |
test.fail('Unknown result returned from JSVerify') | |
} | |
} | |
} | |
// prettier-ignore | |
export interface CheckFn { | |
<A, T>(a: Arbitrary<A>, prop: (t: ExecutionContext<T>, a: A) => ImplementationResult): Implementation<T> | |
<A, T>(opts: Options, a: Arbitrary<A>, prop: (t: ExecutionContext<T>, a: A) => ImplementationResult): Implementation<T> | |
<A, B, T>(a: Arbitrary<A>, b: Arbitrary<B>, prop: (t: ExecutionContext<T>, a: A, b: B) => ImplementationResult): Implementation<T> | |
<A, B, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, prop: (t: ExecutionContext<T>, a: A, b: B) => ImplementationResult): Implementation<T> | |
<A, B, C, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C) => ImplementationResult): Implementation<T> | |
<A, B, C, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C) => ImplementationResult): Implementation<T> | |
<A, B, C, D, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D) => ImplementationResult): Implementation<T> | |
<A, B, C, D, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, I, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, i: Arbitrary<I>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, I, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, i: Arbitrary<I>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, I, J, T>(a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, i: Arbitrary<I>, j: Arbitrary<J>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => ImplementationResult): Implementation<T> | |
<A, B, C, D, E, F, G, H, I, J, T>(opts: Options, a: Arbitrary<A>, b: Arbitrary<B>, c: Arbitrary<C>, d: Arbitrary<D>, e: Arbitrary<E>, f: Arbitrary<F>, g: Arbitrary<G>, h: Arbitrary<H>, i: Arbitrary<I>, j: Arbitrary<J>, prop: (t: ExecutionContext<T>, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => ImplementationResult): Implementation<T> | |
<T>(/* options?, ...arbitrary, propertyFn */ ...args: any[]): Implementation<T> | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import test from 'ava' | |
import jsc from 'jsverify' | |
import { jsc_macro_1, jsc_macro_2 } from './jsc-prop-connector' | |
test( | |
'jsc_macro_1 first test', | |
jsc_macro_1, | |
{quiet: true}, | |
(t, a, b) => { | |
t.pass() | |
}, | |
jsc.nat, | |
jsc.nat | |
) | |
test( | |
'jsc_macro_1 second test', | |
jsc_macro_1, | |
(t, a, b) => { | |
t.pass() | |
}, | |
jsc.string, | |
jsc.string | |
) | |
test( | |
'jsc_macro_2 first test', | |
jsc_macro_2({quiet: true}, jsc.nat, jsc.number), | |
(t, a, b) => { | |
} | |
) | |
test( | |
'jsc_macro_2 second test', | |
jsc_macro_2(jsc.string, jsc.number), | |
(t, a, b) => { | |
} | |
) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import test from 'ava' | |
import jsc from 'jsverify' | |
import { check } from './property-test' | |
test( | |
'addition is commutative', | |
check(jsc.nat, jsc.nat, (t, x, y) => { | |
t.is(x + y, y + x) | |
}), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment