The OpenD Programming Language

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 }