1 /++ 2 Testing utilities 3 4 Authors: Ilya Yaroshenko 5 +/ 6 module mir.test; 7 8 import mir.exception: MirError; 9 10 private noreturn assumeAllAttrAndCall(scope const void delegate() t) 11 @nogc pure nothrow @trusted { 12 (cast(const void delegate() @safe pure nothrow @nogc) t)(); 13 assert(0); 14 } 15 16 /// 17 struct ShouldApprox(T, F = T) 18 if ((__traits(isFloating, T) || __traits(hasMember, T, "approxEqual")) && __traits(isFloating, F)) 19 { 20 /// 21 T value; 22 /// 23 F maxRelDiff = 0x1p-20f; 24 /// 25 F maxAbsDiff = 0x1p-20f; 26 27 /// 28 void opEquals(T expected, string file = __FILE__, int line = __LINE__) @safe pure nothrow @nogc 29 { 30 import mir.format: stringBuf, getData; 31 import mir.math.common: approxEqual; 32 if (value.approxEqual(expected, maxRelDiff, maxAbsDiff)) 33 return; 34 auto buf = stringBuf; 35 assumeAllAttrAndCall({ 36 throw new MirError(buf 37 << "expected approximately " << expected 38 << ", got " << value 39 << ", maxRelDiff = " << maxRelDiff 40 << ", maxAbsDiff = " << maxAbsDiff 41 << getData, file, line); 42 }); 43 assert(0); 44 } 45 } 46 47 /// ditto 48 ShouldApprox!T shouldApprox(T)(const T value, const T maxRelDiff = T(0x1p-20f), const T maxAbsDiff = T(0x1p-20f)) 49 if (__traits(isFloating, T)) 50 { 51 return typeof(return)(value, maxRelDiff, maxAbsDiff); 52 } 53 54 /// 55 version(mir_test) 56 unittest 57 { 58 1.0.shouldApprox == 1 + 9e-7; 59 shouldApprox(1 + 9e-7, 1e-6, 1e-6) == 1; 60 } 61 62 /// ditto 63 ShouldApprox!(T, F) shouldApprox(T, F)(const T value, const F maxRelDiff = double(0x1p-20f), const F maxAbsDiff = double(0x1p-20f)) 64 if (__traits(hasMember, T, "approxEqual") && __traits(isFloating, F)) 65 { 66 return typeof(return)(value, maxRelDiff, maxAbsDiff); 67 } 68 69 /// 70 version(mir_test) 71 unittest 72 { 73 static struct C { 74 double re, im; 75 auto approxEqual(C rhs, double maxRelDiff, double maxAbsDiff) 76 { 77 import mir.math.common: approxEqual; 78 return approxEqual(re, rhs.re, maxRelDiff, maxAbsDiff) && approxEqual(im, rhs.im, maxRelDiff, maxAbsDiff); 79 } 80 } 81 C(1.0, 1.0).shouldApprox == C(1 + 9e-7, 1 - 9e-7); 82 } 83 84 /// 85 struct Should(T) 86 { 87 /// 88 T value; 89 90 static if(!is(immutable T == immutable ubyte[])) 91 /// 92 void opEquals(R)(const R expected, string file = __FILE__, int line = __LINE__) @trusted 93 { 94 import mir.format: stringBuf, getData; 95 static if (__traits(isFloating, R)) 96 { 97 if (expected != expected && value != value) 98 return; 99 } 100 if (value == expected) 101 return; 102 auto buf = stringBuf; 103 buf << "mir.test.should:\n"; 104 buf << "expected " << expected << "\n" 105 << " got " << value; 106 assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); }); 107 } 108 else 109 /// ditto 110 void opEquals(scope const ubyte[] expected, string file = __FILE__, int line = __LINE__) @trusted 111 pure nothrow @nogc 112 { 113 import mir.format: stringBuf, getData; 114 if (value == expected) 115 return; 116 auto buf = stringBuf; 117 import mir.format: printHexArray; 118 import mir.ndslice.topology: map; 119 buf << "mir.test.should:\n"; 120 buf << "expected "; 121 buf.printHexArray(expected); 122 buf << "\n"; 123 buf << " got "; 124 buf.printHexArray( value); 125 assumeAllAttrAndCall({ throw new MirError(buf << getData, file, line); }); 126 } 127 } 128 129 /// ditto 130 Should!T should(T)(T value) 131 { 132 return typeof(return)(value); 133 } 134 135 /// 136 version(mir_test) 137 unittest 138 { 139 1.0.should == 1; 140 should(1) == 1; 141 142 ubyte[] val = [0, 2, 3]; 143 val.should == [0, 2, 3]; 144 } 145 146 /// 147 void should(alias fun = "a == b", T, R)(const T value, const R expected, string file = __FILE__, int line = __LINE__) 148 { 149 import mir.functional; 150 import mir.format: stringBuf, getData; 151 if (naryFun!fun(value, expected)) 152 return; 153 auto buf = stringBuf; 154 buf << fun.stringof 155 << " returns false" 156 << " for a = " << value 157 << ", b = " << expected; 158 throw new MirError(buf << getData, file, line); 159 } 160 161 /// 162 version(mir_test) 163 unittest 164 { 165 1.0.should!"a < b"(1.3); 166 }