/**
* Authors: k.inaba
* License: NYSL 0.9982 http://www.kmonos.net/nysl/
*
* Unittest helpers.
* TODO: use stderr instead of stdout. the problem is that std.cstream is xxxx....
* TODO: is there any way to clearnly implement "assert_compiles" and "assert_not_compile"?
*/
module tricks.test;
import std.conv : to;
import core.exception;
version(unittest)
{
import std.stdio;
import core.runtime;
// Install custom test runner
static this()
{
Runtime.moduleUnitTester = function()
{
Throwable ee = null;
size_t failed=0;
foreach(m; ModuleInfo) if(m) if(auto fp=m.unitTest)
{
writeln("[TEST] ", m.name);
try {
fp();
} catch( Throwable e ) {
if(ee is null)
ee = e;
failed++;
writeln(" !! ",e.file,"(",e.line,"): ",e.msg);
}
}
if(ee !is null)
{
writeln("[TEST] ",failed," modules failed. The first error was:");
writeln(ee);
write("[TEST] press enter to exit.");
readln();
return false;
}
return true;
};
}
}
/// Unittest helper that asserts an expression must throw something
void assert_throw(ExcT=Throwable, T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
{
static if( is(ExcT == Throwable) )
try
{ t(); }
catch(ExcT)
{ return; }
else
try
{ t(); }
catch(ExcT)
{ return; }
catch(Throwable e)
{ onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
onAssertErrorMsg(fn, ln, msg.length ? msg : "not thrown");
}
/// Unittest helper that asserts an expression must not throw anything
T assert_nothrow(T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
{
try
{ return t(); }
catch(Throwable e)
{ onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
assert(false);
}
unittest
{
auto error = {throw new Error("hello");};
auto nothing = (){};
auto assertError = {assert(0);};
assert_nothrow ( assert_nothrow(nothing()) );
assert_throw!AssertError( assert_nothrow(error()) );
assert_throw!AssertError( assert_nothrow(assertError()) );
assert_nothrow ( assert_throw!Error(error()) );
assert_throw!AssertError( assert_throw!Error(nothing()) );
assert_nothrow ( assert_throw!Error(assertError()) );
assert_throw!AssertError( assert_throw!AssertError(error()) );
}
template assertOp(string op)
{
void assertOp(A, B, string fn=__FILE__, size_t ln=__LINE__)(A a, B b, string msg="")
{
try
{ if( mixin("a"~op~"b") ) return; }
catch(Throwable e)
{ onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
onAssertErrorMsg(fn, ln, msg.length ? msg : to!string(a)~" !"~op~" "~to!string(b));
}
}
alias assertOp!(`==`) assert_eq; /// asserts two operands are ==
alias assertOp!(`!=`) assert_ne; /// asserts two operands are !=
alias assertOp!(`<`) assert_lt; /// asserts two operands are <
alias assertOp!(`<=`) assert_le; /// asserts two operands are <=
alias assertOp!(`>`) assert_gt; /// asserts two operands are >
alias assertOp!(`>=`) assert_ge; /// asserts two operands are >=
unittest
{
assert_nothrow( assert_eq(1, 1) );
assert_nothrow( assert_ne(1, 0) );
assert_nothrow( assert_lt(0, 1) );
assert_nothrow( assert_le(0, 1) );
assert_nothrow( assert_le(0, 0) );
assert_nothrow( assert_gt(1, 0) );
assert_nothrow( assert_ge(1, 0) );
assert_nothrow( assert_ge(0, 0) );
assert_throw!AssertError( assert_eq(1, 0) );
assert_throw!AssertError( assert_ne(1, 1) );
assert_throw!AssertError( assert_lt(1, 1) );
assert_throw!AssertError( assert_lt(1, 0) );
assert_throw!AssertError( assert_le(1, 0) );
assert_throw!AssertError( assert_gt(0, 0) );
assert_throw!AssertError( assert_gt(0, 1) );
assert_throw!AssertError( assert_ge(0, 1) );
class Temp { bool opEquals(int x){return x/x==x;} }
assert_throw!AssertError( assert_eq(new Temp, 0) );
assert_nothrow ( assert_eq(new Temp, 1) );
assert_throw!AssertError( assert_eq(new Temp, 2) );
}