1 module des.util.testsuite;
2 
3 import std.traits;
4 import std.typetuple;
5 import std.math;
6 
7 ///
8 template isElemHandler(A)
9 {
10     enum isElemHandler = !is( Unqual!A == void[] ) &&
11                           is( typeof(A.init[0]) ) &&
12                          !is( Unqual!(typeof(A.init[0])) == void ) &&
13                           is( typeof( A.init.length ) == size_t );
14 }
15 
16 ///
17 unittest
18 {
19     static assert(  isElemHandler!(int[]) );
20     static assert(  isElemHandler!(float[]) );
21     static assert(  isElemHandler!(string) );
22     static assert( !isElemHandler!int );
23     static assert( !isElemHandler!float );
24     static assert( !isElemHandler!(immutable(void)[]) );
25 }
26 
27 ///
28 bool eq(A,B)( in A a, in B b ) pure
29 {
30     static if( allSatisfy!(isElemHandler,A,B) )
31     {
32         if( a.length != b.length ) return false;
33         foreach( i; 0 .. a.length )
34             if( !eq(a[i],b[i]) ) return false;
35         return true;
36     }
37     else static if( allSatisfy!(isNumeric,A,B) && anySatisfy!(isFloatingPoint,A,B) )
38     {
39         static if( isFloatingPoint!A && !isFloatingPoint!B )
40             auto epsilon = A.epsilon;
41         else static if( isFloatingPoint!B && !isFloatingPoint!A )
42             auto epsilon = B.epsilon;
43         else
44             auto epsilon = fmax( A.epsilon, B.epsilon );
45         return abs(a-b) < epsilon;
46     }
47     else return a == b;
48 }
49 
50 ///
51 unittest
52 {
53     assert(  eq( 1, 1.0 ) );
54     assert(  eq( "hello", "hello"w ) );
55     assert( !eq( cast(void[])"hello", cast(void[])"hello"w ) );
56     assert(  eq( cast(void[])"hello", cast(void[])"hello" ) );
57     assert(  eq( cast(void[])"hello", "hello" ) );
58     assert( !eq( cast(void[])"hello", "hello"w ) );
59     assert(  eq( [[1,2],[3,4]], [[1.0f,2],[3.0f,4]] ) );
60     assert( !eq( [[1,2],[3,4]], [[1.1f,2],[3.0f,4]] ) );
61     assert( !eq( [[1,2],[3,4]], [[1.0f,2],[3.0f]] ) );
62     assert(  eq( [1,2,3], [1.0,2,3] ) );
63     assert(  eq( [1.0f,2,3], [1.0,2,3] ) );
64     assert(  eq( [1,2,3], [1,2,3] ) );
65     assert( !eq( [1.0000001,2,3], [1,2,3] ) );
66     assert(  eq( ["hello","world"], ["hello","world"] ) );
67     assert( !eq( "hello", [1,2,3] ) );
68     static assert( !__traits(compiles, eq(["hello"],1)) );
69     static assert( !__traits(compiles, eq(["hello"],[1,2,3])) );
70 }
71 
72 ///
73 bool eq_approx(A,B,E)( in A a, in B b, in E eps ) pure
74     if( allSatisfy!(isNumeric,A,B,E) || allSatisfy!(isElemHandler,A,B) )
75 {
76     static if( allSatisfy!(isElemHandler,A,B) )
77     {
78         if( a.length != b.length ) return false;
79         foreach( i; 0 .. a.length )
80             if( !eq_approx(a[i],b[i],eps) ) return false;
81         return true;
82     }
83     else return abs(a-b) < eps;
84 }
85 
86 ///
87 unittest
88 {
89     assert(  eq_approx( [1.1f,2,3], [1,2,3], 0.2 ) );
90     assert( !eq_approx( [1.1f,2,3], [1,2,3], 0.1 ) );
91     assert( !eq_approx( [1.0f,2], [1,2,3], 1 ) );
92 }
93 
94 ///
95 bool mustExcept(E=Exception)( void delegate() fnc, bool throwUnexpected=false )
96 if( is( E : Throwable ) )
97 in { assert( fnc ); } body
98 {
99     static if( !is( E == Throwable ) )
100     {
101         try fnc();
102         catch( E e ) return true;
103         catch( Throwable t )
104             if( throwUnexpected ) throw t;
105         return false;
106     }
107     else
108     {
109         try fnc();
110         catch( Throwable t ) return true;
111         return false;
112     }
113 }
114 
115 ///
116 unittest
117 {
118     assert( mustExcept!Exception( { throw new Exception("test"); } ) );
119     assert( !mustExcept!Exception( { throw new Throwable("test"); } ) );
120     assert( !mustExcept!Exception( { auto a = 4; } ) );
121 }