@@ -1,14 +1,15 @@ -/** +/** * Authors: k.inaba * License: NYSL 0.9982 http://www.kmonos.net/nysl/ * * Common tricks and utilities for programming in D. */ -module polemy.tricks; +module tricks.tricks; +import tricks.test; import std.array : appender; -import std.format : formattedWrite; -import core.exception : onAssertErrorMsg, AssertError; +import std.format : formattedWrite; +import core.exception : AssertError; /// Simple Wrapper for std.format.doFormat string sprintf(string fmt, T...)(T params) @@ -23,102 +24,28 @@ 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 +/// Create an exception with automatically completed filename and lineno information -void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +auto genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params) { - 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)); - } + static if( T.length > 0 && is(T[$-1] : Throwable) ) + return new ExceptionType(params[0..$-1], fn, ln, params[$-1]); + else + return new ExceptionType(params, fn, ln); } 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()) ); + assert_ne( genex!Exception("msg").file, "" ); + assert_ne( genex!Exception("msg").line, 0 ); + assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init ); } -/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= - -template assertOp(string op) -{ - void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="") - { - try { - if( mixin("a"~op~"b") ) return; - } catch(Throwable e) { - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); - } -} - -alias assertOp!(`==`) assert_eq; -alias assertOp!(`!=`) assert_ne; -alias assertOp!(`<`) assert_lt; -alias assertOp!(`<=`) assert_le; -alias assertOp!(`>`) assert_gt; -alias assertOp!(`>=`) assert_ge; - -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) ); -} - -/* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */ - /// Mixing-in the bean constructor for a class -/*mixin*/ +/*mixin*/ template SimpleConstructor() { static if( is(typeof(super) == Object) || super.tupleof.length==0 ) this( typeof(this.tupleof) params ) @@ -173,9 +100,9 @@ } /// Mixing-in the MOST-DERIVED-member-wise comparator for a class -/*mixin*/ +/*mixin*/ template SimpleCompare() { override bool opEquals(Object rhs_) const { @@ -236,48 +163,48 @@ } assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); } - -/// Mixing-in a simple toString method - -/*mixin*/ -template SimpleToString() -{ - override string toString() - { - string str = sprintf!"%s("(typeof(this).stringof); - foreach(i,mem; this.tupleof) - { - if(i) str ~= ","; - static if( is(typeof(mem) == std.bigint.BigInt) ) - str ~= std.bigint.toDecimalString(mem); - else - str ~= sprintf!"%s"(mem); - } - return str ~ ")"; - } -} - -version(unittest) import std.bigint; -unittest -{ - class Temp - { - int x; - string y; - BigInt z; - mixin SimpleConstructor; - mixin SimpleToString; - } - assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" ); -} - -/// Everything is in - -/*mixin*/ -template SimpleClass() -{ - mixin SimpleConstructor; - mixin SimpleCompare; - mixin SimpleToString; -} + +/// Mixing-in a simple toString method + +/*mixin*/ +template SimpleToString() +{ + override string toString() + { + string str = sprintf!"%s("(typeof(this).stringof); + foreach(i,mem; this.tupleof) + { + if(i) str ~= ","; + static if( is(typeof(mem) == std.bigint.BigInt) ) + str ~= std.bigint.toDecimalString(mem); + else + str ~= sprintf!"%s"(mem); + } + return str ~ ")"; + } +} + +version(unittest) import std.bigint; +unittest +{ + class Temp + { + int x; + string y; + BigInt z; + mixin SimpleConstructor; + mixin SimpleToString; + } + assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" ); +} + +/// Everything is in + +/*mixin*/ +template SimpleClass() +{ + mixin SimpleConstructor; + mixin SimpleCompare; + mixin SimpleToString; +}