Comparing version 0.6.3 to 0.7.0
## Release History | ||
- **0.7.0** — *2015-08-23* — More experiments | ||
- `jsc.sum` - generate arbitrary sum types (generalisation of either) [125](https://github.com/jsverify/jsverify/pull/125) | ||
- *BREAKING CHANGE:* bar (`|`) in DSL generates `jsc.sum` | ||
- 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) | ||
- fail early when `jsc.forall` is given zero generators [#128](https://github.com/jsverify/jsverify/issues/128) | ||
- `jsc.json` has shrink [#122](https://github.com/jsverify/jsverify/issues/122) | ||
- non-true non-function results from properties are treated as exceptions [#127](https://github.com/jsverify/jsverify/issues/127) | ||
- **0.6.3** — *2015-07-27* — Bug fixes | ||
@@ -4,0 +11,0 @@ - `jsc.utils.isEqual` doesn't care about key ordering [#123](https://github.com/jsverify/jsverify/issues/123) |
@@ -0,1 +1,2 @@ | ||
/* eslint strict:[2,"function"] */ | ||
module.exports = function (config) { | ||
@@ -9,3 +10,3 @@ "use strict"; | ||
"dist/jsverify.standalone.js", | ||
"spec/*.js" | ||
"spec/*.js", | ||
], | ||
@@ -22,4 +23,4 @@ exclude: [ | ||
browsers: ["Chrome", "Firefox"], | ||
singleRun: true | ||
singleRun: true, | ||
}); | ||
}; |
@@ -27,3 +27,5 @@ "use strict"; | ||
tuple: arbitrary.tuple, | ||
sum: arbitrary.sum, | ||
oneof: arbitrary.oneof, | ||
recursive: arbitrary.recursive, | ||
}, | ||
@@ -40,2 +42,3 @@ generator: { | ||
// Re-export stuff from internal modules | ||
/* eslint-disable guard-for-in */ | ||
var k; | ||
@@ -42,0 +45,0 @@ for (k in primitive) { |
@@ -83,2 +83,13 @@ /* @flow weak */ | ||
/** | ||
- `sum(arbs: (arbitrary a, arbitrary b...)): arbitrary (a | b ...) | ||
*/ | ||
function sum(arbs) { | ||
arbs = arbs.map(utils.force); | ||
return arbitraryBless({ | ||
generator: generator.sum(utils.pluck(arbs, "generator")), | ||
shrink: shrink.sum(utils.pluck(arbs, "shrink")), | ||
show: show.sum(utils.pluck(arbs, "show")), | ||
}); | ||
} | ||
/** | ||
- `dict(arb: arbitrary a): arbitrary (dict a)` | ||
@@ -143,2 +154,21 @@ | ||
function recursive(arbZ, arbS) { | ||
var genZ = arbZ.generator; | ||
var genS = function (recGen) { | ||
var recArb = arbitraryBless({ | ||
generator: recGen, | ||
shrink: shrink.noop, | ||
show: show.def, | ||
}); | ||
return arbS(recArb).generator; | ||
}; | ||
var gen = generator.recursive(genZ, genS); | ||
return arbitraryBless({ | ||
generator: gen, | ||
shrink: shrink.noop, | ||
show: show.def, | ||
}); | ||
} | ||
module.exports = { | ||
@@ -154,3 +184,5 @@ nonshrink: nonshrink, | ||
tuple: tuple, | ||
sum: sum, | ||
oneof: oneof, | ||
recursive: recursive, | ||
}; |
@@ -13,7 +13,7 @@ "use strict"; | ||
/* jshint validthis:true */ | ||
var arb = this; | ||
var arb = this; // eslint-disable-line no-invalid-this | ||
return arbitraryBless({ | ||
generator: arb.generator.map(f), | ||
shrink: arb.shrink.smap(f, g), | ||
show: newShow || show.def | ||
show: newShow || show.def, | ||
}); | ||
@@ -20,0 +20,0 @@ } |
@@ -43,3 +43,3 @@ /* @flow weak */ | ||
}).join(", ") + "]"; | ||
} | ||
}, | ||
}); | ||
@@ -46,0 +46,0 @@ } |
@@ -5,4 +5,5 @@ /* @flow weak */ | ||
var assert = require("assert"); | ||
var either = require("./either.js"); | ||
var random = require("./random.js"); | ||
var either = require("./either.js"); | ||
var sum = require("./sum.js"); | ||
var utils = require("./utils.js"); | ||
@@ -33,3 +34,3 @@ | ||
/* jshint validthis:true */ | ||
var generator = this; | ||
var generator = this; // eslint-disable-line no-invalid-this | ||
generatorAssert(generator); | ||
@@ -43,3 +44,3 @@ return generatorBless(function (size) { | ||
/* jshint validthis:true */ | ||
var generator = this; | ||
var generator = this; // eslint-disable-line no-invalid-this | ||
generatorAssert(generator); | ||
@@ -171,2 +172,3 @@ return generatorBless(function (size) { | ||
case 1: return either.right(genB(size)); | ||
// no default | ||
} | ||
@@ -203,2 +205,15 @@ }); | ||
/** | ||
- `generator.sum(gens: (generator a, generator b...)): generator (a | b...)` | ||
*/ | ||
function generateSum(gens) { | ||
var len = gens.length; | ||
var result = generatorBless(function (size) { | ||
var idx = random(0, len - 1); | ||
return sum.addend(idx, len, gens[idx](size)); | ||
}); | ||
return utils.curried2(result, arguments); | ||
} | ||
/** | ||
- `generator.array(gen: generator a): generator (array a)` | ||
@@ -258,2 +273,3 @@ */ | ||
tuple: generateTuple, | ||
sum: generateSum, | ||
array: generateArray, | ||
@@ -260,0 +276,0 @@ nearray: generateNEArray, |
"use strict"; | ||
var assert = require("assert"); | ||
var arbitraryBless = require("./arbitraryBless.js"); | ||
@@ -9,2 +11,3 @@ var generator = require("./generator.js"); | ||
var string = require("./string.js"); | ||
var utils = require("./utils.js"); | ||
@@ -22,5 +25,35 @@ var generateInteger = primitive.integer.generator; | ||
// Forward declaration | ||
var shrinkDictJson; | ||
var shrinkJson; | ||
function shrinkRecJson(json) { | ||
if (Array.isArray(json)) { | ||
return shrink.array(shrinkJson, json); | ||
} else { | ||
return shrinkDictJson(json); | ||
} | ||
} | ||
shrinkJson = shrink.bless(function (json) { | ||
assert(typeof json !== "function"); | ||
switch (typeof json) { | ||
case "boolean": return primitive.bool.shrink(json); | ||
case "number": return primitive.number.shrink(json); | ||
case "string": return string.string.shrink(json); | ||
default: return shrinkRecJson(json); | ||
} | ||
}); | ||
shrinkDictJson = (function () { | ||
var pairShrink = shrink.pair(string.string.shrink, shrinkJson); | ||
var arrayShrink = shrink.array(pairShrink); | ||
return arrayShrink.smap(utils.pairArrayToDict, utils.dictToPairArray); | ||
}()); | ||
var json = arbitraryBless({ | ||
generator: generateJson, | ||
shrink: shrink.noop, | ||
shrink: shrinkJson, | ||
show: show.def, | ||
@@ -27,0 +60,0 @@ }); |
@@ -34,3 +34,3 @@ /* @flow weak */ | ||
*/ | ||
"use strict"; | ||
"use strict"; | ||
@@ -120,2 +120,3 @@ /** | ||
var suchthat = require("./suchthat.js"); | ||
var sum = require("./sum.js"); | ||
var typify = require("./typify.js"); | ||
@@ -187,2 +188,4 @@ var utils = require("./utils.js"); | ||
assert(gens.length > 0, "forall requires at least single generator"); | ||
// Map typify-dsl to hard generators | ||
@@ -200,4 +203,5 @@ gens = gens.map(function (g) { | ||
return functor.bind(property, x, function (r, exc) { | ||
if (r === true) { return true; } | ||
if (typeof r === "function") { | ||
if (r === true) { | ||
return true; | ||
} else if (typeof r === "function") { | ||
var rRec = r(size); | ||
@@ -212,3 +216,3 @@ | ||
counterexample: rr.counterexample.concat(rRecPrime.counterexample), | ||
counterexamplestr: rr.counterexamplestr ,//+ "; " + rRec.counterexamplestr, | ||
counterexamplestr: rr.counterexamplestr ,// + "; " + rRec.counterexamplestr, | ||
shrinks: rr.shrinks, | ||
@@ -220,5 +224,5 @@ exc: rr.exc || rRecPrime.exc, | ||
}); | ||
} else { | ||
return shrinkResult(gens, x, test, size, shrinks, exc || r, utils.identity); | ||
} | ||
return shrinkResult(gens, x, test, size, shrinks, exc, utils.identity); | ||
}); | ||
@@ -243,3 +247,3 @@ } | ||
} else { | ||
msg += "Exception: " + r.exc; | ||
msg += "Error: " + r.exc; | ||
} | ||
@@ -490,2 +494,5 @@ } | ||
// sum | ||
addend: sum.addend, | ||
// compile | ||
@@ -508,6 +515,7 @@ compile: compile, | ||
/* primitives */ | ||
var k; | ||
for (k in api.arbitrary) { | ||
/* eslint-disable guard-for-in */ | ||
for (var k in api.arbitrary) { | ||
jsc[k] = api.arbitrary[k]; | ||
} | ||
/* eslint-enable guard-for-in */ | ||
@@ -514,0 +522,0 @@ module.exports = jsc; |
@@ -168,3 +168,3 @@ /* @flow weak */ | ||
var i = random(0, 1); | ||
return i === 0 ? false : true; | ||
return i === 1; | ||
}), | ||
@@ -272,3 +272,3 @@ | ||
shrink: shrink.noop, | ||
show: show.def | ||
show: show.def, | ||
}); | ||
@@ -275,0 +275,0 @@ } |
@@ -66,3 +66,3 @@ "use strict"; | ||
}).join(", ") + "}"; | ||
} | ||
}, | ||
}); | ||
@@ -69,0 +69,0 @@ } |
@@ -0,1 +1,2 @@ | ||
/* @flow weak */ | ||
@@ -65,2 +66,15 @@ "use strict"; | ||
/** | ||
- `show.sum(shrinks: (a -> string, b -> string...), x: (a | b ...)): string` | ||
*/ | ||
function showSum(shows) { | ||
var result = function (sum) { | ||
return sum.fold(function (idx, n, value) { | ||
return "Sum(" + idx + "/" + n + ": " + shows[idx](value) + ")"; | ||
}); | ||
}; | ||
return utils.curried2(result, arguments); | ||
} | ||
/** | ||
- `show.array(shrink: a -> string, x: array a): string` | ||
@@ -81,3 +95,4 @@ */ | ||
tuple: showTuple, | ||
sum: showSum, | ||
array: showArray, | ||
}; |
@@ -7,2 +7,3 @@ /* @flow weak */ | ||
var lazyseq = require("lazy-seq"); | ||
var sum = require("./sum.js"); | ||
var utils = require("./utils.js"); | ||
@@ -27,3 +28,3 @@ | ||
/* jshint validthis:true */ | ||
var shrink = this; | ||
var shrink = this; // eslint-disable-line no-invalid-this | ||
return shrinkBless(function (value) { | ||
@@ -161,2 +162,3 @@ return shrink(g(value)).map(f); | ||
var result = shrinkBless(function (tuple) { | ||
assert(tuple.length === shrinks.length, "shrinkTuple: not-matching params"); | ||
var ll = toLinkedList(tuple); | ||
@@ -169,2 +171,19 @@ return shrink(ll).map(fromLinkedList); | ||
/** | ||
- `shrink.sum(shrs: (shrink a, shrink b...)): shrink (a | b...)` | ||
*/ | ||
function shrinkSum(shrinks) { | ||
assert(shrinks.length > 0, "shrinkTuple needs > 0 values"); | ||
var result = shrinkBless(function (s) { | ||
return s.fold(function (idx, len, value) { | ||
assert(len === shrinks.length, "shrinkSum: not-matching params"); | ||
return shrinks[idx](value).map(function (shrinked) { | ||
return sum.addend(idx, len, shrinked); | ||
}); | ||
}); | ||
}); | ||
return utils.curried2(result, arguments); | ||
} | ||
function shrinkArrayWithMinimumSize(size) { | ||
@@ -207,2 +226,3 @@ function shrinkArrayImpl(shrink) { | ||
tuple: shrinkTuple, | ||
sum: shrinkSum, | ||
array: shrinkArray, | ||
@@ -209,0 +229,0 @@ nearray: shrinkNEArray, |
@@ -18,8 +18,11 @@ /* @flow weak */ | ||
- *square brackets* are treated as a shorthand for the array type: `"[nat]"` is evaluated to `jsc.array(jsc.nat)`. | ||
- *union*: `"bool | nat"` is evaulated to `jsc.oneof(jsc.bool, jsc.nat)`. | ||
- *union*: `"bool | nat"` is evaluated to `jsc.sum(jsc.bool, jsc.nat)`. | ||
- **Note** `oneof` cannot be shrinked, because the union is untagged, we don't know which shrink to use. | ||
- *anonymous records*: `"{ b: bool; n: nat}"` is evaluated to `jsc.record({ n: jsc.bool, n: jsc.nat })`. | ||
- *conjunction*: `"bool & nat"` is evaluated to `jsc.tuple(jsc.bool, jsc.nat)`. | ||
- *anonymous records*: `"{ b: bool; n: nat }"` is evaluated to `jsc.record({ n: jsc.bool, n: jsc.nat })`. | ||
- *EXPRIMENTAL: recursive types*: `"rec list -> unit | (nat & list)"`. | ||
*/ | ||
var arbitrary = require("./arbitrary.js"); | ||
var assert = require("assert"); | ||
var record = require("./record.js"); | ||
@@ -29,2 +32,3 @@ var array = require("./array.js"); | ||
var typifyParser = require("typify-parser"); | ||
var utils = require("./utils.js"); | ||
@@ -63,5 +67,10 @@ // Forward declarations | ||
var args = compileTypeArray(env, type.args); | ||
return arbitrary.oneof(args); | ||
return arbitrary.sum(args); | ||
} | ||
function compileConjunction(env, type) { | ||
var args = compileTypeArray(env, type.args); | ||
return arbitrary.tuple(args); | ||
} | ||
function compileRecord(env, type) { | ||
@@ -76,2 +85,31 @@ // TODO: use mapValues | ||
function compileRecursive(env, type) { | ||
assert(type.arg.type === "disjunction", "recursive type's argument should be disjunction"); | ||
// bound variable | ||
var name = type.name; | ||
var par = utils.partition(type.arg.args, function (t) { | ||
return typifyParser.freeVars(t).indexOf(name) === -1; | ||
}); | ||
var terminal = par[0]; | ||
if (terminal.length === 0) { | ||
throw new Error("Recursive type without non-recursive branch"); | ||
} | ||
var terminalArb = compileType(env, { | ||
type: "disjunction", | ||
args: terminal, | ||
}); | ||
return arbitrary.recursive(terminalArb, function (arb) { | ||
var arbEnv = {}; | ||
arbEnv[name] = arb; | ||
var newEnv = utils.merge(env, arbEnv); | ||
return compileType(newEnv, type.arg); | ||
}); | ||
} | ||
compileType = function compileTypeFn(env, type) { | ||
@@ -84,4 +122,6 @@ switch (type.type) { | ||
case "disjunction": return compileDisjunction(env, type); | ||
case "conjunction": return compileConjunction(env, type); | ||
case "record": return compileRecord(env, type); | ||
case "number": return type.value; | ||
case "recursive": return compileRecursive(env, type); | ||
default: throw new Error("Unsupported typify ast type: " + type.type); | ||
@@ -88,0 +128,0 @@ } |
@@ -76,3 +76,3 @@ /* @flow weak */ | ||
opts = opts || {}; | ||
var fnEqual = opts.fnEqual === false ? false : true; | ||
var fnEqual = opts.fnEqual !== false; | ||
var depth = opts.depth || 5; // totally arbitrary | ||
@@ -226,2 +226,18 @@ | ||
function partition(arr, pred) { | ||
var truthy = []; | ||
var falsy = []; | ||
for (var i = 0; i < arr.length; i++) { | ||
var x = arr[i]; | ||
if (pred(x)) { | ||
truthy.push(x); | ||
} else { | ||
falsy.push(x); | ||
} | ||
} | ||
return [truthy, falsy]; | ||
} | ||
module.exports = { | ||
@@ -244,2 +260,3 @@ isArray: isArray, | ||
dictToPairArray: dictToPairArray, | ||
partition: partition, | ||
}; |
{ | ||
"name": "jsverify", | ||
"description": "Property-based testing for JavaScript.", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"homepage": "http://jsverify.github.io/", | ||
@@ -31,6 +31,6 @@ "author": { | ||
"david": "^6.0.1", | ||
"eslint": ">=0.23.0 <1.0.0", | ||
"eslint": "^1.2.1", | ||
"esprima": "^2.2.0", | ||
"istanbul": "~0.3.0", | ||
"jscs": "^1.6.2", | ||
"jscs": "^2.0.0", | ||
"jshint": "^2.7.0", | ||
@@ -61,7 +61,7 @@ "karma": "^0.13.3", | ||
"dependencies": { | ||
"lazy-seq": "~0.2.0", | ||
"lazy-seq": "^1.0.0", | ||
"rc4": "~0.1.5", | ||
"trampa": "^1.0.0", | ||
"typify-parser": "^1.0.0" | ||
"typify-parser": "^1.1.0" | ||
} | ||
} |
@@ -199,5 +199,7 @@ # JSVerify | ||
- *square brackets* are treated as a shorthand for the array type: `"[nat]"` is evaluated to `jsc.array(jsc.nat)`. | ||
- *union*: `"bool | nat"` is evaulated to `jsc.oneof(jsc.bool, jsc.nat)`. | ||
- *union*: `"bool | nat"` is evaluated to `jsc.sum(jsc.bool, jsc.nat)`. | ||
- **Note** `oneof` cannot be shrinked, because the union is untagged, we don't know which shrink to use. | ||
- *anonymous records*: `"{ b: bool; n: nat}"` is evaluated to `jsc.record({ n: jsc.bool, n: jsc.nat })`. | ||
- *conjunction*: `"bool & nat"` is evaluated to `jsc.tuple(jsc.bool, jsc.nat)`. | ||
- *anonymous records*: `"{ b: bool; n: nat }"` is evaluated to `jsc.record({ n: jsc.bool, n: jsc.nat })`. | ||
- *EXPRIMENTAL: recursive types*: `"rec list -> unit | (nat & list)"`. | ||
@@ -316,2 +318,4 @@ ### Arbitrary data | ||
- `sum(arbs: (arbitrary a, arbitrary b...)): arbitrary (a | b ...) | ||
- `dict(arb: arbitrary a): arbitrary (dict a)` | ||
@@ -431,2 +435,4 @@ | ||
- `generator.sum(gens: (generator a, generator b...)): generator (a | b...)` | ||
- `generator.array(gen: generator a): generator (array a)` | ||
@@ -471,2 +477,4 @@ | ||
- `shrink.sum(shrs: (shrink a, shrink b...)): shrink (a | b...)` | ||
- `shrink.array(shr: shrink a): shrink (array a)` | ||
@@ -488,2 +496,4 @@ | ||
- `show.sum(shrinks: (a -> string, b -> string...), x: (a | b ...)): string` | ||
- `show.array(shrink: a -> string, x: array a): string` | ||
@@ -587,2 +597,9 @@ | ||
- **0.7.0** — *2015-08-23* — More experiments | ||
- `jsc.sum` - generate arbitrary sum types (generalisation of either) [125](https://github.com/jsverify/jsverify/pull/125) | ||
- *BREAKING CHANGE:* bar (`|`) in DSL generates `jsc.sum` | ||
- 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) | ||
- fail early when `jsc.forall` is given zero generators [#128](https://github.com/jsverify/jsverify/issues/128) | ||
- `jsc.json` has shrink [#122](https://github.com/jsverify/jsverify/issues/122) | ||
- non-true non-function results from properties are treated as exceptions [#127](https://github.com/jsverify/jsverify/issues/127) | ||
- **0.6.3** — *2015-07-27* — Bug fixes | ||
@@ -749,3 +766,3 @@ - `jsc.utils.isEqual` doesn't care about key ordering [#123](https://github.com/jsverify/jsverify/issues/123) | ||
- [quick\_check](https://www.npmjs.org/package/quick_check) | ||
- [gencheck](https://github.com/graue/gentest) | ||
- [gentest](https://github.com/graue/gentest) | ||
- [node-quickcheck](https://github.com/mcandre/node-quickcheck) | ||
@@ -752,0 +769,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
263572
39
7312
794
+ Addedlazy-seq@1.0.0(transitive)
- Removedlazy-seq@0.2.0(transitive)
Updatedlazy-seq@^1.0.0
Updatedtypify-parser@^1.1.0