4198578702 2010-11-07 kinaba: /** 4198578702 2010-11-07 kinaba: * Authors: k.inaba 4198578702 2010-11-07 kinaba: * License: NYSL 0.9982 http://www.kmonos.net/nysl/ 4198578702 2010-11-07 kinaba: * 4198578702 2010-11-07 kinaba: * Common tricks and utilities for programming in D. 423f308350 2010-11-07 kinaba: */ 4198578702 2010-11-07 kinaba: module polemy.tricks; b0d8d7875b 2010-11-08 kinaba: import std.array : appender; b0d8d7875b 2010-11-08 kinaba: import std.format : formattedWrite; b0d8d7875b 2010-11-08 kinaba: import core.exception : onAssertErrorMsg, AssertError; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Simple Wrapper for std.format.doFormat 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: string sprintf(string fmt, T...)(T params) 423f308350 2010-11-07 kinaba: { b0d8d7875b 2010-11-08 kinaba: auto writer = appender!string(); b0d8d7875b 2010-11-08 kinaba: formattedWrite(writer, fmt, params); 423f308350 2010-11-07 kinaba: return writer.data; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" ); 423f308350 2010-11-07 kinaba: assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" ); b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: /// Unittest helper that asserts an expression must throw something b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: try { b0d8d7875b 2010-11-08 kinaba: t(); b0d8d7875b 2010-11-08 kinaba: } catch(ExceptionType) { b0d8d7875b 2010-11-08 kinaba: return; b0d8d7875b 2010-11-08 kinaba: } catch(Throwable e) { b0d8d7875b 2010-11-08 kinaba: onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: /// Unittest helper that asserts an expression must not throw anything b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: try { b0d8d7875b 2010-11-08 kinaba: t(); b0d8d7875b 2010-11-08 kinaba: } catch(Throwable e) { b0d8d7875b 2010-11-08 kinaba: onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: unittest b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: auto error = {throw new Error("hello");}; b0d8d7875b 2010-11-08 kinaba: auto nothing = (){}; b0d8d7875b 2010-11-08 kinaba: auto assertError = {assert(0);}; b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: assert_nothrow ( assert_nothrow(nothing()) ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_nothrow(error()) ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_nothrow(assertError()) ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: assert_nothrow ( assert_throw!Error(error()) ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_throw!Error(nothing()) ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow ( assert_throw!Error(assertError()) ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_throw!AssertError(error()) ); 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: 61998c472a 2010-11-08 kinaba: /// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= 61998c472a 2010-11-08 kinaba: 61998c472a 2010-11-08 kinaba: template assertOp(string op) 61998c472a 2010-11-08 kinaba: { 61998c472a 2010-11-08 kinaba: void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="") 61998c472a 2010-11-08 kinaba: { 61998c472a 2010-11-08 kinaba: try { 61998c472a 2010-11-08 kinaba: if( mixin("a"~op~"b") ) return; 61998c472a 2010-11-08 kinaba: } catch(Throwable e) { b0d8d7875b 2010-11-08 kinaba: onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); 61998c472a 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: 61998c472a 2010-11-08 kinaba: alias assertOp!(`==`) assert_eq; 61998c472a 2010-11-08 kinaba: alias assertOp!(`!=`) assert_ne; 61998c472a 2010-11-08 kinaba: alias assertOp!(`<`) assert_lt; 61998c472a 2010-11-08 kinaba: alias assertOp!(`<=`) assert_le; 61998c472a 2010-11-08 kinaba: alias assertOp!(`>`) assert_gt; 61998c472a 2010-11-08 kinaba: alias assertOp!(`>=`) assert_ge; 61998c472a 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: unittest 61998c472a 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_eq("foo", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_ne("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_lt("bar", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_le("bar", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_le("bar", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_gt("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_ge("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow( assert_ge("bar", "bar") ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_eq("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_ne("foo", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_lt("foo", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_lt("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_le("foo", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_gt("bar", "bar") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_gt("bar", "foo") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_ge("bar", "foo") ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: class Temp { bool opEquals(int x){return x/x==x;} } b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_eq(new Temp, 0) ); b0d8d7875b 2010-11-08 kinaba: assert_nothrow ( assert_eq(new Temp, 1) ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( assert_eq(new Temp, 2) ); 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: 61998c472a 2010-11-08 kinaba: /* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */ 61998c472a 2010-11-08 kinaba: 423f308350 2010-11-07 kinaba: /// Mixing-in the bean constructor for a class 423f308350 2010-11-07 kinaba: 61998c472a 2010-11-08 kinaba: template SimpleConstructor() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: static if( is(typeof(super) == Object) || super.tupleof.length==0 ) 423f308350 2010-11-07 kinaba: this( typeof(this.tupleof) params ) 423f308350 2010-11-07 kinaba: { 0569f7b8c2 2010-11-07 kinaba: static if(this.tupleof.length>0) 0569f7b8c2 2010-11-07 kinaba: this.tupleof = params; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: else 423f308350 2010-11-07 kinaba: this( typeof(super.tupleof) ps, typeof(this.tupleof) params ) 423f308350 2010-11-07 kinaba: { 61998c472a 2010-11-08 kinaba: // including (only) the direct super class members 61998c472a 2010-11-08 kinaba: // may not always be a desirable choice, but should work for many cases 423f308350 2010-11-07 kinaba: super(ps); 0569f7b8c2 2010-11-07 kinaba: static if(this.tupleof.length>0) 0569f7b8c2 2010-11-07 kinaba: this.tupleof = params; 0569f7b8c2 2010-11-07 kinaba: } 0569f7b8c2 2010-11-07 kinaba: } 0569f7b8c2 2010-11-07 kinaba: 61998c472a 2010-11-08 kinaba: unittest 61998c472a 2010-11-08 kinaba: { 61998c472a 2010-11-08 kinaba: class Temp 61998c472a 2010-11-08 kinaba: { 61998c472a 2010-11-08 kinaba: int x; 61998c472a 2010-11-08 kinaba: string y; 61998c472a 2010-11-08 kinaba: mixin SimpleConstructor; 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: assert_eq( (new Temp(1,"foo")).x, 1 ); 61998c472a 2010-11-08 kinaba: assert_eq( (new Temp(1,"foo")).y, "foo" ); 61998c472a 2010-11-08 kinaba: assert( !__traits(compiles, new Temp) ); 61998c472a 2010-11-08 kinaba: assert( !__traits(compiles, new Temp(1)) ); 61998c472a 2010-11-08 kinaba: assert( !__traits(compiles, new Temp("foo",1)) ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: class Tomp : Temp b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: real z; b0d8d7875b 2010-11-08 kinaba: mixin SimpleConstructor; b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).x, 1 ); b0d8d7875b 2010-11-08 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" ); b0d8d7875b 2010-11-08 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 ); b0d8d7875b 2010-11-08 kinaba: assert( !__traits(compiles, new Tomp(3.14)) ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: // shiyo- desu. Don't use in this way. b0d8d7875b 2010-11-08 kinaba: // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), b0d8d7875b 2010-11-08 kinaba: // but it fails because Tomp takes (int,string,real). b0d8d7875b 2010-11-08 kinaba: assert( !__traits(compiles, { b0d8d7875b 2010-11-08 kinaba: class Tamp : Tomp b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: mixin SimpleConstructor; b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: }) ); 61998c472a 2010-11-08 kinaba: } 61998c472a 2010-11-08 kinaba: 820e7198cc 2010-11-07 kinaba: /// Mixing-in the MOST-DERIVED-member-wise comparator for a class 423f308350 2010-11-07 kinaba: 61998c472a 2010-11-08 kinaba: template SimpleCompare() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: override bool opEquals(Object rhs_) const 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( auto rhs = cast(typeof(this))rhs_ ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: foreach(i,_; this.tupleof) 423f308350 2010-11-07 kinaba: if( this.tupleof[i] != rhs.tupleof[i] ) 423f308350 2010-11-07 kinaba: return false; 423f308350 2010-11-07 kinaba: return true; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: override hash_t toHash() const 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: hash_t h = 0; 423f308350 2010-11-07 kinaba: foreach(mem; this.tupleof) 423f308350 2010-11-07 kinaba: h += typeid(mem).getHash(&mem); 423f308350 2010-11-07 kinaba: return h; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: override int opCmp(Object rhs_) const 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( auto rhs = cast(typeof(this))rhs_ ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: foreach(i,_; this.tupleof) 423f308350 2010-11-07 kinaba: if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i])) 423f308350 2010-11-07 kinaba: return c; 423f308350 2010-11-07 kinaba: return 0; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: class Temp 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: int x; 423f308350 2010-11-07 kinaba: string y; 423f308350 2010-11-07 kinaba: mixin SimpleConstructor; 423f308350 2010-11-07 kinaba: mixin SimpleCompare; 423f308350 2010-11-07 kinaba: } 61998c472a 2010-11-08 kinaba: assert_eq( new Temp(1,"foo"), new Temp(1,"foo") ); 61998c472a 2010-11-08 kinaba: assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash ); 61998c472a 2010-11-08 kinaba: assert_ne( new Temp(1,"foo"), new Temp(2,"foo") ); 61998c472a 2010-11-08 kinaba: assert_ne( new Temp(1,"foo"), new Temp(1,"bar") ); 61998c472a 2010-11-08 kinaba: assert_gt( new Temp(1,"foo"), new Temp(1,"bar") ); 61998c472a 2010-11-08 kinaba: assert_lt( new Temp(1,"foo"), new Temp(2,"bar") ); b0d8d7875b 2010-11-08 kinaba: assert_ge( new Temp(1,"foo"), new Temp(1,"foo") ); b0d8d7875b 2010-11-08 kinaba: b0d8d7875b 2010-11-08 kinaba: class TempDummy b0d8d7875b 2010-11-08 kinaba: { b0d8d7875b 2010-11-08 kinaba: int x; b0d8d7875b 2010-11-08 kinaba: string y; b0d8d7875b 2010-11-08 kinaba: mixin SimpleConstructor; b0d8d7875b 2010-11-08 kinaba: mixin SimpleCompare; b0d8d7875b 2010-11-08 kinaba: } b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); b0d8d7875b 2010-11-08 kinaba: assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); 423f308350 2010-11-07 kinaba: }