1 // generate arbitrary data
2 module qcheck.detail.arbitrary;
3 
4 import std.algorithm, std.array, std.conv, std.exception, std.traits, std.typecons, std.typetuple;
5 import qcheck.detail.conv, qcheck.detail.random, qcheck.config, qcheck.exceptions;
6 
7 package(qcheck):
8 
9 struct Builder(T, Generators...)
10 {
11     Config _config;
12     //! To detect recursive calls at runtime
13     TypeInfo[] _recursed;
14 
15     static if (Generators.length)
16         alias staticMap!(ReturnType, Generators) UserTypes;
17     else
18         alias TypeTuple!() UserTypes;
19 
20     /**
21      * Construct a randomized instance of T.
22      */
23     T get()
24     {
25         return internalGet!(T)();
26     }
27 
28     /**
29      * Construct a randomized Tuple of T.
30      */
31     alias constructTuple getTuple;
32 
33 private:
34     T2 internalGet(T2)()
35     {
36         auto info = typeid(T2);
37 
38         // @@ BUG @@
39         version (none)
40         {
41             // clobbers global.errors during speculative instantiation of
42             // isBidirectionalRange!(typeof(info))
43             enforceEx!CyclicDependencyException(!_recursed.canFind(info),
44                                                 "Recursive call of getArbitrary!("~T.stringof~")()");
45         }
46         else
47         {
48             foreach(ti; _recursed)
49             {
50                 if (ti == info)
51                     throw new CyclicDependencyException(
52                         "Recursive call of getArbitrary!("~T.stringof~")()");
53             }
54         }
55 
56         _recursed ~= info;
57         scope(exit) _recursed.popBack;
58 
59         enum UserIdx = staticIndexOf!(T2, UserTypes);
60         static if (UserIdx != -1)
61         {
62             alias Generators[UserIdx] Ctor;
63             static assert(isCallable!(Ctor), "Generator "~to!string(Ctor)~" is not callable.");
64             return callUserFunc!(T2, Ctor)();
65         }
66         else
67         {
68             return arbitraryL!(T2).get();
69         }
70     }
71 
72     /**
73      * Call a user defined function to instantiate a T.
74      */
75     T callUserFunc(T, alias Ctor)()
76     {
77         alias staticMap!(Unqual, ParameterTypeTuple!(Ctor)) TP;
78 
79         static if (TP.length == 0)
80             return Ctor();
81         else
82         {
83             auto ctorParams = constructTuple!(TP)();
84             return Ctor(ctorParams.tupleof);
85         }
86     }
87 
88     /**
89      * Instantiate a struct, choose a random ctor overload.
90      */
91     template arbitraryL(T) if(is(T == struct))
92     {
93         T get()
94         {
95             auto t = newStructInstance!(T)();
96             if (_config.randomizeFields)
97                 this.initTuple(t.tupleof);
98             return t;
99         }
100     }
101 
102     /**
103      * Instantiate a class, choose a random ctor overload.
104      */
105     template arbitraryL(T) if(is(T == class))
106     {
107         T get() {
108             T inst = newClassInstance!(T)();
109             if (_config.randomizeFields && inst !is null)
110             {
111                 initTuple(inst.tupleof);
112             }
113             return inst;
114         }
115     }
116 
117     /**
118      * Interface implementation, tries at runtime to find default
119      * constructible implementations of interface.
120      */
121     template arbitraryL(T) if(is(T == interface))
122     {
123         //! Might return null, if not implemented or not default
124         //! constructible.
125         T get()
126         {
127             T result;
128 
129             auto implementors =
130                 findClasses((ClassInfo ci) { return inherits(ci, T.classinfo); } );
131 
132             if (implementors.length)
133             {
134                 auto rndIdx = randomNumeric(0u, implementors.length - 1);
135                 auto name = implementors[rndIdx].name;
136                 result = cast(T)Object.factory(name);
137             }
138             return result;
139         }
140     }
141 
142     /**
143      * Instantiate a static array.
144      */
145     template arbitraryL(T) if(isStaticArray!T && !is(T == struct))
146     {
147         T get()
148         {
149             alias Unqual!(typeof(T.init[0])) ElemT;
150 
151             auto count = T.length;
152             T res = void;
153             while (count--)
154             {
155                 ElemT e = internalGet!(ElemT)();
156                 res[count] = e;
157             }
158             return res;
159         }
160     }
161 
162     /**
163      * Instantiate an array.
164      */
165     template arbitraryL(T) if(isDynamicArray!T)
166     {
167         T get()
168         {
169             alias Unqual!(typeof(T.init[0])) ElemT;
170 
171             auto count = randomNumeric(cast(size_t)0, _config.maxSize);
172             T res;
173             res.reserve(count);
174             while (count--)
175                 res ~= internalGet!(ElemT)();
176             return res;
177         }
178     }
179 
180     /**
181      * Instantiate an array.
182      */
183     template arbitraryL(T) if(isAssociativeArray!T)
184     {
185         T get()
186         {
187             alias typeof(T.init.keys[0]) KeyT;
188             alias typeof(T.init.values[0]) ValueT;
189 
190             auto count = randomNumeric(cast(size_t)0, _config.maxSize);
191             T res;
192             while (count--)
193             {
194                 auto key = internalGet!(KeyT)();
195                 auto value = internalGet!(ValueT)();
196                 res[key] = value;
197             }
198             return res;
199         }
200     }
201 
202   /**
203    * Default char
204    */
205   template arbitraryL(T) if(is(T == dchar) || is(T == wchar) || is(T == char))
206   {
207       T get()
208       {
209           return randomChar!(T)();
210       }
211   }
212 
213   /**
214    * Default numerical types
215    */
216   template arbitraryL(T) if(isNumeric!T && !is(T == enum))
217   {
218       T get()
219       {
220           return randomNumeric!(T)(clipTo!T(_config.minValue), clipTo!T(_config.maxValue));
221       }
222   }
223 
224     /**
225      * Bool type
226      */
227     template arbitraryL(T) if(is(T == bool))
228     {
229         bool get()
230         {
231             return randomNumeric!(int)() < 0;
232         }
233     }
234 
235     /**
236      * Enums
237      */
238     template arbitraryL(T) if(is(T == enum))
239     {
240         T get() {
241             alias EnumMembers!T EnumTuple;
242             auto idx = randomNumeric!(size_t)(cast(size_t)0, EnumMembers!T.length - 1);
243             switch (idx)
244             {
245             foreach(i, v; EnumMembers!T)
246             case i:
247                 return v;
248 
249             default:
250                 assert(0);
251             }
252         }
253     }
254 
255     /**
256      * Complex types
257      */
258     template isComplex(T)
259     {
260         enum bool isComplex = staticIndexOf!(Unqual!(T),
261                                              cfloat, cdouble, creal) >= 0;
262     }
263 
264     template arbitraryL(T) if(isComplex!T)
265     {
266         T get()
267         {
268             return internalGet!(typeof(T.re))() + 1i * internalGet!(typeof(T.im))();
269         }
270     }
271 
272     // struct instantiation helper
273     T newStructInstance(T)()
274     {
275         alias ctorOverloadSet!(T) overloads;
276 
277         final switch (_config.ctors)
278         {
279         case Config.Ctors.Any:
280             auto which = overloads.length ? randomNumeric(cast(size_t)0, overloads.length - 1) : 0;
281             return callStructCtorOverload!(T, overloads)(which);
282 
283         case Config.Ctors.DefaultOnly:
284             static if (!is(typeof(T())))
285                 assert(0, "Can't default construct a " ~ T.stringof);
286             else
287                 return T();
288         }
289     }
290 
291     T callStructCtorOverload(T, Overloads...)(size_t idx)
292     {
293         switch (idx)
294         {
295         foreach(i,ctor; Overloads)
296         case i:
297             return callStructCtor!(T, ctor)();
298 
299         default:
300             assert(0);
301         }
302     }
303 
304     T callStructCtorOverload(T)(size_t idx)
305     {
306         return T();
307     }
308 
309     T callStructCtor(T, ctorType)()
310     {
311         alias staticMap!(Unqual, ParameterTypeTuple!(ctorType)) TP;
312 
313         static if (TP.length == 0)
314             return T();
315         else
316         {
317             auto ctorParams = constructTuple!(TP)();
318             return T(ctorParams.tupleof);
319         }
320     }
321 
322     // class instantiation helper
323     T newClassInstance(T)()
324     {
325         alias ctorOverloadSet!(T) overloads;
326 
327         final switch (_config.ctors)
328         {
329         case Config.Ctors.Any:
330             auto which = overloads.length ? randomNumeric(cast(size_t)0,  overloads.length - 1) : 0;
331             return callClassCtorOverload!(T, overloads)(which);
332 
333         case Config.Ctors.DefaultOnly:
334             static if (!is(typeof(new T())))
335                 assert(0, "Can't default construct class " ~ T.stringof);
336             else
337                 return new T();
338         }
339     }
340 
341     T callClassCtorOverload(T)(size_t idx)
342     {
343         return new T();
344     }
345 
346     T callClassCtorOverload(T, Overloads...)(size_t idx)
347     {
348         switch (idx)
349         {
350         foreach(i, ctor; Overloads)
351         case i:
352             return callClassCtor!(T, ctor)();
353 
354         default:
355             assert(0);
356         }
357     }
358 
359     T callClassCtor(T, ctorType)()
360     {
361         alias staticMap!(Unqual, ParameterTypeTuple!(ctorType)) TP;
362 
363         auto ctorParams = constructTuple!(TP)();
364         return new T(ctorParams.tupleof);
365     }
366 
367     // Tuple helpers
368     Tuple!T constructTuple(T...)() if(isTypeTuple!T)
369     {
370         Tuple!T res;
371         initTuple(res.tupleof);
372         return res;
373     }
374 
375     void initTuple(TS...)(ref TS ts)
376     {
377         foreach(i, T; TS)
378             ts[i] = internalGet!T();
379     }
380 }
381 
382 private:
383 
384 template ctorOverloadSet(T)
385 {
386     static if (__traits(hasMember, T, "__ctor"))
387         alias typeof(__traits(getOverloads, T, "__ctor")) ctorOverloadSet;
388     else
389         alias TypeTuple!() ctorOverloadSet; // empty
390 }