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 }