Skip to content

Instantly share code, notes, and snippets.

@qlonik
Last active September 22, 2019 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qlonik/9a297285284d71f7da47022f120ef4ad to your computer and use it in GitHub Desktop.
Save qlonik/9a297285284d71f7da47022f120ef4ad to your computer and use it in GitHub Desktop.
JSVerify connector for ava written in TS
// 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')
}
}
}
/* 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>
}
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) => {
}
)
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