1 module fluentasserts.core.operations.throwable;
2 
3 public import fluentasserts.core.base;
4 import fluentasserts.core.results;
5 import fluentasserts.core.lifecycle;
6 import fluentasserts.core.expect;
7 import fluentasserts.core.serializers;
8 
9 import std..string;
10 import std.conv;
11 import std.algorithm;
12 import std.array;
13 
14 version(unittest) {
15   class CustomException : Exception {
16     this(string msg, string fileName = "", size_t line = 0, Throwable next = null) {
17       super(msg, fileName, line, next);
18     }
19   }
20 }
21 
22 ///
23 IResult[] throwAnyException(ref Evaluation evaluation) @trusted nothrow {
24   IResult[] results;
25 
26   evaluation.message.addText(". ");
27   auto thrown = evaluation.currentValue.throwable;
28 
29   if(evaluation.currentValue.throwable && evaluation.isNegated) {
30     string message;
31     try message = thrown.message.to!string; catch(Exception) {}
32 
33     evaluation.message.addText("`");
34     evaluation.message.addValue(thrown.classinfo.name);
35     evaluation.message.addText("` saying `");
36     evaluation.message.addValue(message);
37     evaluation.message.addText("` was thrown.");
38 
39     try results ~= new ExpectedActualResult("No exception to be thrown", "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
40   }
41 
42   if(!thrown && !evaluation.isNegated) {
43     evaluation.message.addText("No exception was thrown.");
44 
45     try results ~= new ExpectedActualResult("Any exception to be thrown", "Nothing was thrown"); catch(Exception) {}
46   }
47 
48   if(thrown && !evaluation.isNegated && "Throwable" in evaluation.currentValue.meta) {
49     string message;
50     try message = thrown.message.to!string; catch(Exception) {}
51 
52     evaluation.message.addText("A `Throwable` saying `" ~ message ~ "` was thrown.");
53 
54     try results ~= new ExpectedActualResult("Any exception to be thrown", "A `Throwable` with message `" ~ message ~ "` was thrown"); catch(Exception) {}
55   }
56 
57   evaluation.throwable = thrown;
58   evaluation.currentValue.throwable = null;
59 
60   return results;
61 }
62 
63 /// It should be successfull when the function does not throw
64 unittest {
65   void test() {}
66   expect({ test(); }).to.not.throwAnyException();
67 }
68 
69 /// It should fail when an exception is thrown and none is expected
70 unittest {
71   void test() { throw new Exception("Test exception"); }
72 
73   bool thrown;
74 
75   try {
76     expect({ test(); }).to.not.throwAnyException();
77   } catch(TestException e) {
78     thrown = true;
79 
80     assert(e.message.indexOf("should not throw any exception. `object.Exception` saying `Test exception` was thrown.") != -1);
81     assert(e.message.indexOf("\n Expected:No exception to be thrown\n") != -1);
82     assert(e.message.indexOf("\n   Actual:`object.Exception` saying `Test exception`\n") != -1);
83   }
84 
85   assert(thrown, "The exception was not thrown");
86 }
87 
88 /// It should be successfull when the function throws an expected exception
89 unittest {
90   void test() { throw new Exception("test"); }
91   expect({ test(); }).to.throwAnyException;
92 }
93 
94 /// It should not be successfull when the function throws a throwable and an exception is expected
95 unittest {
96   void test() { assert(false); }
97 
98   bool thrown;
99 
100   try {
101     expect({ test(); }).to.throwAnyException;
102   } catch(TestException e) {
103     thrown = true;
104 
105     assert(e.message.indexOf("should throw any exception. A `Throwable` saying `Assertion failure` was thrown.") != -1);
106     assert(e.message.indexOf("\n Expected:Any exception to be thrown\n") != -1);
107     assert(e.message.indexOf("\n   Actual:A `Throwable` with message `Assertion failure` was thrown\n") != -1);
108     assert(e.file == "source/fluentasserts/core/operations/throwable.d");
109   }
110 
111   assert(thrown, "The exception was not thrown");
112 }
113 
114 /// It should be successfull when the function throws an expected exception
115 unittest {
116   void test() { throw new Exception("test"); }
117   expect({ test(); }).to.throwAnyException;
118 }
119 
120 IResult[] throwAnyExceptionWithMessage(ref Evaluation evaluation) @trusted nothrow {
121   IResult[] results;
122 
123   auto thrown = evaluation.currentValue.throwable;
124 
125 
126   if(thrown !is null && evaluation.isNegated) {
127     string message;
128     try message = thrown.message.to!string; catch(Exception) {}
129 
130     evaluation.message.addText("`");
131     evaluation.message.addValue(thrown.classinfo.name);
132     evaluation.message.addText("` saying `");
133     evaluation.message.addValue(message);
134     evaluation.message.addText("` was thrown.");
135 
136     try results ~= new ExpectedActualResult("No exception to be thrown", "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
137   }
138 
139   if(thrown is null && !evaluation.isNegated) {
140     evaluation.message.addText("Nothing was thrown.");
141 
142     try results ~= new ExpectedActualResult("Any exception to be thrown", "Nothing was thrown"); catch(Exception) {}
143   }
144 
145   if(thrown && !evaluation.isNegated && "Throwable" in evaluation.currentValue.meta) {
146     string message;
147     try message = thrown.message.to!string; catch(Exception) {}
148 
149     evaluation.message.addText(". A `Throwable` saying `" ~ message ~ "` was thrown.");
150 
151     try results ~= new ExpectedActualResult("Any throwable with the message `" ~ message ~ "` to be thrown", "A `" ~ thrown.classinfo.name ~ "` with message `" ~ message ~ "` was thrown"); catch(Exception) {}
152   }
153 
154   evaluation.throwable = thrown;
155   evaluation.currentValue.throwable = null;
156 
157   return results;
158 }
159 
160 ///
161 IResult[] throwException(ref Evaluation evaluation) @trusted nothrow {
162   evaluation.message.addText(".");
163 
164   string exceptionType;
165 
166   if("exceptionType" in evaluation.expectedValue.meta) {
167     exceptionType = evaluation.expectedValue.meta["exceptionType"].cleanString;
168   }
169 
170   IResult[] results;
171   auto thrown = evaluation.currentValue.throwable;
172 
173   if(thrown && evaluation.isNegated && thrown.classinfo.name == exceptionType) {
174     string message;
175     try message = thrown.message.to!string; catch(Exception) {}
176 
177     evaluation.message.addText("`");
178     evaluation.message.addValue(thrown.classinfo.name);
179     evaluation.message.addText("` saying `");
180     evaluation.message.addValue(message);
181     evaluation.message.addText("` was thrown.");
182 
183     try results ~= new ExpectedActualResult("no `" ~ exceptionType ~ "` to be thrown", "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
184   }
185 
186   if(thrown && !evaluation.isNegated && thrown.classinfo.name != exceptionType) {
187     string message;
188     try message = thrown.message.to!string; catch(Exception) {}
189 
190     evaluation.message.addText("`");
191     evaluation.message.addValue(thrown.classinfo.name);
192     evaluation.message.addText("` saying `");
193     evaluation.message.addValue(message);
194     evaluation.message.addText("` was thrown.");
195 
196     try results ~= new ExpectedActualResult(exceptionType, "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
197   }
198 
199   evaluation.throwable = thrown;
200   evaluation.currentValue.throwable = null;
201 
202   return results;
203 }
204 
205 /// Should be able to catch a certain exception type
206 unittest {
207   expect({
208     throw new CustomException("test");
209   }).to.throwException!CustomException;
210 }
211 
212 /// It should fail when an unexpected exception is thrown
213 unittest {
214   bool thrown;
215 
216   try {
217     expect({
218       throw new Exception("test");
219     }).to.throwException!CustomException;
220   } catch(TestException e) {
221     thrown = true;
222 
223     assert(e.message.indexOf("should throw exception \"fluentasserts.core.operations.throwable.CustomException\".`object.Exception` saying `test` was thrown.") != -1);
224     assert(e.message.indexOf("\n Expected:fluentasserts.core.operations.throwable.CustomException\n") != -1);
225     assert(e.message.indexOf("\n   Actual:`object.Exception` saying `test`\n") != -1);
226     assert(e.file == "source/fluentasserts/core/operations/throwable.d");
227   }
228 
229   assert(thrown, "The exception was not thrown");
230 }
231 
232 /// It should not fail when an exception is thrown and it is not expected
233 unittest {
234   expect({
235     throw new Exception("test");
236   }).to.not.throwException!CustomException;
237 }
238 
239 /// It should fail when an different exception than the one checked is thrown
240 unittest {
241   bool thrown;
242 
243   try {
244     expect({
245       throw new CustomException("test");
246     }).to.not.throwException!CustomException;
247   } catch(TestException e) {
248     thrown = true;
249     assert(e.message.indexOf("should not throw exception \"fluentasserts.core.operations.throwable.CustomException\".`fluentasserts.core.operations.throwable.CustomException` saying `test` was thrown.") != -1);
250     assert(e.message.indexOf("\n Expected:no `fluentasserts.core.operations.throwable.CustomException` to be thrown\n") != -1);
251     assert(e.message.indexOf("\n   Actual:`fluentasserts.core.operations.throwable.CustomException` saying `test`\n") != -1);
252     assert(e.file == "source/fluentasserts/core/operations/throwable.d");
253   }
254 
255   assert(thrown, "The exception was not thrown");
256 }
257 
258 ///
259 IResult[] throwExceptionWithMessage(ref Evaluation evaluation) @trusted nothrow {
260   import std.stdio;
261 
262 
263   evaluation.message.addText(". ");
264 
265   string exceptionType;
266   string message;
267   string expectedMessage = evaluation.expectedValue.strValue;
268 
269   if(expectedMessage.startsWith(`"`)) {
270     expectedMessage = expectedMessage[1..$-1];
271   }
272 
273   if("exceptionType" in evaluation.expectedValue.meta) {
274     exceptionType = evaluation.expectedValue.meta["exceptionType"].cleanString;
275   }
276 
277   IResult[] results;
278   auto thrown = evaluation.currentValue.throwable;
279   evaluation.throwable = thrown;
280   evaluation.currentValue.throwable = null;
281 
282   if(thrown) {
283     try message = thrown.message.to!string; catch(Exception) {}
284   }
285 
286   if(!thrown && !evaluation.isNegated) {
287     evaluation.message.addText("No exception was thrown.");
288 
289     try results ~= new ExpectedActualResult("`" ~ exceptionType ~ "` with message `" ~ expectedMessage ~ "` to be thrown", "nothing was thrown"); catch(Exception) {}
290   }
291 
292   if(thrown && !evaluation.isNegated && thrown.classinfo.name != exceptionType) {
293     evaluation.message.addText("`");
294     evaluation.message.addValue(thrown.classinfo.name);
295     evaluation.message.addText("` saying `");
296     evaluation.message.addValue(message);
297     evaluation.message.addText("` was thrown.");
298 
299     try results ~= new ExpectedActualResult("`" ~ exceptionType ~ "` to be thrown", "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
300   }
301 
302   if(thrown && !evaluation.isNegated && thrown.classinfo.name == exceptionType && message != expectedMessage) {
303     evaluation.message.addText("`");
304     evaluation.message.addValue(thrown.classinfo.name);
305     evaluation.message.addText("` saying `");
306     evaluation.message.addValue(message);
307     evaluation.message.addText("` was thrown.");
308 
309     try results ~= new ExpectedActualResult("`" ~ exceptionType ~ "` saying `" ~ message ~ "` to be thrown", "`" ~ thrown.classinfo.name ~ "` saying `" ~ message ~ "`"); catch(Exception) {}
310   }
311 
312   return results;
313 }
314 
315 /// It fails when an exception is not catched
316 unittest {
317   Exception exception;
318 
319   try {
320     expect({}).to.throwException!Exception.withMessage.equal("test");
321   } catch(Exception e) {
322     exception = e;
323   }
324 
325   assert(exception !is null);
326   expect(exception.message).to.contain(`should throw exception with message equal "test". No exception was thrown.`);
327 }
328 
329 /// It does not fail when an exception is not expected and none is not catched
330 unittest {
331   Exception exception;
332 
333   try {
334     expect({}).not.to.throwException!Exception.withMessage.equal("test");
335   } catch(Exception e) {
336     exception = e;
337   }
338 
339   assert(exception is null);
340 }
341 
342 /// It fails when the caught exception has a different type
343 unittest {
344   Exception exception;
345 
346   try {
347     expect({
348       throw new CustomException("hello");
349     }).to.throwException!Exception.withMessage.equal("test");
350   } catch(Exception e) {
351     exception = e;
352   }
353 
354   assert(exception !is null);
355   expect(exception.message).to.contain("should throw exception with message equal \"test\". `fluentasserts.core.operations.throwable.CustomException` saying `hello` was thrown.");
356 }
357 
358 /// It does not fail when a certain exception type is not catched
359 unittest {
360   Exception exception;
361 
362   try {
363     expect({
364       throw new CustomException("hello");
365     }).not.to.throwException!Exception.withMessage.equal("test");
366   } catch(Exception e) {
367     exception = e;
368   }
369 
370   assert(exception is null);
371 }
372 
373 /// It fails when the caught exception has a different message
374 unittest {
375   Exception exception;
376 
377   try {
378     expect({
379       throw new CustomException("hello");
380     }).to.throwException!CustomException.withMessage.equal("test");
381   } catch(Exception e) {
382     exception = e;
383   }
384 
385   assert(exception !is null);
386   expect(exception.message).to.contain("should throw exception with message equal \"test\". `fluentasserts.core.operations.throwable.CustomException` saying `hello` was thrown.");
387 }
388 
389 /// It does not fails when the caught exception is expected to have a different message
390 unittest {
391   Exception exception;
392 
393   try {
394     expect({
395       throw new CustomException("hello");
396     }).not.to.throwException!CustomException.withMessage.equal("test");
397   } catch(Exception e) {
398     exception = e;
399   }
400 
401   assert(exception is null);
402 }