1 /**
2 * Authors: k.inaba
3 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
4 *
5 * Hepler routines for unittesting.
6 * TODO: Is there any clean way to implement "assert_compiles" and "assert_not_compile"?
7 */
8 module tricks.test;
9 import std.conv : text;
10 import core.exception;
11
12 version(unittest)
13 {
14 import std.cstream;
15 import core.runtime;
16
17 static this()
18 {
19 installCustomTestRunner();
20 }
21
22 private void installCustomTestRunner()
23 {
24 Runtime.moduleUnitTester = function()
25 {
26 Throwable firstError = null;
27
28 void logError(Throwable e)
29 {
30 if(firstError is null)
31 firstError = e;
32 derr.writefln(" !! %s(%d): %s", e.file, e.line, e.msg);
33 }
34
35 void testModule(ModuleInfo* m, void function() test)
36 {
37 derr.writefln("[TEST] %s", m.name);
38 try { test(); } catch( Throwable e ) { logError(e); }
39 }
40
41 bool report()
42 {
43 if(firstError is null)
44 return true;
45 derr.writeString("[TEST] press enter to exit.");
46 din.readLine();
47 return false;
48 }
49
50 foreach(m; ModuleInfo)
51 if(m && m.unitTest)
52 testModule(m, m.unitTest);
53 return report();
54 };
55 }
56 }
57
58 /// Unittest helper that asserts an expression must throw something
59
60 void assert_throw(ExcT=Throwable, T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
61 {
62 static if( is(ExcT == Throwable) )
63 try
64 { t(); }
65 catch(ExcT)
66 { return; }
67 else
68 try
69 { t(); }
70 catch(ExcT)
71 { return; }
72 catch(Throwable e)
73 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
74 onAssertErrorMsg(fn, ln, msg.length ? msg : "not thrown");
75 }
76
77 /// Unittest helper that asserts an expression must not throw anything
78
79 T assert_nothrow(T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
80 {
81 try
82 { return t(); }
83 catch(Throwable e)
84 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
85 assert(false);
86 }
87
88 unittest
89 {
90 auto error = {throw new Error("hello");};
91 auto nothing = (){};
92 auto assertError = {assert(0);};
93
94 assert_nothrow ( assert_nothrow(nothing()) );
95 assert_throw!AssertError( assert_nothrow(error()) );
96 assert_throw!AssertError( assert_nothrow(assertError()) );
97
98 assert_nothrow ( assert_throw!Error(error()) );
99 assert_throw!AssertError( assert_throw!Error(nothing()) );
100 assert_nothrow ( assert_throw!Error(assertError()) );
101 assert_throw!AssertError( assert_throw!AssertError(error()) );
102 }
103
104 template assertOp(string op)
105 {
106 void assertOp(A, B, string fn=__FILE__, size_t ln=__LINE__)(lazy A a_, lazy B b_, string msg="")
107 {
108 try
109 { A a=a_(); B b=b_(); if( mixin("a"~op~"b") ) return;
110 onAssertErrorMsg(fn, ln, msg.length ? msg : text(a, " !", op, " ", b)); }
111 catch(Throwable e)
112 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
113 assert(false);
114 }
115 }
116
117 alias assertOp!(`==`) assert_eq; /// asserts two operands are ==
118 alias assertOp!(`!=`) assert_ne; /// asserts two operands are !=
119 alias assertOp!(`<`) assert_lt; /// asserts two operands are <
120 alias assertOp!(`<=`) assert_le; /// asserts two operands are <=
121 alias assertOp!(`>`) assert_gt; /// asserts two operands are >
122 alias assertOp!(`>=`) assert_ge; /// asserts two operands are >=
123
124 unittest
125 {
126 assert_nothrow( assert_eq(1, 1) );
127 assert_nothrow( assert_ne(1, 0) );
128 assert_nothrow( assert_lt(0, 1) );
129 assert_nothrow( assert_le(0, 1) );
130 assert_nothrow( assert_le(0, 0) );
131 assert_nothrow( assert_gt(1, 0) );
132 assert_nothrow( assert_ge(1, 0) );
133 assert_nothrow( assert_ge(0, 0) );
134
135 assert_throw!AssertError( assert_eq(1, 0) );
136 assert_throw!AssertError( assert_ne(1, 1) );
137 assert_throw!AssertError( assert_lt(1, 1) );
138 assert_throw!AssertError( assert_lt(1, 0) );
139 assert_throw!AssertError( assert_le(1, 0) );
140 assert_throw!AssertError( assert_gt(0, 0) );
141 assert_throw!AssertError( assert_gt(0, 1) );
142 assert_throw!AssertError( assert_ge(0, 1) );
143
144 class Temp { bool opEquals(int x){return x/x==x;} }
145 assert_throw!AssertError( assert_eq(new Temp, 0) );
146 assert_nothrow ( assert_eq(new Temp, 1) );
147 assert_throw!AssertError( assert_eq(new Temp, 2) );
148 }