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 }