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 }