1 | # JSVerify
|
2 |
|
3 | <img src="https://raw.githubusercontent.com/jsverify/jsverify/master/jsverify-300.png" align="right" height="100" />
|
4 |
|
5 | > Property-based checking. Like QuickCheck.
|
6 |
|
7 | [![Build Status](https://secure.travis-ci.org/jsverify/jsverify.svg?branch=master)](http://travis-ci.org/jsverify/jsverify)
|
8 | [![NPM version](https://badge.fury.io/js/jsverify.svg)](http://badge.fury.io/js/jsverify)
|
9 | [![Dependency Status](https://david-dm.org/jsverify/jsverify.svg)](https://david-dm.org/jsverify/jsverify)
|
10 | [![devDependency Status](https://david-dm.org/jsverify/jsverify/dev-status.svg)](https://david-dm.org/jsverify/jsverify#info=devDependencies)
|
11 | [![Code Climate](https://img.shields.io/codeclimate/github/jsverify/jsverify.svg)](https://codeclimate.com/github/jsverify/jsverify)
|
12 |
|
13 | ## Getting Started
|
14 |
|
15 | Install the module with: `npm install jsverify`
|
16 |
|
17 | ## Synopsis
|
18 |
|
19 | ```js
|
20 | var jsc = require("jsverify");
|
21 |
|
22 | // forall (f : bool -> bool) (b : bool), f (f (f b)) = f(b).
|
23 | var boolFnAppliedThrice =
|
24 | jsc.forall("bool -> bool", "bool", function (f, b) {
|
25 | return f(f(f(b))) === f(b);
|
26 | });
|
27 |
|
28 | jsc.assert(boolFnAppliedThrice);
|
29 | // OK, passed 100 tests
|
30 | ```
|
31 |
|
32 | ## Documentation
|
33 |
|
34 | ### Usage with [mocha](http://mochajs.org/)
|
35 |
|
36 | Using jsverify with mocha is easy, just define the properties and use `jsverify.assert`.
|
37 |
|
38 | Starting from version 0.4.3 you can write your specs without any boilerplate:
|
39 |
|
40 | ```js
|
41 | describe("sort", function () {
|
42 | jsc.property("idempotent", "array nat", function (arr) {
|
43 | return _.isEqual(sort(sort(arr)), sort(arr));
|
44 | });
|
45 | });
|
46 | ```
|
47 |
|
48 | Starting from version 0.8.0 you can write the specs in TypeScript. There are
|
49 | typings provided. The drawback is that you cannot use type DSL:
|
50 |
|
51 | ```typescript
|
52 | describe("basic jsverify usage", () => {
|
53 | jsc.property("(b && b) === b", jsc.bool, b => (b && b) === b);
|
54 |
|
55 | jsc.property("boolean fn thrice", jsc.fn(jsc.bool), jsc.bool, (f, b) =>
|
56 | f(f(f(b))) === f(b)
|
57 | );
|
58 | });
|
59 | ```
|
60 |
|
61 | You can also provide `--jsverifyRngState state` command line argument, to run tests with particular random generator state.
|
62 |
|
63 | ```
|
64 | $ mocha examples/nat.js
|
65 |
|
66 | 1) natural numbers are less than 90:
|
67 | Error: Failed after 49 tests and 1 shrinks. rngState: 074e9b5f037a8c21d6; Counterexample: 90;
|
68 |
|
69 | $ mocha examples/nat.js --grep 'are less than' --jsverifyRngState 074e9b5f037a8c21d6
|
70 |
|
71 | 1) natural numbers are less than 90:
|
72 | Error: Failed after 1 tests and 1 shrinks. rngState: 074e9b5f037a8c21d6; Counterexample: 90;
|
73 | ```
|
74 |
|
75 | Erroneous case is found with first try.
|
76 |
|
77 | ### Usage with [jasmine](https://jasmine.github.io/)
|
78 |
|
79 | Check [jasmineHelpers.js](helpers/jasmineHelpers.js) and [jasmineHelpers2.js](helpers/jasmineHelpers2.js) for jasmine 1.3 and 2.0 respectively.
|
80 |
|
81 | ## API Reference
|
82 |
|
83 | > _Testing shows the presence, not the absence of bugs._
|
84 | >
|
85 | > Edsger W. Dijkstra
|
86 |
|
87 | To show that propositions hold, we need to construct proofs.
|
88 | There are two extremes: proof by example (unit tests) and formal (machine-checked) proof.
|
89 | Property-based testing is somewhere in between.
|
90 | We formulate propositions, invariants or other properties we believe to hold, but
|
91 | only test it to hold for numerous (randomly generated) values.
|
92 |
|
93 | Types and function signatures are written in [Coq](http://coq.inria.fr/)/[Haskell](http://www.haskell.org/haskellwiki/Haskell)-influenced style:
|
94 | C# -style `List<T> filter(List<T> v, Func<T, bool> predicate)` is represented by
|
95 | `filter(v: array T, predicate: T -> bool): array T` in our style.
|
96 |
|
97 | Methods and objects live in `jsc` object, e.g. `shrink.bless` method is used by
|
98 | ```js
|
99 | var jsc = require("jsverify");
|
100 | var foo = jsc.shrink.bless(...);
|
101 | ```
|
102 |
|
103 | Methods starting with `.dot` are prototype methods:
|
104 | ```js
|
105 | var arb = jsc.nat;
|
106 | var arb2 = jsc.nat.smap(f, g);
|
107 | ```
|
108 |
|
109 | `jsverify` can operate with both synchronous and asynchronous-promise properties.
|
110 | Generally every property can be wrapped inside [functor](http://learnyouahaskell.com/functors-applicative-functors-and-monoids),
|
111 | for now in either identity or promise functor, for synchronous and promise properties respectively.
|
112 |
|
113 | ### Properties
|
114 |
|
115 | - `forall(arbs: arbitrary a ..., userenv: (map arbitrary)?, prop : a -> property): property`
|
116 |
|
117 | Property constructor
|
118 |
|
119 | - `check (prop: property, opts: checkoptions?): result`
|
120 |
|
121 | Run random checks for given `prop`. If `prop` is promise based, result is also wrapped in promise.
|
122 |
|
123 | Options:
|
124 | - `opts.tests` - test count to run, default 100
|
125 | - `opts.size` - maximum size of generated values, default 50
|
126 | - `opts.quiet` - do not `console.log`
|
127 | - `opts.rngState` - state string for the rng
|
128 |
|
129 | The `result` is `true` if check succeeds, otherwise it's an object with various fields:
|
130 | - `counterexample` - an input for which property fails.
|
131 | - `tests` - number of tests run before failing case is found
|
132 | - `shrinks` - number of shrinks performed
|
133 | - `exc` - an optional exception thrown by property function
|
134 | - `rngState` - random number generator's state before execution of the property
|
135 |
|
136 | - `assert(prop: property, opts: checkoptions?) : void`
|
137 |
|
138 | Same as `check`, but throw exception if property doesn't hold.
|
139 |
|
140 | - `property(name: string, ...)`
|
141 |
|
142 | Assuming there is globally defined `it`, the same as:
|
143 |
|
144 | ```js
|
145 | it(name, function () {
|
146 | jsc.assert(jsc.forall(...));
|
147 | }
|
148 | ```
|
149 |
|
150 | You can use `property` to write facts too:
|
151 | ```js
|
152 | jsc.property("+0 === -0", function () {
|
153 | return +0 === -0;
|
154 | });
|
155 | ```
|
156 |
|
157 | - `compile(desc: string, env: typeEnv?): arbitrary a`
|
158 |
|
159 | Compile the type description in provided type environment, or default one.
|
160 |
|
161 | - `sampler(arb: arbitrary a, genSize: nat = 10): (sampleSize: nat?) -> a`
|
162 |
|
163 | Create a sampler for a given arbitrary with an optional size. Handy when used in
|
164 | a REPL:
|
165 | ```
|
166 | > jsc = require('jsverify') // or require('./lib/jsverify') w/in the project
|
167 | ...
|
168 | > jsonSampler = jsc.sampler(jsc.json, 4)
|
169 | [Function]
|
170 | > jsonSampler()
|
171 | 0.08467432763427496
|
172 | > jsonSampler()
|
173 | [ [ [] ] ]
|
174 | > jsonSampler()
|
175 | ''
|
176 | > sampledJson(2)
|
177 | [-0.4199344692751765, false]
|
178 | ```
|
179 |
|
180 | - `throws(block: () -> a, error: class?, message: string?): bool`
|
181 |
|
182 | Executes nullary function `block`. Returns `true` if `block` throws. See [assert.throws](https://nodejs.org/api/assert.html#assert_assert_throws_block_error_message)
|
183 |
|
184 | - `assertForall(arbs: arbitrary a ..., userenv: (map arbitrary)?, prop : a -> property): void`
|
185 |
|
186 | Combines 'assert' and 'forall'.
|
187 | Constructs a property with forall from arguments, then throws an exception if the property doesn't hold.
|
188 | Options for 'assert' cannot be set here - use assert(forall(...)) if you need that.
|
189 |
|
190 | - `checkForall(arbs: arbitrary a ..., userenv: (map arbitrary)?, prop : a -> property): result`
|
191 |
|
192 | Combines 'check' and 'forall'.
|
193 | Constructs a property with forall from arguments, and returns a value based on if the property holds or not.
|
194 | See 'check' for description of return value.
|
195 |
|
196 | Options for 'check' cannot be set here - use check(forall(...)) if you need that.
|
197 |
|
198 | ### Types
|
199 |
|
200 | - `generator a` is a function `(size: nat) -> a`.
|
201 | - `show` is a function `a -> string`.
|
202 | - `shrink` is a function `a -> [a]`, returning *smaller* values.
|
203 | - `arbitrary a` is a triple of generator, shrink and show functions.
|
204 | - `{ generator: nat -> a, shrink : a -> array a, show: a -> string }`
|
205 |
|
206 | ### Blessing
|
207 |
|
208 | We chose to represent generators and shrinks by functions, yet we would
|
209 | like to have additional methods on them. Thus we *bless* objects with
|
210 | additional properties.
|
211 |
|
212 | Usually you don't need to bless anything explicitly, as all combinators
|
213 | return blessed values.
|
214 |
|
215 | See [perldoc for bless](http://perldoc.perl.org/functions/bless.html).
|
216 |
|
217 | ### DSL for input parameters
|
218 |
|
219 | There is a small DSL to help with `forall`. For example the two definitions below are equivalent:
|
220 | ```js
|
221 | var bool_fn_applied_thrice = jsc.forall("bool -> bool", "bool", check);
|
222 | var bool_fn_applied_thrice = jsc.forall(jsc.fn(jsc.bool), jsc.bool, check);
|
223 | ```
|
224 |
|
225 | The DSL is based on a subset of language recognized by [typify-parser](https://github.com/phadej/typify-parser):
|
226 | - *identifiers* are fetched from the predefined environment.
|
227 | - *applications* are applied as one could expect: `"array bool"` is evaluated to `jsc.array(jsc.bool)`.
|
228 | - *functions* are supported: `"bool -> bool"` is evaluated to `jsc.fn(jsc.bool)`.
|
229 | - *square brackets* are treated as a shorthand for the array type: `"[nat]"` is evaluated to `jsc.array(jsc.nat)`.
|
230 | - *union*: `"bool | nat"` is evaluated to `jsc.sum([jsc.bool, jsc.nat])`.
|
231 | - **Note** `oneof` cannot be shrinked, because the union is untagged, we don't know which shrink to use.
|
232 | - *conjunction*: `"bool & nat"` is evaluated to `jsc.tuple(jsc.bool, jsc.nat)`.
|
233 | - *anonymous records*: `"{ b: bool; n: nat }"` is evaluated to `jsc.record({ b: jsc.bool, n: jsc.nat })`.
|
234 | - *EXPERIMENTAL: recursive types*: `"rec list -> unit | (nat & list)"`.
|
235 |
|
236 | ### Arbitrary data
|
237 |
|
238 | - `.smap(f: a -> b, g: b -> a, newShow: (b -> string)?): arbitrary b`
|
239 |
|
240 | Transform `arbitrary a` into `arbitrary b`. For example:
|
241 |
|
242 | `g` should be a [right inverse](http://en.wikipedia.org/wiki/Surjective_function#Surjections_as_right_invertible_functions) of `f`, but doesn't need to be complete inverse.
|
243 | i.e. i.e. `f` doesn't need to be invertible, only surjective.
|
244 |
|
245 | ```js
|
246 | var positiveIntegersArb = nat.smap(
|
247 | function (x) { return x + 1; },
|
248 | function (x) { return x - 1; });
|
249 | ```
|
250 |
|
251 | ```js
|
252 | var setNatArb = jsc.array(jsc.nat).smap(_.uniq, _.identity);
|
253 | ```
|
254 |
|
255 | Right inverse means that *f(g(y)) = y* for all *y* in *Y*. Here *Y* is a type of **arrays of unique natural numbers**. For them
|
256 | ```js
|
257 | _.uniq(_.identity(y)) = _.uniq(y) = y
|
258 | ```
|
259 |
|
260 | Opposite: *g(f(x))* for all *x* in *X*, doesn't need to hold. *X* is **arrays of natural numbers**:
|
261 | ```js
|
262 | _.identity(_uniq([0, 0])) = [0]] != [0, 0]
|
263 | ```
|
264 |
|
265 | We need an inverse for shrinking, and there right inverse is enough. We can always *pull back* `smap`ped value, shrink the preimage, and *map* or *push forward* shrunken preimages again.
|
266 |
|
267 | - `bless(arb: {...}): arbitrary a`
|
268 |
|
269 | Bless almost arbitrary structure to be proper arbitrary. *Note*: this function mutates argument.
|
270 |
|
271 | #### Example:
|
272 |
|
273 | ```js
|
274 | var arbTokens = jsc.bless({
|
275 | generator: function () {
|
276 | switch (jsc.random(0, 2)) {
|
277 | case 0: return "foo";
|
278 | case 1: return "bar";
|
279 | case 2: return "quux";
|
280 | }
|
281 | }
|
282 | });
|
283 | ```
|
284 |
|
285 | ### Primitive arbitraries
|
286 |
|
287 | - `integer: arbitrary integer`
|
288 | - `integer(maxsize: nat): arbitrary integer`
|
289 | - `integer(minsize: integer, maxsize: integer): arbitrary integer`
|
290 |
|
291 | Integers, ℤ
|
292 |
|
293 | - `nat: arbitrary nat`
|
294 | - `nat(maxsize: nat): arbitrary nat`
|
295 |
|
296 | Natural numbers, ℕ (0, 1, 2...)
|
297 |
|
298 | - `number: arbitrary number`
|
299 | - `number(maxsize: number): arbitrary number`
|
300 | - `number(min: number, max: number): arbitrary number`
|
301 |
|
302 | JavaScript numbers, "doubles", ℝ. `NaN` and `Infinity` are not included.
|
303 |
|
304 | - `uint8: arbitrary nat`
|
305 | - `uint16: arbitrary nat`
|
306 | - `uint32: arbitrary nat`
|
307 |
|
308 | - `int8: arbitrary integer`
|
309 | - `int16: arbitrary integer`
|
310 | - `int32: arbitrary integer`
|
311 |
|
312 | - `bool: arbitrary bool`
|
313 |
|
314 | Booleans, `true` or `false`.
|
315 |
|
316 | - `datetime: arbitrary datetime`
|
317 |
|
318 | Random datetime
|
319 |
|
320 | - `elements(args: array a): arbitrary a`
|
321 |
|
322 | Random element of `args` array.
|
323 |
|
324 | - `falsy: arbitrary *`
|
325 |
|
326 | Generates falsy values: `false`, `null`, `undefined`, `""`, `0`, and `NaN`.
|
327 |
|
328 | - `constant(x: a): arbitrary a`
|
329 |
|
330 | Returns an unshrinkable arbitrary that yields the given object.
|
331 |
|
332 | ### Arbitrary combinators
|
333 |
|
334 | - `nonshrink(arb: arbitrary a): arbitrary a`
|
335 |
|
336 | Non shrinkable version of arbitrary `arb`.
|
337 |
|
338 | - `unit: arbitrary ()`
|
339 |
|
340 | - `either(arbA: arbitrary a, arbB : arbitrary b): arbitrary (either a b)`
|
341 |
|
342 | - `pair(arbA: arbitrary a, arbB : arbitrary b): arbitrary (pair a b)`
|
343 |
|
344 | If not specified `a` and `b` are equal to `value()`.
|
345 |
|
346 | - `tuple(arbs: (arbitrary a, arbitrary b...)): arbitrary (a, b...)`
|
347 |
|
348 | - `sum(arbs: (arbitrary a, arbitrary b...)): arbitrary (a | b ...)`
|
349 |
|
350 | - `dict(arb: arbitrary a): arbitrary (dict a)`
|
351 |
|
352 | Generates a JavaScript object with properties of type `A`.
|
353 |
|
354 | - `array(arb: arbitrary a): arbitrary (array a)`
|
355 |
|
356 | - `nearray(arb: arbitrary a): arbitrary (array a)`
|
357 |
|
358 | - `json: arbitrary json`
|
359 |
|
360 | JavaScript Objects: boolean, number, string, null, array of `json` values or object with `json` values.
|
361 |
|
362 | - `oneof(gs : array (arbitrary a)...) : arbitrary a`
|
363 |
|
364 | Randomly uses one of the given arbitraries.
|
365 |
|
366 | - ```js
|
367 | letrec(
|
368 | (tie: key -> (arbitrary a | arbitrary b | ...))
|
369 | -> { key: arbitrary a, key: arbitrary b, ... }):
|
370 | { key: arbitrary a, key: arbitrary b, ... }
|
371 | ```
|
372 |
|
373 | Mutually recursive definitions. Every reference to a sibling arbitrary
|
374 | should go through the `tie` function.
|
375 |
|
376 | ```js
|
377 | { arb1, arb2 } = jsc.letrec(function (tie) {
|
378 | return {
|
379 | arb1: jsc.tuple(jsc.int, jsc.oneof(jsc.const(null), tie("arb2"))),
|
380 | arb2: jsc.tuple(jsc.bool, jsc.oneof(jsc.const(null), tie("arb1"))),
|
381 | }
|
382 | });
|
383 | ```
|
384 |
|
385 | ### Arbitrary records
|
386 |
|
387 | - `record(spec: { key: arbitrary a... }, userenv: env?): arbitrary { key: a... }`
|
388 |
|
389 | Generates a javascript object with given record spec.
|
390 |
|
391 | - `generator.record(gen: { key: generator a... }): generator { key: a... }`
|
392 |
|
393 | - `shrink.record(shrs: { key: shrink a... }): shrink { key: a... }`
|
394 |
|
395 | ### Arbitrary strings
|
396 |
|
397 | - `char: arbitrary char` — Single character
|
398 |
|
399 | - `asciichar: arbitrary char` — Single ascii character (0x20-0x7e inclusive, no DEL)
|
400 |
|
401 | - `string: arbitrary string`
|
402 |
|
403 | - `nestring: arbitrary string` — Generates strings which are not empty.
|
404 |
|
405 | - `asciistring: arbitrary string`
|
406 |
|
407 | - `asciinestring: arbitrary string`
|
408 |
|
409 | ### Arbitrary functions
|
410 |
|
411 | - `fn(arb: arbitrary a): arbitrary (b -> a)`
|
412 | - `fun(arb: arbitrary a): arbitrary (b -> a)`
|
413 |
|
414 | ### Small arbitraries
|
415 |
|
416 | - `generator.small(gen: generator a): generator a`
|
417 | - `small(arb: arbitrary a): arbitrary a`
|
418 |
|
419 | Create a generator (abitrary) which will generate smaller values, i.e. generator's `size` parameter is decreased logarithmically.
|
420 |
|
421 | ```js
|
422 | jsc.property("small array of small natural numbers", "small (array nat)", function (arr) {
|
423 | return Array.isArray(arr);
|
424 | });
|
425 |
|
426 | jsc.property("small array of normal natural numbers", "(small array) nat", function (arr) {
|
427 | return Array.isArray(arr);
|
428 | });
|
429 | ```
|
430 |
|
431 | ### Generator functions
|
432 |
|
433 | A generator function, `generator a`, is a function `(size: nat) -> a`, which generates a value of given size.
|
434 |
|
435 | Generator combinators are auto-curried:
|
436 |
|
437 | ```js
|
438 | var xs = jsc.generator.array(jsc.nat.generator, 1); // ≡
|
439 | var ys = jsc.generator.array(jsc.nat.generator)(1);
|
440 | ```
|
441 |
|
442 | In purely functional approach `generator a` would be explicitly stateful computation:
|
443 | `(size: nat, rng: randomstate) -> (a, randomstate)`.
|
444 | *JSVerify* uses an implicit random number generator state,
|
445 | but the value generation is deterministic (tests are reproducible),
|
446 | if the primitives from *random* module are used.
|
447 |
|
448 | - `generator.bless(f: nat -> a): generator a`
|
449 |
|
450 | Bless function with `.map` and `.flatmap` properties.
|
451 |
|
452 | - `.map(f: a -> b): generator b`
|
453 |
|
454 | Map `generator a` into `generator b`. For example:
|
455 |
|
456 | ```js
|
457 | positiveIntegersGenerator = nat.generator.map(
|
458 | function (x) { return x + 1; });
|
459 | ```
|
460 |
|
461 | - `.flatmap(f: a -> generator b): generator b`
|
462 |
|
463 | Monadic bind for generators. Also `flatMap` version is supported.
|
464 |
|
465 | - `generator.constant(x: a): generator a`
|
466 |
|
467 | - `generator.combine(gen: generator a..., f: a... -> b): generator b`
|
468 |
|
469 | - `generator.oneof(gens: list (generator a)): generator a`
|
470 |
|
471 | - `generator.recursive(genZ: generator a, genS: generator a -> generator a): generator a`
|
472 |
|
473 | - `generator.pair(genA: generator a, genB: generator b): generator (a, b)`
|
474 |
|
475 | - `generator.either(genA: generator a, genB: generator b): generator (either a b)`
|
476 |
|
477 | - `generator.unit: generator ()`
|
478 |
|
479 | `unit` is an empty tuple, i.e. empty array in JavaScript representation. This is useful as a building block.
|
480 |
|
481 | - `generator.tuple(gens: (generator a, generator b...)): generator (a, b...)`
|
482 |
|
483 | - `generator.sum(gens: (generator a, generator b...)): generator (a | b...)`
|
484 |
|
485 | - `generator.array(gen: generator a): generator (array a)`
|
486 |
|
487 | - `generator.nearray(gen: generator a): generator (array a)`
|
488 |
|
489 | - `generator.dict(gen: generator a): generator (dict a)`
|
490 |
|
491 | ### Shrink functions
|
492 |
|
493 | A shrink function, `shrink a`, is a function `a -> [a]`, returning an array of *smaller* values.
|
494 |
|
495 | Shrink combinators are auto-curried:
|
496 |
|
497 | ```js
|
498 | var xs = jsc.shrink.array(jsc.nat.shrink, [1]); // ≡
|
499 | var ys = jsc.shrink.array(jsc.nat.shrink)([1]);
|
500 | ```
|
501 |
|
502 | - `shrink.bless(f: a -> [a]): shrink a`
|
503 |
|
504 | Bless function with `.smap` property.
|
505 |
|
506 | - `.smap(f: a -> b, g: b -> a): shrink b`
|
507 |
|
508 | Transform `shrink a` into `shrink b`. For example:
|
509 |
|
510 | ```js
|
511 | positiveIntegersShrink = nat.shrink.smap(
|
512 | function (x) { return x + 1; },
|
513 | function (x) { return x - 1; });
|
514 | ```
|
515 |
|
516 | - `shrink.noop: shrink a`
|
517 |
|
518 | - `shrink.pair(shrA: shrink a, shrB: shrink b): shrink (a, b)`
|
519 |
|
520 | - `shrink.either(shrA: shrink a, shrB: shrink b): shrink (either a b)`
|
521 |
|
522 | - `shrink.tuple(shrs: (shrink a, shrink b...)): shrink (a, b...)`
|
523 |
|
524 | - `shrink.sum(shrs: (shrink a, shrink b...)): shrink (a | b...)`
|
525 |
|
526 | - `shrink.array(shr: shrink a): shrink (array a)`
|
527 |
|
528 | - `shrink.nearray(shr: shrink a): shrink (nearray a)`
|
529 |
|
530 | ### Show functions
|
531 |
|
532 | - `show.def(x : a): string`
|
533 |
|
534 | Currently implemented as `JSON.stringify`.
|
535 |
|
536 | - `show.pair(showA: a -> string, showB: b -> string, x: (a, b)): string`
|
537 |
|
538 | - `show.either(showA: a -> string, showB: b -> string, e: either a b): string`
|
539 |
|
540 | - `show.tuple(shrinks: (a -> string, b -> string...), x: (a, b...)): string`
|
541 |
|
542 | - `show.sum(shrinks: (a -> string, b -> string...), x: (a | b ...)): string`
|
543 |
|
544 | - `show.array(shrink: a -> string, x: array a): string`
|
545 |
|
546 | ### Random functions
|
547 |
|
548 | - `random(min: int, max: int): int`
|
549 |
|
550 | Returns random int from `[min, max]` range inclusively.
|
551 |
|
552 | ```js
|
553 | getRandomInt(2, 3) // either 2 or 3
|
554 | ```
|
555 |
|
556 | - `random.number(min: number, max: number): number`
|
557 |
|
558 | Returns random number from `[min, max)` range.
|
559 |
|
560 | ### either
|
561 |
|
562 | - `either.left(value: a): either a b`
|
563 |
|
564 | - `either.right(value: b): either a b`
|
565 |
|
566 | - `either.either(l: a -> x, r: b -> x): x`
|
567 |
|
568 | - `either.isEqual(other: either a b): bool`
|
569 |
|
570 | TODO: add `eq` optional parameter
|
571 |
|
572 | - `either.bimap(f: a -> c, g: b -> d): either c d`
|
573 |
|
574 | ```js
|
575 | either.bimap(compose(f, g), compose(h, i)) ≡ either.bimap(g, i).bimap(f, h);
|
576 | ```
|
577 |
|
578 | - `either.first(f: a -> c): either c b`
|
579 |
|
580 | ```js
|
581 | either.first(f) ≡ either.bimap(f, utils.identity)
|
582 | ```
|
583 |
|
584 | - `either.second(g: b -> d): either a d`
|
585 |
|
586 | ```js
|
587 | either.second(g) === either.bimap(utils.identity, g)
|
588 | ```
|
589 |
|
590 | ### Utility functions
|
591 |
|
592 | Utility functions are exposed (and documented) only to make contributions to jsverify more easy.
|
593 | The changes here don't follow semver, i.e. there might be backward-incompatible changes even in patch releases.
|
594 |
|
595 | Use [underscore.js](http://underscorejs.org/), [lodash](https://lodash.com/), [ramda](http://ramda.github.io/ramdocs/docs/), [lazy.js](http://danieltao.com/lazy.js/) or some other utility belt.
|
596 |
|
597 | - `utils.isEqual(x: json, y: json): bool`
|
598 |
|
599 | Equality test for `json` objects.
|
600 |
|
601 | - `utils.isApproxEqual(x: a, y: b, opts: obj): bool`
|
602 |
|
603 | Tests whether two objects are approximately and optimistically equal.
|
604 | Returns `false` only if they are distinguishable not equal.
|
605 | Returns `true` when `x` and `y` are `NaN`.
|
606 | This function works with cyclic data.
|
607 |
|
608 | Takes optional 'opts' parameter with properties:
|
609 |
|
610 | - `fnEqual` - whether all functions are considered equal (default: yes)
|
611 | - `depth` - how deep to recurse until treating as equal (default: 5)
|
612 |
|
613 | - `utils.force(x: a | () -> a) : a`
|
614 |
|
615 | Evaluate `x` as nullary function, if it is one.
|
616 |
|
617 | - `utils.merge(x... : obj): obj`
|
618 |
|
619 | Merge two objects, a bit like `_.extend({}, x, y)`.
|
620 |
|
621 | ## FAQ
|
622 |
|
623 | ### Why do all the examples import the library as jsc instead of jsv?
|
624 |
|
625 | Does JSC originate with [JSCheck](http://www.jscheck.org/)?
|
626 |
|
627 | **A:** Yes
|
628 |
|
629 | ### smap requires an inverse function, which isn't always practical. Is this complexity related to shrinking?
|
630 |
|
631 | **A:** Yes. We don't want to give an easy-to-use interface which forgets
|
632 | shrinking altogether. Note, that *right* inverse is enough, which is most
|
633 | likely easy to write, even *complete* inverse doesn't exist.
|
634 |
|
635 | ## Contributing
|
636 |
|
637 | - `README.md` is generated from the source with [ljs](https://github.com/phadej/ljs), say `make literate`.
|
638 | - `jsverify.standalone.js` is also generated by the build process
|
639 | - Before creating a pull request run `make test`, yet travis will do it for you.
|
640 |
|
641 | ## Release History
|
642 |
|
643 | - **0.8.0** — *2017-03-12* — TypeScript typings
|
644 | - Provide TypeScript typings
|
645 | [#202](https://github.com/jsverify/jsverify/pull/202)
|
646 | [#196](https://github.com/jsverify/jsverify/pull/196)
|
647 | - **0.7.5** — *2017-03-08* — International Women's Day
|
648 | - Add `letrec` combinator
|
649 | [#193](https://github.com/jsverify/jsverify/pull/193)
|
650 | - Add `null` to `json` arbitrary
|
651 | [#201](https://github.com/jsverify/jsverify/pull/201)
|
652 | - Fix typos and outdated links in documentation
|
653 | - **0.7.4** — *2016-09-07* — Bless `suchthat`
|
654 | - Fix "arbitraries created with `suchthat` are missing `.smap`"
|
655 | [#184](https://github.com/jsverify/jsverify/issues/184)
|
656 | - **0.7.3** — *2016-08-26* — Remove lodash
|
657 | - Fixed accidental use of `lodash`. We have our own `isNaN` now.
|
658 | - **0.7.2** — *2016-08-25* — One year since the last release
|
659 | - `jsc.utils.isEqual` returns true if both arguments are `NaN`.
|
660 | - Add `jsc.assertForall` and `jsc.checkForall`
|
661 | - **0.7.1** — *2015-08-24* — jsc.throws
|
662 | - Add `jsc.throws` [#133](https://github.com/jsverify/jsverify/pull/133)
|
663 | - **0.7.0** — *2015-08-23* — More experiments
|
664 | - `jsc.sum` - generate arbitrary sum types (generalisation of either) [#125](https://github.com/jsverify/jsverify/pull/125)
|
665 | - *BREAKING CHANGE:* bar (`|`) in DSL generates `jsc.sum`
|
666 | - experimental support of recursive types in DSL (especially no shrinking yet) [#109](https://github.com/jsverify/jsverify/issues/109) [#126](https://github.com/jsverify/jsverify/pull/126)
|
667 | - fail early when `jsc.forall` is given zero generators [#128](https://github.com/jsverify/jsverify/issues/128)
|
668 | - `jsc.json` has shrink [#122](https://github.com/jsverify/jsverify/issues/122)
|
669 | - non-true non-function results from properties are treated as exceptions [#127](https://github.com/jsverify/jsverify/issues/127)
|
670 | - **0.6.3** — *2015-07-27* — Bug fixes
|
671 | - `jsc.utils.isEqual` doesn't care about key ordering [#123](https://github.com/jsverify/jsverify/issues/123)
|
672 | - tuple's shrink is blessed [#124](https://github.com/jsverify/jsverify/issues/124)
|
673 | - **0.6.2** — *2015-07-13* — Trampolines
|
674 | - **0.6.1** — *2015-07-13* — Bug fixes
|
675 | - Print stacktrace of catched exceptions
|
676 | - `maxsize = 0` for numeric generators works
|
677 | - Issue with non-parametric jsc.property returning property.
|
678 | - **0.6.0** — *2015-06-19* — Minor but major release!
|
679 | - added `jsc.utils.isApproxEqual`
|
680 | - **0.6.0-beta.2** — *2015-05-31* — Beta!
|
681 | - Fix issue [#113](https://github.com/jsverify/jsverify/issues/113) - Shrink of tuple with arrays failed.
|
682 | - **0.6.0-beta.1** — *2015-05-04* — Beta!
|
683 | - FAQ section
|
684 | - Improved `smap` documentation
|
685 | - `flatmap` is also `flatMap`
|
686 | - Fix function arbitrary
|
687 | - `small` arbitraries
|
688 | - `jsc.generator.record`
|
689 | - Thanks to @peterjoel for reporting issues
|
690 | - **0.6.0-alpha.6** — *2015-04-25* — Fix issues #98
|
691 | - Documentation improvements
|
692 | - Fix issue [#98](https://github.com/jsverify/jsverify/issues/98) - error while generating `int32` values
|
693 | - **0.6.0-alpha.5** — *2015-04-23* — Fix issue #99
|
694 | - Documentation improvements
|
695 | - Fix issue #99 (`suchthat` shrink)
|
696 | - **0.6.0-alpha.4** — *2015-04-26* — Fix issue #87
|
697 | - jsc.property didn't fail with asynchronous properties
|
698 | - thanks @Ezku for reporting
|
699 | - **0.6.0-alpha.3** — *2015-04-24* — promise shrink fixed
|
700 | - **0.6.0-alpha.2** — *2015-04-24* — jsc.bless
|
701 | - Added `jsc.bless`
|
702 | - **0.6.0-alpha.1** — *2015-04-22* — Preview
|
703 | - Using lazy sequences for shrink results
|
704 | - *Breaking changes:*
|
705 | - `jsc.map` renamed to `jsc.dict`
|
706 | - `jsc.value` removed, use `jsc.json`
|
707 | - `jsc.string()` removed, use `jsc.string`
|
708 | - `shrink.isomap` renamed to `shrink.smap`
|
709 | - **0.5.3** — *2015-04-21* — More algebra
|
710 | - `unit` and `either` arbitraries
|
711 | - `arbitrary.smap` to help creating compound data
|
712 | - **0.5.2** — *2015-04-10* — `show.def` -change
|
713 | - **0.5.1** — *2015-02-19* — Dependencies bump
|
714 | - We also work on 0.12 and iojs!
|
715 | - **0.5.0** — *2014-12-24* — Merry Chrismas 2014!
|
716 | - Documentation cleanup
|
717 | - **0.5.0-beta.2** — *2014-12-21* — Beta 2!
|
718 | - Pair & tuple related code cleanup
|
719 | - Update `CONTRIBUTING.md`
|
720 | - Small documentation type fixes
|
721 | - Bless `jsc.elements` shrink
|
722 | - **0.5.0-beta.1** — *2014-12-20* — Beta!
|
723 | - `bless` don't close over (uses `this`)
|
724 | - Cleanup generator module
|
725 | - Other code cleanup here and there
|
726 | - **0.4.6** — *2014-11-30* — better shrinks & recursive
|
727 | - Implemented shrinks: [#51](https://github.com/jsverify/jsverify/issues/51)
|
728 | - `jsc.generator.recursive`: [#37](https://github.com/jsverify/jsverify/issues/37)
|
729 | - array, nearray & map generators return a bit smaller results (*log2* of size)
|
730 | - **0.4.5** — *2014-11-22* — stuff
|
731 | - `generator.combine` & `.flatmap`
|
732 | - `nat`, `integer`, `number` & and `string` act as objects too
|
733 | - **0.4.4** — *2014-11-22* — new generators
|
734 | - New generators: `nearray`, `nestring`
|
735 | - `generator.constant`
|
736 | - zero-ary `jsc.property` (it ∘ assert)
|
737 | - `jsc.sampler`
|
738 | - **0.4.3** — *2014-11-08* — jsc.property
|
739 | - Now you can write your bdd specs without any boilerplate
|
740 | - support for nat-litearls in dsl [#36](https://github.com/jsverify/jsverify/issues/36)
|
741 | ```js
|
742 | describe("Math.abs", function () {
|
743 | jsc.property("result is non-negative", "integer 100", function (x) {
|
744 | return Math.abs(x) >= 0;
|
745 | });
|
746 | });
|
747 | ```
|
748 | - Falsy generator [#42](https://github.com/jsverify/jsverify/issues/42)
|
749 | - **0.4.2** — *2014-11-03* — User environments for DSL
|
750 | - User environments for DSL
|
751 | - Generator prototype `map`, and shrink prototype `isomap`
|
752 | - JSON generator works with larger sizes
|
753 | - **0.4.1** Move to own organization in GitHub
|
754 | - **0.4.0** — *2014-10-27* — typify-dsl & more arbitraries.
|
755 | Changes from **0.3.6**:
|
756 | - DSL for `forall` and `suchthat`
|
757 | - new primitive arbitraries
|
758 | - `oneof` behaves as in QuickCheck (BREAKING CHANGE)
|
759 | - `elements` is new name of old `oneof`
|
760 | - Other smaller stuff under the hood
|
761 | - **0.4.0**-beta.4 generator.oneof
|
762 | - **0.4.0**-beta.3 Expose shrink and show modules
|
763 | - **0.4.0**-beta.2 Move everything around
|
764 | - Better looking README.md!
|
765 | - **0.4.0**-beta.1 Beta!
|
766 | - Dev Dependencies update
|
767 | - **0.4.0**-alpha8 oneof & record -dsl support
|
768 | - also `jsc.compile`
|
769 | - record is shrinkable!
|
770 | - **0.4.0**-alpha7 oneof & record
|
771 | - *oneof* and *record* generator combinators ([@fson](https://github.com/fson))
|
772 | - Fixed uint\* generators
|
773 | - Default test size increased to 10
|
774 | - Numeric generators with size specified are independent of test size ([#20](https://github.com/phadej/jsverify/issues/20))
|
775 | - **0.4.0**-alpha6 more primitives
|
776 | - int8, int16, int32, uint8, uint16, uint32
|
777 | - char, asciichar and asciistring
|
778 | - value → json
|
779 | - use eslint
|
780 | - **0.4.0**-alpha5 move david to be devDependency
|
781 | - **0.4.0**-alpha4 more typify
|
782 | - `suchchat` supports typify dsl
|
783 | - `oneof` → `elements` to be in line with QuickCheck
|
784 | - Added versions of examples using typify dsl
|
785 | - **0.4.0**-alpha3 David, npm-freeze and jscs
|
786 | - **0.4.0**-alpha2 Fix typo in readme
|
787 | - **0.4.0**-alpha1 typify
|
788 | - DSL for `forall`
|
789 | ```js
|
790 | var bool_fn_applied_thrice = jsc.forall("bool -> bool", "bool", check);
|
791 | ```
|
792 |
|
793 | - generator arguments, which are functions are evaluated. One can now write:
|
794 | ```js
|
795 | jsc.forall(jsc.nat, check) // previously had to be jsc.nat()
|
796 | ```
|
797 |
|
798 | - **0.3.6** map generator
|
799 | - **0.3.5** Fix forgotten rngState in console output
|
800 | - **0.3.4** Dependencies update
|
801 | - **0.3.3** Dependencies update
|
802 | - **0.3.2** `fun` → `fn`
|
803 | - **0.3.1** Documentation typo fixes
|
804 | - **0.3.0** Major changes
|
805 | - random generate state handling
|
806 | - `--jsverifyRngState` parameter value used when run on node
|
807 | - karma tests
|
808 | - use make
|
809 | - dependencies update
|
810 | - **0.2.0** Use browserify
|
811 | - **0.1.4** Mocha test suite
|
812 | - major cleanup
|
813 | - **0.1.3** gen.show and exception catching
|
814 | - **0.1.2** Added jsc.assert
|
815 | - **0.1.1** Use grunt-literate
|
816 | - **0.1.0** Usable library
|
817 | - **0.0.2** Documented preview
|
818 | - **0.0.1** Initial preview
|
819 |
|
820 | ## Related work
|
821 |
|
822 | ### JavaScript
|
823 |
|
824 | - [JSCheck](http://www.jscheck.org/)
|
825 | - [claire](https://npmjs.org/package/claire)
|
826 | - [gent](https://npmjs.org/package/gent)
|
827 | - [fatcheck](https://npmjs.org/package/fatcheck)
|
828 | - [quickcheck](https://npmjs.org/package/quickcheck)
|
829 | - [qc.js](https://bitbucket.org/darrint/qc.js/)
|
830 | - [quick\_check](https://www.npmjs.org/package/quick_check)
|
831 | - [gentest](https://github.com/graue/gentest)
|
832 | - [node-quickcheck](https://github.com/mcandre/node-quickcheck)
|
833 |
|
834 | ### Others
|
835 |
|
836 | - [Wikipedia - QuickCheck](http://en.wikipedia.org/wiki/QuickCheck)
|
837 | - [Haskell - QuickCheck](http://hackage.haskell.org/package/QuickCheck) [Introduction](http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck1)
|
838 | - [Erlang - QuviQ](http://www.quviq.com/index.html)
|
839 | - [Erlang - triq](https://github.com/krestenkrab/triq)
|
840 | - [Scala - ScalaCheck](https://github.com/rickynils/scalacheck)
|
841 | - [Clojure - test.check](https://github.com/clojure/test.check)
|
842 | - [Python - Hypothesis](https://github.com/DRMacIver/hypothesis)
|
843 |
|
844 | The MIT License (MIT)
|
845 |
|
846 | Copyright (c) 2013-2015 Oleg Grenrus
|
847 |
|
848 | Permission is hereby granted, free of charge, to any person obtaining a copy
|
849 | of this software and associated documentation files (the "Software"), to deal
|
850 | in the Software without restriction, including without limitation the rights
|
851 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
852 | copies of the Software, and to permit persons to whom the Software is
|
853 | furnished to do so, subject to the following conditions:
|
854 |
|
855 | The above copyright notice and this permission notice shall be included in
|
856 | all copies or substantial portions of the Software.
|
857 |
|
858 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
859 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
860 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
861 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
862 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
863 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
864 | THE SOFTWARE.
|