UNPKG

5.79 kBJavaScriptView Raw
1/* @flow weak */
2"use strict";
3
4var arbitraryAssert = require("./arbitraryAssert.js");
5var arbitraryBless = require("./arbitraryBless.js");
6var array = require("./array.js");
7var assert = require("assert");
8var dict = require("./dict.js");
9var generator = require("./generator.js");
10var json = require("./json.js");
11var pair = require("./pair.js");
12var show = require("./show.js");
13var shrink = require("./shrink.js");
14var utils = require("./utils.js");
15
16/**
17 ### Arbitrary combinators
18*/
19
20/**
21 - `nonshrink(arb: arbitrary a): arbitrary a`
22
23 Non shrinkable version of arbitrary `arb`.
24*/
25function nonshrink(arb) {
26 arb = utils.force(arb);
27
28 return arbitraryBless({
29 generator: arb.generator,
30 shrink: shrink.noop,
31 show: arb.show,
32 });
33}
34
35/**
36 - `unit: arbitrary ()`
37*/
38var unit = arbitraryBless({
39 generator: generator.unit,
40 shrink: shrink.noop,
41 show: show.def,
42});
43
44/**
45 - `either(arbA: arbitrary a, arbB : arbitrary b): arbitrary (either a b)`
46*/
47function either(a, b) {
48 a = utils.force(a || json.json);
49 b = utils.force(b || json.json);
50
51 arbitraryAssert(a);
52 arbitraryAssert(b);
53
54 return arbitraryBless({
55 generator: generator.either(a.generator, b.generator),
56 shrink: shrink.either(a.shrink, b.shrink),
57 show: show.either(a.show, b.show),
58 });
59}
60
61/**
62 - `pair(arbA: arbitrary a, arbB : arbitrary b): arbitrary (pair a b)`
63
64 If not specified `a` and `b` are equal to `value()`.
65*/
66function pairArb(a, b) {
67 return pair.pair(a || json.json, b || json.json);
68}
69
70/**
71 - `tuple(arbs: (arbitrary a, arbitrary b...)): arbitrary (a, b...)`
72*/
73function tuple(arbs) {
74 arbs = arbs.map(utils.force);
75 return arbitraryBless({
76 generator: generator.tuple(utils.pluck(arbs, "generator")),
77 shrink: shrink.tuple(utils.pluck(arbs, "shrink")),
78 show: show.tuple(utils.pluck(arbs, "show")),
79 });
80}
81
82/**
83 - `sum(arbs: (arbitrary a, arbitrary b...)): arbitrary (a | b ...)`
84*/
85function sum(arbs) {
86 arbs = arbs.map(utils.force);
87 return arbitraryBless({
88 generator: generator.sum(utils.pluck(arbs, "generator")),
89 shrink: shrink.sum(utils.pluck(arbs, "shrink")),
90 show: show.sum(utils.pluck(arbs, "show")),
91 });
92}
93/**
94 - `dict(arb: arbitrary a): arbitrary (dict a)`
95
96 Generates a JavaScript object with properties of type `A`.
97*/
98function dictArb(arb) {
99 return dict.arbitrary(arb || json.json);
100}
101
102/**
103 - `array(arb: arbitrary a): arbitrary (array a)`
104*/
105function arrayArb(arb) {
106 return array.array(arb || json.json);
107}
108
109/**
110 - `nearray(arb: arbitrary a): arbitrary (array a)`
111*/
112function nearrayArb(arb) {
113 return array.nearray(arb || json.json);
114}
115
116/**
117 - `json: arbitrary json`
118
119 JavaScript Objects: boolean, number, string, null, array of `json` values or object with `json` values.
120*/
121var jsonArb = json.json;
122
123/**
124 - `oneof(gs : array (arbitrary a)...) : arbitrary a`
125
126 Randomly uses one of the given arbitraries.
127*/
128function oneof() {
129 assert(arguments.length !== 0, "oneof: at least one parameter expected");
130
131 // TODO: write this in more functional way
132 var generators = [];
133 var append = function (a) {
134 generators.push(utils.force(a).generator);
135 };
136 for (var i = 0; i < arguments.length; i++) {
137 var arg = arguments[i];
138 if (utils.isArray(arg)) {
139 arg.forEach(append);
140 } else {
141 append(arg);
142 }
143 }
144
145 return arbitraryBless({
146 generator: generator.oneof(generators),
147 // TODO: make shrink
148 shrink: shrink.noop,
149 show: show.def,
150 });
151}
152
153// Return a lazy arbitrary that delegates to another arbitrary at its
154// 'strict' property. An arbitrary must be assigned to that property before
155// this arbitrary can generate anything.
156function lazyArbitrary() {
157 var arb = {};
158 // This function must be pure because it will not be called with
159 // meaningful context.
160 arb.generator = generator.bless(function (size) {
161 return arb.strict.generator(size);
162 });
163 arb.shrink = shrink.noop;
164 arb.show = show.def;
165 arb = arbitraryBless(arb);
166 return arb;
167}
168
169/**
170 - ```js
171 letrec(
172 (tie: key -> (arbitrary a | arbitrary b | ...))
173 -> { key: arbitrary a, key: arbitrary b, ... }):
174 { key: arbitrary a, key: arbitrary b, ... }
175 ```
176
177 Mutually recursive definitions. Every reference to a sibling arbitrary
178 should go through the `tie` function.
179
180 ```js
181 { arb1, arb2 } = jsc.letrec(function (tie) {
182 return {
183 arb1: jsc.tuple(jsc.int, jsc.oneof(jsc.const(null), tie("arb2"))),
184 arb2: jsc.tuple(jsc.bool, jsc.oneof(jsc.const(null), tie("arb1"))),
185 }
186 });
187 ```
188*/
189function letrec(definition) {
190 // We must use a lazy dictionary because we do not know the key set
191 // before calling the definition.
192 var lazyArbs = {};
193
194 function tie(name) {
195 if (!lazyArbs.hasOwnProperty(name)) {
196 lazyArbs[name] = lazyArbitrary();
197 }
198 return lazyArbs[name];
199 }
200
201 var strictArbs = definition(tie);
202
203 Object.keys(lazyArbs).forEach(function (key) {
204 var strictArb = strictArbs[key];
205 if (!strictArb) {
206 throw new Error("undefined lazy arbitrary: " + key);
207 }
208 lazyArbs[key].strict = strictArb;
209 });
210
211 return strictArbs;
212}
213
214function recursive(arbZ, arbS) {
215 var genZ = arbZ.generator;
216 var genS = function (recGen) {
217 var recArb = arbitraryBless({
218 generator: recGen,
219 shrink: shrink.noop,
220 show: show.def,
221 });
222 return arbS(recArb).generator;
223 };
224
225 var gen = generator.recursive(genZ, genS);
226 return arbitraryBless({
227 generator: gen,
228 shrink: shrink.noop,
229 show: show.def,
230 });
231}
232
233module.exports = {
234 nonshrink: nonshrink,
235 pair: pairArb,
236 either: either,
237 unit: unit,
238 dict: dictArb,
239 json: jsonArb,
240 nearray: nearrayArb,
241 array: arrayArb,
242 tuple: tuple,
243 sum: sum,
244 oneof: oneof,
245 recursive: recursive,
246 letrec: letrec,
247};