@@ -4,18 +4,18 @@ * * Common tricks and utilities for programming in D. */ module polemy.tricks; -static import std.array; -static import std.format; -static import core.exception; +import std.array : appender; +import std.format : formattedWrite; +import core.exception : onAssertErrorMsg, AssertError; /// Simple Wrapper for std.format.doFormat string sprintf(string fmt, T...)(T params) { - auto writer = std.array.appender!string(); - std.format.formattedWrite(writer, fmt, params); + auto writer = appender!string(); + formattedWrite(writer, fmt, params); return writer.data; } unittest @@ -22,8 +22,49 @@ { assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" ); assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" ); } + +/// Unittest helper that asserts an expression must throw something + +void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +{ + try { + t(); + } catch(ExceptionType) { + return; + } catch(Throwable e) { + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + } + onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); +} + +/// Unittest helper that asserts an expression must not throw anything + +void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +{ + try { + t(); + } catch(Throwable e) { + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + } +} + +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()) ); +} /// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= template assertOp(string op) @@ -32,11 +73,11 @@ { try { if( mixin("a"~op~"b") ) return; } catch(Throwable e) { - core.exception.onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); } - core.exception.onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); } } alias assertOp!(`==`) assert_eq; @@ -45,31 +86,32 @@ alias assertOp!(`<=`) assert_le; alias assertOp!(`>`) assert_gt; alias assertOp!(`>=`) assert_ge; -/// Unittest helper that asserts an expression must throw something - -void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +unittest { - try { - t(); - } catch(ExceptionType) { - return; - } catch(Throwable e) { - core.exception.onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } - core.exception.onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); -} - -/// Unittest helper that asserts an expression must not throw anything - -void assert_nothrow(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") -{ - try { - t(); - } catch(Throwable e) { - core.exception.onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } + assert_nothrow( assert_eq("foo", "foo") ); + assert_nothrow( assert_ne("foo", "bar") ); + assert_nothrow( assert_lt("bar", "foo") ); + assert_nothrow( assert_le("bar", "foo") ); + assert_nothrow( assert_le("bar", "bar") ); + assert_nothrow( assert_gt("foo", "bar") ); + assert_nothrow( assert_ge("foo", "bar") ); + assert_nothrow( assert_ge("bar", "bar") ); + + assert_throw!AssertError( assert_eq("foo", "bar") ); + assert_throw!AssertError( assert_ne("foo", "foo") ); + assert_throw!AssertError( assert_lt("foo", "foo") ); + assert_throw!AssertError( assert_lt("foo", "bar") ); + assert_throw!AssertError( assert_le("foo", "bar") ); + assert_throw!AssertError( assert_gt("bar", "bar") ); + assert_throw!AssertError( assert_gt("bar", "foo") ); + assert_throw!AssertError( assert_ge("bar", "foo") ); + + 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) ); } /* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */ @@ -106,8 +148,28 @@ assert_eq( (new Temp(1,"foo")).y, "foo" ); assert( !__traits(compiles, new Temp) ); assert( !__traits(compiles, new Temp(1)) ); assert( !__traits(compiles, new Temp("foo",1)) ); + + class Tomp : Temp + { + real z; + mixin SimpleConstructor; + } + assert_eq( (new Tomp(1,"foo",2.5)).x, 1 ); + assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" ); + assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 ); + assert( !__traits(compiles, new Tomp(3.14)) ); + + // shiyo- desu. Don't use in this way. + // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), + // but it fails because Tomp takes (int,string,real). + assert( !__traits(compiles, { + class Tamp : Tomp + { + mixin SimpleConstructor; + } + }) ); } /// Mixing-in the MOST-DERIVED-member-wise comparator for a class @@ -160,5 +222,16 @@ assert_ne( new Temp(1,"foo"), new Temp(2,"foo") ); assert_ne( new Temp(1,"foo"), new Temp(1,"bar") ); assert_gt( new Temp(1,"foo"), new Temp(1,"bar") ); assert_lt( new Temp(1,"foo"), new Temp(2,"bar") ); + assert_ge( new Temp(1,"foo"), new Temp(1,"foo") ); + + class TempDummy + { + int x; + string y; + mixin SimpleConstructor; + mixin SimpleCompare; + } + assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); + assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); }