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.writefln("[TEST] The first error was:\n%s", firstError);
46 derr.writeString("[TEST] press enter to exit.");
47 din.readLine();
48 return false;
49 }
50
51 foreach(m; ModuleInfo)
52 if(m && m.unitTest)
53 testModule(m, m.unitTest);
54 return report();
55 };
56 }
57 }
58
59 /// Unittest helper that asserts an expression must throw something
60
61 void assert_throw(ExcT=Throwable, T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
62 {
63 static if( is(ExcT == Throwable) )
64 try
65 { t(); }
66 catch(ExcT)
67 { return; }
68 else
69 try
70 { t(); }
71 catch(ExcT)
72 { return; }
73 catch(Throwable e)
74 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
75 onAssertErrorMsg(fn, ln, msg.length ? msg : "not thrown");
76 }
77
78 /// Unittest helper that asserts an expression must not throw anything
79
80 T assert_nothrow(T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="")
81 {
82 try
83 { return t(); }
84 catch(Throwable e)
85 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
86 assert(false);
87 }
88
89 unittest
90 {
91 auto error = {throw new Error("hello");};
92 auto nothing = (){};
93 auto assertError = {assert(0);};
94
95 assert_nothrow ( assert_nothrow(nothing()) );
96 assert_throw!AssertError( assert_nothrow(error()) );
97 assert_throw!AssertError( assert_nothrow(assertError()) );
98
99 assert_nothrow ( assert_throw!Error(error()) );
100 assert_throw!AssertError( assert_throw!Error(nothing()) );
101 assert_nothrow ( assert_throw!Error(assertError()) );
102 assert_throw!AssertError( assert_throw!AssertError(error()) );
103 }
104
105 template assertOp(string op)
106 {
107 void assertOp(A, B, string fn=__FILE__, size_t ln=__LINE__)(lazy A a_, lazy B b_, string msg="")
108 {
109 try
110 { A a=a_(); B b=b_(); if( mixin("a"~op~"b") ) return;
111 onAssertErrorMsg(fn, ln, msg.length ? msg : text(a, " !", op, " ", b)); }
112 catch(Throwable e)
113 { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); }
114 assert(false);
115 }
116 }
117
118 alias assertOp!(`==`) assert_eq; /// asserts two operands are ==
119 alias assertOp!(`!=`) assert_ne; /// asserts two operands are !=
120 alias assertOp!(`<`) assert_lt; /// asserts two operands are <
121 alias assertOp!(`<=`) assert_le; /// asserts two operands are <=
122 alias assertOp!(`>`) assert_gt; /// asserts two operands are >
123 alias assertOp!(`>=`) assert_ge; /// asserts two operands are >=
124
125 unittest
126 {
127 assert_nothrow( assert_eq(1, 1) );
128 assert_nothrow( assert_ne(1, 0) );
129 assert_nothrow( assert_lt(0, 1) );
130 assert_nothrow( assert_le(0, 1) );
131 assert_nothrow( assert_le(0, 0) );
132 assert_nothrow( assert_gt(1, 0) );
133 assert_nothrow( assert_ge(1, 0) );
134 assert_nothrow( assert_ge(0, 0) );
135
136 assert_throw!AssertError( assert_eq(1, 0) );
137 assert_throw!AssertError( assert_ne(1, 1) );
138 assert_throw!AssertError( assert_lt(1, 1) );
139 assert_throw!AssertError( assert_lt(1, 0) );
140 assert_throw!AssertError( assert_le(1, 0) );
141 assert_throw!AssertError( assert_gt(0, 0) );
142 assert_throw!AssertError( assert_gt(0, 1) );
143 assert_throw!AssertError( assert_ge(0, 1) );
144
145 class Temp { bool opEquals(int x){return x/x==x;} }
146 assert_throw!AssertError( assert_eq(new Temp, 0) );
147 assert_nothrow ( assert_eq(new Temp, 1) );
148 assert_throw!AssertError( assert_eq(new Temp, 2) );
149 }