1 module unit_threaded.randomized.random; 2 3 4 /** This type will generate a $(D Gen!T) for all passed $(D T...). 5 Every call to $(D genValues) will call $(D gen) of all $(D Gen) structs 6 present in $(D values). The member $(D values) can be passed to every 7 function accepting $(D T...). 8 */ 9 struct RndValueGen(T...) 10 { 11 import std.meta : staticMap; 12 import std.random: Random; 13 14 /* $(D Values) is a collection of $(D Gen) types created through 15 $(D ParameterToGen) of passed $(T ...). 16 */ 17 static if(is(typeof(T[0]) == string[])) { 18 alias generators = T[1 .. $]; 19 string[] parameterNames = T[0]; 20 } else { 21 alias generators = T; 22 string[T.length] parameterNames; 23 } 24 25 alias Values = staticMap!(ParameterToGen, generators); 26 27 /// Ditto 28 Values values; 29 30 /* The constructor accepting the required random number generator. 31 Params: 32 rnd = The required random number generator. 33 */ 34 this(Random* rnd) @safe 35 { 36 this.rnd = rnd; 37 } 38 39 this(ref Random rnd) { 40 this.rnd = &rnd; 41 } 42 43 /* The random number generator used to generate new value for all 44 $(D values). 45 */ 46 Random* rnd; 47 48 /** A call to this member function will call $(D gen) on all items in 49 $(D values) passing $(D the provided) random number generator 50 */ 51 void genValues() 52 { 53 assert(rnd !is null); 54 foreach (ref it; this.values) 55 { 56 it.gen(*this.rnd); 57 } 58 } 59 60 void toString(scope void delegate(const(char)[]) sink) 61 { 62 import std.format : formattedWrite; 63 64 foreach (idx, ref it; values) 65 { 66 formattedWrite(sink, "'%s' = %s ", parameterNames[idx], it); 67 } 68 } 69 } 70 71 /// 72 unittest 73 { 74 import unit_threaded.randomized.gen: Gen; 75 import std.random: Random; 76 77 auto rnd = Random(1337); 78 auto generator = (&rnd).RndValueGen!(["i", "f"], 79 Gen!(int, 0, 10), 80 Gen!(float, 0.0, 10.0)); 81 generator.genValues(); 82 83 static fun(int i, float f) 84 { 85 import std.conv: to; 86 assert(i >= 0 && i <= 10, i.to!string); 87 assert(f >= 0.0 && f <= 10.0, f.to!string); 88 } 89 90 fun(generator.values); 91 } 92 93 @("RndValueGen can be used without parameter names") 94 unittest 95 { 96 import unit_threaded.randomized.gen: Gen; 97 import std.random: Random; 98 99 auto rnd = Random(1337); 100 auto generator = rnd.RndValueGen!(Gen!(int, 0, 10), 101 Gen!(float, 0.0, 10.0)); 102 generator.genValues(); 103 104 static fun(int i, float f) 105 { 106 import std.conv: to; 107 assert(i >= 0 && i <= 10, i.to!string); 108 assert(f >= 0.0 && f <= 10.0, f.to!string); 109 } 110 111 fun(generator.values); 112 } 113 114 115 unittest 116 { 117 import unit_threaded.randomized.gen: Gen; 118 import std.random: Random; 119 120 static fun(int i, float f) 121 { 122 assert(i >= 0 && i <= 10); 123 assert(f >= 0.0 && i <= 10.0); 124 } 125 126 auto rnd = Random(1337); 127 auto generator = (&rnd).RndValueGen!(["i", "f"], 128 Gen!(int, 0, 10), 129 Gen!(float, 0.0, 10.0)); 130 131 generator.genValues(); 132 foreach (i; 0 .. 1000) 133 { 134 fun(generator.values); 135 } 136 } 137 138 @("RndValueGen with int[]") 139 unittest { 140 import unit_threaded.randomized.gen: Gen; 141 import std.random: Random; 142 143 void fun(int[] i) { } 144 auto rnd = Random(1337); 145 auto gen = rnd.RndValueGen!(Gen!(int[])); 146 gen.genValues; 147 fun(gen.values); 148 } 149 150 /** A template that turns a $(D T) into a $(D Gen!T) unless $(D T) is 151 already a $(D Gen) or no $(D Gen) for given $(D T) is available. 152 */ 153 template ParameterToGen(T) 154 { 155 import unit_threaded.randomized.gen: isGen, Gen; 156 static if (isGen!T) 157 alias ParameterToGen = T; 158 else static if (is(T : GenASCIIString!(S), S...)) 159 alias ParameterToGen = T; 160 else { 161 static assert(__traits(compiles, Gen!T), 162 "ParameterToGen does not handle " ~ T.stringof); 163 alias ParameterToGen = Gen!T; 164 } 165 } 166 167 /// 168 unittest 169 { 170 alias GenInt = ParameterToGen!int; 171 172 static fun(int i) 173 { 174 assert(i == 1337); 175 } 176 177 GenInt a; 178 a.value = 1337; 179 fun(a); 180 } 181 182 @("RndValueGen with user defined struct") 183 unittest { 184 import unit_threaded.randomized.gen: Gen; 185 import std.random: Random; 186 187 struct Foo { 188 int i; 189 short s; 190 } 191 192 auto rnd = Random(1337); 193 auto gen = rnd.RndValueGen!(Gen!Foo); 194 195 foreach(_; 0 .. 5) // get rid of front-loaded uninteresting values 196 gen.genValues; 197 198 void fun(Foo foo) { 199 import std.conv: text; 200 assert(foo == Foo(1125387415, -8003), text(foo)); 201 } 202 203 fun(gen.values); 204 } 205 206 207 unittest 208 { 209 import unit_threaded.randomized.gen: isGen; 210 import std.random: Random; 211 import std.meta : AliasSeq, staticMap; 212 213 struct Foo { 214 int i; 215 double d; 216 } 217 218 foreach (T; AliasSeq!(byte, ubyte, ushort, short, uint, int, ulong, long, 219 float, double, real, 220 string, wstring, dstring, Foo)) 221 { 222 alias TP = staticMap!(ParameterToGen, T); 223 static assert(isGen!TP); 224 } 225 }