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