1 module des.util.testsuite;
2
3 import std.traits;
4 import std.typetuple;
5 import std.math;
6
7 import std.stdio;
8 import std..string;
9 import std.exception;
10 import core.exception : AssertError;
11
12 ///
13 template isElemHandler(A)
14 {
15 enum isElemHandler = !is( Unqual!A == void[] ) &&
16 is( typeof(A.init[0]) ) &&
17 !is( Unqual!(typeof(A.init[0])) == void ) &&
18 is( typeof( A.init.length ) == size_t );
19 }
20
21 ///
22 unittest
23 {
24 static assert( isElemHandler!(int[]) );
25 static assert( isElemHandler!(float[]) );
26 static assert( isElemHandler!(string) );
27 static assert( !isElemHandler!int );
28 static assert( !isElemHandler!float );
29 static assert( !isElemHandler!(immutable(void)[]) );
30 }
31
32 ///
33 bool eq(A,B)( in A a, in B b ) pure
34 {
35 static if( allSatisfy!(isElemHandler,A,B) )
36 {
37 if( a.length != b.length ) return false;
38 foreach( i; 0 .. a.length )
39 if( !eq(a[i],b[i]) ) return false;
40 return true;
41 }
42 else static if( ( is( A == class ) || is( A == interface ) ) &&
43 ( is( B == class ) || is( B == interface ) ) )
44 return a is b;
45 else static if( allSatisfy!(isNumeric,A,B) && anySatisfy!(isFloatingPoint,A,B) )
46 {
47 static if( isFloatingPoint!A && !isFloatingPoint!B )
48 auto epsilon = A.epsilon;
49 else static if( isFloatingPoint!B && !isFloatingPoint!A )
50 auto epsilon = B.epsilon;
51 else
52 auto epsilon = fmax( A.epsilon, B.epsilon );
53 return abs(a-b) < epsilon;
54 }
55 else return a == b;
56 }
57
58 ///
59 unittest
60 {
61 assert( eq( 1, 1.0 ) );
62 assert( eq( "hello", "hello"w ) );
63 assert( !eq( cast(void[])"hello", cast(void[])"hello"w ) );
64 assert( eq( cast(void[])"hello", cast(void[])"hello" ) );
65 assert( eq( cast(void[])"hello", "hello" ) );
66 assert( !eq( cast(void[])"hello", "hello"w ) );
67 assert( eq( [[1,2],[3,4]], [[1.0f,2],[3.0f,4]] ) );
68 assert( !eq( [[1,2],[3,4]], [[1.1f,2],[3.0f,4]] ) );
69 assert( !eq( [[1,2],[3,4]], [[1.0f,2],[3.0f]] ) );
70 assert( eq( [1,2,3], [1.0,2,3] ) );
71 assert( eq( [1.0f,2,3], [1.0,2,3] ) );
72 assert( eq( [1,2,3], [1,2,3] ) );
73 assert( !eq( [1.0000001,2,3], [1,2,3] ) );
74 assert( eq( ["hello","world"], ["hello","world"] ) );
75 assert( !eq( "hello", [1,2,3] ) );
76 static assert( !__traits(compiles, eq(["hello"],1)) );
77 static assert( !__traits(compiles, eq(["hello"],[1,2,3])) );
78 }
79
80 ///
81 bool eq_approx(A,B,E)( in A a, in B b, in E eps ) pure
82 if( allSatisfy!(isNumeric,A,B,E) || allSatisfy!(isElemHandler,A,B) )
83 {
84 static if( allSatisfy!(isElemHandler,A,B) )
85 {
86 if( a.length != b.length ) return false;
87 foreach( i; 0 .. a.length )
88 if( !eq_approx(a[i],b[i],eps) ) return false;
89 return true;
90 }
91 else return abs(a-b) < eps;
92 }
93
94 ///
95 unittest
96 {
97 assert( eq_approx( [1.1f,2,3], [1,2,3], 0.2 ) );
98 assert( !eq_approx( [1.1f,2,3], [1,2,3], 0.1 ) );
99 assert( !eq_approx( [1.0f,2], [1,2,3], 1 ) );
100 }
101
102 ///
103 bool mustExcept(E=Exception)( void delegate() fnc, bool throwUnexpected=false )
104 if( is( E : Throwable ) )
105 in { assert( fnc ); } body
106 {
107 static if( !is( E == Throwable ) )
108 {
109 try fnc();
110 catch( E e ) return true;
111 catch( Throwable t )
112 if( throwUnexpected ) throw t;
113 return false;
114 }
115 else
116 {
117 try fnc();
118 catch( Throwable t ) return true;
119 return false;
120 }
121 }
122
123 ///
124 unittest
125 {
126 assert( mustExcept!Exception( { throw new Exception("test"); } ) );
127 assert( !mustExcept!Exception( { throw new Throwable("test"); } ) );
128 assert( !mustExcept!Exception( { auto a = 4; } ) );
129 }
130
131 import std.conv : to;
132
133 string toSF(T)( in T val )
134 {
135 static if( is( typeof( to!string( val ) )) )
136 return to!string( val );
137 else static if( isArray!T )
138 {
139 string[] rr;
140 foreach( v; val )
141 rr ~= toSF( v );
142 return "[ " ~ rr.join(", ") ~ " ]";
143 }
144 else static if( is( T == typeof(null) ) ) return null;
145 else static if( is( T == interface ) || is( T == class ) )
146 {
147 if( val is null ) return "null";
148 else return to!string( cast(void*)val );
149 }
150 else return val.stringof;
151 }
152
153 unittest
154 {
155 assert( eq( toSF([0,4]), "[0, 4]" ) );
156 assert( eq( toSF(null), "null" ) );
157 assert( eq( toSF(0), "0" ) );
158
159 Object a = null;
160 assert( eq( toSF(a), "null" ) );
161 }
162
163 auto throwError(Args...)( string file, size_t line, string fmt, Args args )
164 { return new AssertError( format( fmt, args ), file, line ); }
165
166 ///
167 void assertEq(A,B,string file=__FILE__,size_t line=__LINE__)( in A a, in B b, lazy string fmt="" )
168 if( is( typeof( eq(a,b) ) ) )
169 {
170 enforce( eq(a,b), throwError( file, line,
171 ( fmt.length ? fmt : "assertEq fails: %s != %s" ),
172 toSF(a), toSF(b) ) );
173 }
174
175 ///
176 void assertNotEq(A,B,string file=__FILE__,size_t line=__LINE__)( in A a, in B b, lazy string fmt="" )
177 if( is( typeof( eq(a,b) ) ) )
178 {
179 enforce( !eq(a,b), throwError( file, line,
180 ( fmt.length ? fmt : "assertNotEq fails: %s == %s" ),
181 toSF(a), toSF(b) ) );
182 }
183
184 ///
185 void assertNull(A,string file=__FILE__,size_t line=__LINE__)( in A a, lazy string fmt="" )
186 {
187 enforce( a is null, throwError( file, line,
188 ( fmt.length ? fmt : "assertNull fails: %s !is null" ),
189 toSF(a) ) );
190 }
191
192 ///
193 void assertNotNull(A,string file=__FILE__,size_t line=__LINE__)( in A a, lazy string fmt="" )
194 {
195 enforce( a !is null, throwError( file, line,
196 ( fmt.length ? fmt : "assertNotNull fails: %s is null" ),
197 toSF(a) ) );
198 }