1 /// generate arbitrary random data
2 module qcheck.arbitrary;
3 
4 import std.array, std.algorithm, std.typetuple;
5 import qcheck.detail.arbitrary, qcheck.detail.random,  qcheck.config;
6 
7 /**
8    Get a random value of type `T`.
9    Randomly generates necessary ctor arguments.
10 
11    Depending on `Config.randomizeFields`, all fields of structs and classes are random initialized.
12 
13    Params:
14      Generators = custom factory functions for specific types
15      config = Configuration for random generation
16 
17    Throws:
18      CyclicDependencyException - when ctor arguments cyclically depend on each other
19 */
20 T getArbitrary(T, Generators...)(Config config=Config.init)
21 {
22     auto builder = Builder!(T, Generators)(config);
23     return builder.get();
24 }
25 
26 /// basic usage
27 unittest
28 {
29     auto sarray = getArbitrary!(int[4])();
30     auto array = getArbitrary!(float[])();
31     auto aarray = getArbitrary!(int[string])();
32 }
33 
34 /// fields not randomized
35 unittest
36 {
37     static struct Tuple { TypeTuple!(uint, float) vals; alias vals this; }
38     Config config;
39     config.randomizeFields = false;
40     auto val = getArbitrary!(Tuple)(config);
41     assert(val[0] == uint.init);
42     assert(isNaN(val[1]));
43 }
44 
45 /// complex example with ctor arguments and generators
46 unittest
47 {
48     static struct UserStruct
49     {
50         this(int val) { this.val = val; }
51         int val;
52     }
53 
54     static struct UserStructHolder
55     {
56         this(UserStruct val) { this.val = val; }
57         UserStruct val;
58     }
59 
60     static struct UserStructInit
61     {
62         UserStruct val;
63     }
64 
65     static UserStruct generator()
66     {
67         return UserStruct(10);
68     }
69 
70     auto val = getArbitrary!(UserStruct, generator)();
71     assert(val.val == 10);
72 
73     auto val2 = getArbitrary!(UserStructHolder, generator)();
74     assert(val2.val.val == 10);
75 
76     auto val3 = getArbitrary!(UserStructInit, generator)();
77     assert(val3.val.val == 10);
78 }
79 
80 /// picks a random ctor
81 unittest
82 {
83     static class MultiCtors
84     {
85         uint val;
86 
87         this(uint)
88         {
89             this.val = 1;
90         }
91 
92         this(uint, uint)
93         {
94             this.val = 2;
95         }
96 
97         this(uint, uint, uint)
98         {
99             this.val = 3;
100         }
101 
102         this(uint, uint, uint, uint)
103         {
104             this.val = 4;
105         }
106     }
107 
108   auto initial = getArbitrary!(MultiCtors)();
109   auto i = 0;
110   while (initial.val == getArbitrary!(MultiCtors)().val)
111       assert(++i < 100);
112 }
113 
114 /// field can be configured to be non-random
115 unittest
116 {
117     static class ClassWCtor
118     {
119         this()
120         {
121         }
122 
123         int m;
124     }
125 
126     static class TestClass
127     {
128         ClassWCtor val;
129     }
130 
131     static struct TestStruct
132     {
133         ClassWCtor val;
134     }
135 
136     static class ClassWOCtor
137     {
138         int m;
139     }
140 
141     Config config;
142     config.randomizeFields = false;
143     auto cl = getArbitrary!TestClass(config);
144     assert(cl.val is null);
145     cl = getArbitrary!(TestClass)();
146     assert(!(cl.val is null));
147 }
148 
149 /** Randomly initialize all fields of tuple `Tup`.
150 
151     See_also: getArbitrary
152 */
153 Tup getArbitraryTuple(Tup, Generators...)(Config config=Config.init)
154 {
155     Tup tup;
156     auto builder = Builder!(Tup, Generators)(config);
157     tup = builder.getTuple!(typeof(tup.tupleof))();
158     return tup;
159 }
160 
161 /** Randomly initialize an array of `len`
162 
163     See_also: getArbitrary
164  */
165 T[] getArbitraryArray(T, Generators...)(size_t len, Config config=Config.init)
166 {
167     T[] result;
168     auto builder = Builder!(T, Generators)(config);
169     foreach(_; 0 .. len)
170         result ~= builder.get();
171     return result;
172 }
173 
174 /*
175  * Set the random seed for the random generator used by the qcheck
176  * library. This is useful to gain reproducible results.
177  */
178 public alias randomSeed = qcheck.detail.random.randomSeed;
179 
180 version (unittest):
181 
182 import std.conv : to, roundTo;
183 import std.stdio : writeln, writefln;
184 import std.math;
185 import std.traits;
186 import std.typecons;
187 import std.typetuple;
188 import qcheck;
189 
190 // debug=ARBITRARY;
191 
192 template maxIt(T) if(isFloatingPoint!T)
193 {
194     enum maxIt = 0;
195 }
196 
197 template maxIt(T) if(isSigned!T && isIntegral!T)
198 {
199      enum maxIt = maxIt!(Unsigned!T);
200 }
201 
202 template maxIt(T) if(is(T == ulong)) { enum maxIt = 0; }
203 template maxIt(T) if(is(T == uint)) { enum maxIt = 0; }
204 template maxIt(T) if(is(T == ushort)) { enum maxIt = 1; }
205 template maxIt(T) if(is(T == ubyte)) { enum maxIt = 1; }
206 template maxIt(T) if(is(T == bool)) { enum maxIt = 10; }
207 
208 template testNumeric(T)
209 {
210     void run()
211     {
212         auto b0 = getArbitrary!T();
213         size_t i;
214         while (b0 == getArbitrary!T())
215         {
216             assert(i <= maxIt!T);
217             ++i;
218         }
219     }
220 }
221 
222 
223 unittest
224 {
225     testNumeric!bool.run();
226     testNumeric!ulong.run();
227     testNumeric!long.run();
228     testNumeric!uint.run();
229     testNumeric!int.run();
230     testNumeric!ushort.run();
231     testNumeric!short.run();
232     testNumeric!ubyte.run();
233     testNumeric!byte.run();
234     testNumeric!float.run();
235     testNumeric!double.run();
236     testNumeric!real.run();
237 }
238 
239 
240 enum BoolNum : bool
241 {
242     FALSE = false,
243     TRUE = true,
244 }
245 
246 struct EnumStruct
247 {
248     this(string name)
249     {
250         this.name = name;
251     }
252 
253     string name;
254 }
255 
256 ///
257 unittest
258 {
259     static struct S
260     {
261         string name; // fields are randomized by default
262     }
263 
264     assert(getArbitrary!S().name != getArbitrary!S().name);
265 }
266 
267 ///
268 unittest
269 {
270     enum Enum
271     {
272         A = 10,
273         B = 20,
274         C = 30,
275         D = 40,
276     }
277 
278     Enum val = getArbitrary!Enum();
279     assert(cast(int)val % 10 == 0);
280 }
281 
282 struct Entry
283 {
284     this(Second val)
285     {
286         this.val = val;
287     }
288     Second val;
289 }
290 
291 class Second
292 {
293     this(Cyclic val)
294     {
295         this.val = val;
296     }
297     Cyclic val;
298 }
299 
300 struct Cyclic
301 {
302     this(Entry val)
303     {
304         this.val = val;
305     }
306     Entry val;
307 }
308 
309 unittest
310 {
311     bool succeeded = false;
312     try
313     {
314         auto val = getArbitrary!(Entry)();
315     }
316     catch (CyclicDependencyException e)
317     {
318         succeeded = true;
319     }
320     assert(succeeded);
321 }
322 
323 unittest
324 {
325     static bool testFloat(float f)
326     {
327         return -1000 < f && f < 1000;
328     }
329 
330     static bool testArray(int K)(byte[] ary)
331     {
332         return ary.length <= K;
333     }
334 
335     Config config;
336     config.minValue = -1000;
337     config.maxValue = 1000;
338 
339     quickCheck!testFloat(config);
340 
341     config.maxSize = 10;
342     quickCheck!(testArray!10)(config);
343 
344     config.minValue = -2;
345     config.maxValue = 2;
346     config.maxSize = 5;
347     quickCheck!(testArray!5)(config);
348 }