1 module fluentasserts.core.callable;
2
3 public import fluentasserts.core.base;
4 import std..string;
5 import std.datetime;
6 import std.conv;
7 import std.traits;
8
9 import fluentasserts.core.results;
10
11 @safe:
12 ///
13 struct ShouldCallable(T) {
14 private {
15 T callable;
16 }
17
18 mixin ShouldCommons;
19 mixin ShouldThrowableCommons;
20
21 ///
22 this(lazy T callable) {
23 auto result = callable.evaluate;
24
25 valueEvaluation = result.evaluation;
26 this.callable = result.value;
27 }
28
29 ///
30 auto haveExecutionTime(string file = __FILE__, size_t line = __LINE__) {
31 validateException;
32
33 auto tmpShould = ShouldBaseType!Duration(evaluate(valueEvaluation.duration)).forceMessage(" have execution time");
34
35 return tmpShould;
36 }
37
38 ///
39 auto beNull(string file = __FILE__, size_t line = __LINE__) {
40 validateException;
41
42 addMessage(" be ");
43 addValue("null");
44 beginCheck;
45
46 bool isNull = callable is null;
47
48 string expected;
49
50 static if(isDelegate!callable) {
51 string actual = callable.ptr.to!string;
52 } else {
53 string actual = (cast(void*)callable).to!string;
54 }
55
56 if(expectedValue) {
57 expected = "null";
58 } else {
59 expected = "not null";
60 }
61
62 return result(isNull, [], new ExpectedActualResult(expected, actual), file, line);
63 }
64 }
65
66 /// Should be able to catch any exception
67 unittest {
68 ({
69 throw new Exception("test");
70 }).should.throwAnyException.msg.should.equal("test");
71 }
72
73 /// Should be able to catch any assert
74 unittest {
75 ({
76 assert(false, "test");
77 }).should.throwSomething.withMessage.equal("test");
78 }
79
80 /// Should be able to use with message without a custom assert
81 unittest {
82 ({
83 assert(false, "test");
84 }).should.throwSomething.withMessage("test");
85 }
86
87 /// Should be able to catch a certain exception type
88 unittest {
89 class CustomException : Exception {
90 this(string msg, string fileName = "", size_t line = 0, Throwable next = null) {
91 super(msg, fileName, line, next);
92 }
93 }
94
95 ({
96 throw new CustomException("test");
97 }).should.throwException!CustomException.withMessage("test");
98
99 bool hasException;
100 try {
101 ({
102 throw new Exception("test");
103 }).should.throwException!CustomException.withMessage("test");
104 } catch(TestException t) {
105 hasException = true;
106 t.msg.should.contain(" }) should throw exception with message equal \"test\". `object.Exception` saying `test` was thrown.");
107 }
108 hasException.should.equal(true).because("we want to catch a CustomException not an Exception");
109 }
110
111 /// Should be able to retrieve a typed version of a custom exception
112 unittest {
113 class CustomException : Exception {
114 int data;
115 this(int data, string msg, string fileName = "", size_t line = 0, Throwable next = null) {
116 super(msg, fileName, line, next);
117
118 this.data = data;
119 }
120 }
121
122 auto thrown = ({
123 throw new CustomException(2, "test");
124 }).should.throwException!CustomException.thrown;
125
126 thrown.should.not.beNull;
127 thrown.msg.should.equal("test");
128 (cast(CustomException) thrown).data.should.equal(2);
129 }
130
131 /// Should fail if an exception is not thrown
132 unittest {
133 auto thrown = false;
134 try {
135 ({ }).should.throwAnyException;
136 } catch(TestException e) {
137 thrown = true;
138 e.msg.split("\n")[0].should.equal("({ }) should throw any exception. No exception was thrown.");
139 }
140
141 thrown.should.equal(true);
142 }
143
144 /// Should fail if an exception is not expected
145 unittest {
146 auto thrown = false;
147 try {
148 ({
149 throw new Exception("test");
150 }).should.not.throwAnyException;
151 } catch(TestException e) {
152 thrown = true;
153 e.msg.split("\n")[2].should.equal(" }) should not throw any exception. `object.Exception` saying `test` was thrown.");
154 }
155
156 thrown.should.equal(true);
157 }
158
159 /// Should be able to benchmark some code
160 unittest {
161 ({
162
163 }).should.haveExecutionTime.lessThan(1.seconds);
164 }
165
166 /// Should fail on benchmark timeout
167 unittest {
168 import core.thread;
169
170 TestException exception = null;
171
172 try {
173 ({
174 Thread.sleep(2.msecs);
175 }).should.haveExecutionTime.lessThan(1.msecs);
176 } catch(TestException e) {
177 exception = e;
178 }
179
180 exception.should.not.beNull.because("we wait 20 milliseconds");
181 exception.msg.should.startWith("({\n Thread.sleep(2.msecs);\n }) should have execution time less than 1 ms.");
182 }
183
184 /// It should check if a delegate is null
185 unittest {
186 void delegate() action;
187 action.should.beNull;
188
189 ({ }).should.not.beNull;
190
191 auto msg = ({
192 action.should.not.beNull;
193 }).should.throwException!TestException.msg;
194
195 msg.should.startWith("action should not be null.");
196 msg.should.contain("Expected:not null");
197 msg.should.contain("Actual:null");
198
199 msg = ({
200 ({ }).should.beNull;
201 }).should.throwException!TestException.msg;
202
203 msg.should.startWith("({ }) should be null.");
204 msg.should.contain("Expected:null\n");
205 msg.should.not.contain("Actual:null\n");
206 }