1 /**
2  * Assert toolkit for more expressive unit testing.
3  *
4  * Many methods implement compile-time parameters (file, line) that are set at the call site.
5  * It is preferred that these parameters are ignored when using these methods.
6  *
7  * License:
8  *     MIT. See LICENSE for full details.
9  */
10 module dunit.toolkit;
11 
12 /**
13  * Imports.
14  */
15 import dunit.error;
16 import dunit.moduleunittester;
17 import std.algorithm;
18 import std.array;
19 import std.math;
20 import std.regex;
21 import std.stdio;
22 import std..string;
23 import std.traits;
24 import std.range;
25 
26 /**
27  * Assert that two floating point values are approximately equal.
28  *
29  * See_Also:
30  *     $(LINK http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm)
31  *
32  * Params:
33  *     value = The value used during the assertion.
34  *     target = The target value.
35  *     ulps = The maximum space between two approximately equal floating point numbers measured in $(LINK2 http://en.wikipedia.org/wiki/Unit_in_the_last_place, units of least precision). A higher number means more approximation.
36  *     message = The error message to display.
37  *     file = The file name where the error occurred. The value is added automatically at the call site.
38  *     line = The line where the error occurred. The value is added automatically at the call site.
39  *
40  * Throws:
41  *     DUnitAssertError if the assertation fails.
42  */
43 public void assertApprox(A, B)(A value, B target, long ulps = 10, string message = "Failed asserting approximately equal", string file = __FILE__, size_t line = __LINE__) if (isFloatingPoint!(CommonType!(A, B)))
44 {
45 	static if (is(CommonType!(A, B) == double))
46 	{
47 		long maximumUlps       = 0x8000000000000;
48 		long negativeZeroFloat = 0x8000000000000000;
49 		long intValue          = *(cast(ulong*)&value);
50 		long intTarget         = *(cast(ulong*)&target);
51 		long difference;
52 	}
53 	else
54 	{
55 		int maximumUlps       = 0x400000;
56 		int negativeZeroFloat = 0x80000000;
57 		int intValue          = *(cast(int*)&value);
58 		int intTarget         = *(cast(int*)&target);
59 		int difference;
60 	}
61 
62 	ulps.assertGreaterThan(0, "Unit of least precision should be above 0", file, line);
63 	ulps.assertLessThan(maximumUlps, format("Unit of least precision should be below %s", maximumUlps), file, line);
64 
65 	if (intValue < 0)
66 	{
67 		intValue = negativeZeroFloat - intValue;
68 	}
69 
70 	if (intTarget < 0)
71 	{
72 		intTarget = negativeZeroFloat - intTarget;
73 	}
74 
75 	difference = abs(intValue - intTarget);
76 
77 	if (difference > ulps)
78 	{
79 		auto error = new DUnitAssertError(message, file, line);
80 
81 		error.addTypedExpectation("Expected value", target);
82 		error.addTypedError("Actual value", value);
83 
84 		throw error;
85 	}
86 }
87 
88 ///
89 unittest
90 {
91 	float smallestFloatSubnormal = float.min_normal * float.epsilon;
92 	smallestFloatSubnormal.assertApprox(-smallestFloatSubnormal);
93 
94 	double smallestDoubleSubnormal = double.min_normal * double.epsilon;
95 	smallestDoubleSubnormal.assertApprox(-smallestDoubleSubnormal);
96 
97 	0.0f.assertApprox(-0.0f);
98 	(-0.0f).assertApprox(0.0f);
99 	0.0.assertApprox(-0.0);
100 	(-0.0).assertApprox(0.0);
101 	2.0f.assertApprox(1.999999f);
102 	1.999999f.assertApprox(2.0f);
103 	2.0.assertApprox(1.999999999999999);
104 	1.999999999999999.assertApprox(2.0);
105 
106 	// The following tests pass but are open for debate whether or not they should.
107 	float.max.assertApprox(float.infinity);
108 	float.infinity.assertApprox(float.max);
109 	double.infinity.assertApprox(double.max);
110 	double.max.assertApprox(double.infinity);
111 	float.nan.assertApprox(float.nan);
112 	double.nan.assertApprox(double.nan);
113 
114 	// Assert a DUnitAssertError is thrown if assertApprox fails.
115 	10f.assertApprox(0f).assertThrow!(DUnitAssertError)("Failed asserting approximately equal");
116 }
117 
118 /**
119  * Assert that two floating point values are approximately equal using an epsilon value.
120  *
121  * See_Also:
122  *     $(LINK http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm)
123  *
124  * Params:
125  *     value = The value used during the assertion.
126  *     target = The target value.
127  *     epsilon = An epsilon value to be used as the maximum absolute and relative error in the comparison.
128  *     message = The error message to display.
129  *     file = The file name where the error occurred. The value is added automatically at the call site.
130  *     line = The line where the error occurred. The value is added automatically at the call site.
131  *
132  * Throws:
133  *     DUnitAssertError if the assertation fails.
134  */
135 public void assertApprox(A, B)(A value, B target, double epsilon, string message = "Failed asserting approximately equal", string file = __FILE__, size_t line = __LINE__) if (isFloatingPoint!(CommonType!(A, B)))
136 {
137 	auto divisor       = (fabs(value) > fabs(target)) ? value : target;
138 	auto absoluteError = fabs(value - target);
139 	auto relativeError = fabs((value - target) / divisor);
140 
141 	if (absoluteError > epsilon && relativeError > epsilon)
142 	{
143 		auto error = new DUnitAssertError(message, file, line);
144 
145 		error.addTypedExpectation("Expected value", target);
146 		error.addTypedError("Actual value", value);
147 
148 		throw error;
149 	}
150 }
151 
152 ///
153 unittest
154 {
155 	float smallestFloatSubnormal = float.min_normal * float.epsilon;
156 	smallestFloatSubnormal.assertApprox(-smallestFloatSubnormal, 0.00001);
157 
158 	double smallestDoubleSubnormal = double.min_normal * double.epsilon;
159 	smallestDoubleSubnormal.assertApprox(-smallestDoubleSubnormal, 0.00001);
160 
161 	0.0f.assertApprox(-0.0f, 0.00001);
162 	(-0.0f).assertApprox(0.0f, 0.00001);
163 	0.0.assertApprox(-0.0, 0.00001);
164 	(-0.0).assertApprox(0.0, 0.00001);
165 	2.0f.assertApprox(1.99f, 0.01);
166 	1.99f.assertApprox(2.0f, 0.01);
167 	2.0.assertApprox(1.99, 0.01);
168 	1.99.assertApprox(2.0, 0.01);
169 
170 	// The following tests pass but are open for debate whether or not they should.
171 	float.max.assertApprox(float.infinity, 0.00001);
172 	float.infinity.assertApprox(float.max, 0.00001);
173 	double.infinity.assertApprox(double.max, 0.00001);
174 	double.max.assertApprox(double.infinity, 0.00001);
175 	float.nan.assertApprox(float.nan, 0.00001);
176 	double.nan.assertApprox(double.nan, 0.00001);
177 
178 	// Assert a DUnitAssertError is thrown if assertApprox fails.
179 	10f.assertApprox(0f, 0.00001).assertThrow!(DUnitAssertError)("Failed asserting approximately equal");
180 }
181 
182 /**
183  * Assert that an array contains a particular value count.
184  *
185  * Params:
186  *     array = The array to interogate.
187  *     count = The amount of values the array should hold.
188  *     message = The error message to display.
189  *     file = The file name where the error occurred. The value is added automatically at the call site.
190  *     line = The line where the error occurred. The value is added automatically at the call site.
191  *
192  * Throws:
193  *     DUnitAssertError if the assertation fails.
194  */
195 public void assertCount(A)(A array, ulong count, string message = "Failed asserting array count", string file = __FILE__, size_t line = __LINE__) if (isArray!(A) || isAssociativeArray!(A))
196 {
197 	if (array.length != count)
198 	{
199 		auto error = new DUnitAssertError(message, file, line);
200 
201 		error.addInfo("Elements", array);
202 		error.addExpectation("Expected count", count);
203 		error.addError("Actual count", array.length);
204 
205 		throw error;
206 	}
207 }
208 
209 ///
210 unittest
211 {
212 	int[string] associativeArray;
213 	int[] dynamicArray;
214 	string string_;
215 
216 	associativeArray.assertCount(0);
217 	dynamicArray.assertCount(0);
218 	string_.assertCount(0);
219 	[].assertCount(0);
220 
221 	"Hello".assertCount(5);
222 	[1, 2, 3, 4].assertCount(4);
223 	["foo", "bar", "baz", "qux"].assertCount(4);
224 	[["foo", "bar"], ["baz", "qux"]].assertCount(2);
225 	["foo":1, "bar":2, "baz":3, "qux":4].assertCount(4);
226 
227 	// Assert a DUnitAssertError is thrown if assertCount fails.
228 	associativeArray.assertCount(1).assertThrow!(DUnitAssertError)("Failed asserting array count");
229 }
230 
231 /**
232  * Assert that an array is empty.
233  *
234  * Params:
235  *     array = The array to interogate.
236  *     message = The error message to display.
237  *     file = The file name where the error occurred. The value is added automatically at the call site.
238  *     line = The line where the error occurred. The value is added automatically at the call site.
239  *
240  * Throws:
241  *     DUnitAssertError if the assertation fails.
242  */
243 public void assertEmpty(A)(A array, string message = "Failed asserting empty array", string file = __FILE__, size_t line = __LINE__) if (isArray!(A) || isAssociativeArray!(A))
244 {
245 	if (array.length > 0)
246 	{
247 		auto error = new DUnitAssertError(message, file, line);
248 
249 		error.addInfo("Elements", array);
250 		error.addExpectation("Expected count", 0);
251 		error.addError("Actual count", array.length);
252 
253 		throw error;
254 	}
255 }
256 
257 ///
258 unittest
259 {
260 	int[string] associativeArray;
261 	int[] dynamicArray;
262 	string string_;
263 
264 	associativeArray.assertEmpty();
265 	dynamicArray.assertEmpty();
266 	string_.assertEmpty();
267 	[].assertEmpty();
268 
269 	// Assert a DUnitAssertError is thrown if assertEmpty fails.
270 	[1].assertEmpty().assertThrow!(DUnitAssertError)("Failed asserting empty array");
271 }
272 
273 /**
274  * Assert that a range is empty.
275  *
276  * Params:
277  *     range = The range to interogate.
278  *     message = The error message to display.
279  *     file = The file name where the error occurred. The value is added automatically at the call site.
280  *     line = The line where the error occurred. The value is added automatically at the call site.
281  *
282  * Throws:
283  *     DUnitAssertError if the assertation fails.
284  */
285 public void assertEmpty(R)(R range, string message = "Failed asserting empty range", string file = __FILE__, size_t line = __LINE__) if ((is(R == class) || is (R == struct)) && isInputRange!(R))
286 {
287 	if (!range.empty)
288 	{
289 		auto error = new DUnitAssertError(message, file, line);
290 
291 		error.addExpectation("Expected count", 0);
292 		error.addInfo("Front elements", range.take(10));
293 
294 		static if (isInfinite!(R))
295 		{
296 			error.addError("Actual count", "∞");
297 		}
298 		else
299 		{
300 			error.addError("Actual count", range.walkLength());
301 		}
302 
303 		throw error;
304 	}
305 }
306 
307 ///
308 unittest
309 {
310 	class A
311 	{
312 		void popFront() {};
313 		@property bool empty() { return true; };
314 		@property int front() { return 0; };
315 	}
316 
317 	struct B
318 	{
319 		void popFront() {};
320 		enum bool empty = false;
321 		@property int front() { return 1; };
322 	}
323 
324 	static assert(isInputRange!(A));
325 	static assert(isInputRange!(B));
326 
327 	auto emptyRange = new A();
328 	emptyRange.assertEmpty();
329 
330 	B infiniteRange = B.init;
331 	infiniteRange.takeNone.assertEmpty();
332 
333 	// Assert a DUnitAssertError is thrown if assertEmpty fails.
334 	infiniteRange.assertEmpty().assertThrow!(DUnitAssertError)("Failed asserting empty range");
335 	infiniteRange.take(10).assertEmpty().assertThrow!(DUnitAssertError)("Failed asserting empty range");
336 }
337 
338 /**
339  * Assert that a string ends with a particular string.
340  *
341  * Params:
342  *     value = The value used during the assertion.
343  *     suffix = The suffix to match.
344  *     message = The error message to display.
345  *     file = The file name where the error occurred. The value is added automatically at the call site.
346  *     line = The line where the error occurred. The value is added automatically at the call site.
347  *
348  * Throws:
349  *     DUnitAssertError if the assertation fails.
350  */
351 public void assertEndsWith(string value, string suffix, string message = "Failed asserting ends with", string file = __FILE__, size_t line = __LINE__)
352 {
353 	if (!endsWith(value, suffix))
354 	{
355 		auto error = new DUnitAssertError(message, file, line);
356 
357 		error.addExpectation("Expected end", "..." ~ suffix);
358 		error.addError("Actual value", value);
359 
360 		throw error;
361 	}
362 }
363 
364 ///
365 unittest
366 {
367 	"foo bar".assertEndsWith("bar");
368 	"baz qux".assertEndsWith("qux");
369 
370 	// Assert a DUnitAssertError is thrown if assertEndsWith fails.
371 	"foo".assertEndsWith("bar").assertThrow!(DUnitAssertError)("Failed asserting ends with");
372 }
373 
374 /**
375  * Assert that two values are equal.
376  *
377  * Params:
378  *     value = The value used during the assertion.
379  *     target = The target value.
380  *     message = The error message to display.
381  *     file = The file name where the error occurred. The value is added automatically at the call site.
382  *     line = The line where the error occurred. The value is added automatically at the call site.
383  *
384  * Throws:
385  *     DUnitAssertError if the assertation fails.
386  */
387 public void assertEqual(A, B)(A value, B target, string message = "Failed asserting equal", string file = __FILE__, size_t line = __LINE__)
388 {
389 	if (target != value)
390 	{
391 		auto error = new DUnitAssertError(message, file, line);
392 
393 		error.addTypedExpectation("Expected value", target);
394 		error.addTypedError("Actual value", value);
395 
396 		throw error;
397 	}
398 }
399 
400 ///
401 unittest
402 {
403 	123.assertEqual(123);
404 	"hello".assertEqual("hello");
405 
406 	// Assert a DUnitAssertError is thrown if assertEqual fails.
407 	1.assertEqual(2).assertThrow!(DUnitAssertError)("Failed asserting equal");
408 }
409 
410 /**
411  * Assert that a boolean value is false.
412  *
413  * Params:
414  *     value = The value used during the assertion.
415  *     message = The error message to display.
416  *     file = The file name where the error occurred. The value is added automatically at the call site.
417  *     line = The line where the error occurred. The value is added automatically at the call site.
418  *
419  * Throws:
420  *     DUnitAssertError if the assertation fails.
421  */
422 public void assertFalse(T)(T value, string message = "Failed asserting false", string file = __FILE__, size_t line = __LINE__)
423 {
424 	value.assertType!(bool)("Wrong type for asserting false", file, line);
425 
426 	if (value)
427 	{
428 		auto error = new DUnitAssertError(message, file, line);
429 
430 		error.addError("Value", value);
431 
432 		throw error;
433 	}
434 }
435 
436 ///
437 unittest
438 {
439 	false.assertFalse();
440 
441 	// Assert a DUnitAssertError is thrown if assertFalse fails.
442 	true.assertFalse().assertThrow!(DUnitAssertError)("Failed asserting false");
443 }
444 
445 /**
446  * Assert that a value evaluates as false.
447  *
448  * Params:
449  *     value = The value used during the assertion.
450  *     message = The error message to display.
451  *     file = The file name where the error occurred. The value is added automatically at the call site.
452  *     line = The line where the error occurred. The value is added automatically at the call site.
453  *
454  * Throws:
455  *     DUnitAssertError if the assertation fails.
456  */
457 public void assertFalsey(T)(T value, string message = "Failed asserting falsey", string file = __FILE__, size_t line = __LINE__)
458 {
459 	if (value)
460 	{
461 		auto error = new DUnitAssertError(message, file, line);
462 
463 		error.addTypedInfo("Value", value);
464 		error.addError("Evaluates to", !!value);
465 
466 		throw error;
467 	}
468 }
469 
470 ///
471 unittest
472 {
473 	false.assertFalsey();
474 	[].assertFalsey();
475 	null.assertFalsey();
476 	0.assertFalsey();
477 
478 	// Assert a DUnitAssertError is thrown if assertFalsey fails.
479 	true.assertFalsey().assertThrow!(DUnitAssertError)("Failed asserting falsey");
480 }
481 
482 /**
483  * Assert that a value is greater than a threshold value.
484  *
485  * Params:
486  *     value = The value used during the assertion.
487  *     threshold = The threshold value.
488  *     message = The error message to display.
489  *     file = The file name where the error occurred. The value is added automatically at the call site.
490  *     line = The line where the error occurred. The value is added automatically at the call site.
491  *
492  * Throws:
493  *     DUnitAssertError if the assertation fails.
494  */
495 public void assertGreaterThan(A, B)(A value, B threshold, string message = "Failed asserting greater than", string file = __FILE__, size_t line = __LINE__)
496 {
497 	if (value <= threshold)
498 	{
499 		auto error = new DUnitAssertError(message, file, line);
500 
501 		error.addExpectation("Minimum value", threshold + 1);
502 		error.addError("Actual value", value);
503 
504 		throw error;
505 	}
506 }
507 
508 ///
509 unittest
510 {
511 	11.assertGreaterThan(10);
512 
513 	// Assert a DUnitAssertError is thrown if assertGreaterThan fails.
514 	11.assertGreaterThan(12).assertThrow!(DUnitAssertError)("Failed asserting greater than");
515 }
516 
517 /**
518  * Assert that a value is greater than or equal to a threshold value.
519  *
520  * Params:
521  *     value = The value used during the assertion.
522  *     threshold = The threshold value.
523  *     message = The error message to display.
524  *     file = The file name where the error occurred. The value is added automatically at the call site.
525  *     line = The line where the error occurred. The value is added automatically at the call site.
526  *
527  * Throws:
528  *     DUnitAssertError if the assertation fails.
529  */
530 public void assertGreaterThanOrEqual(A, B)(A value, B threshold, string message = "Failed asserting greater than or equal", string file = __FILE__, size_t line = __LINE__)
531 {
532 	if (value < threshold)
533 	{
534 		auto error = new DUnitAssertError(message, file, line);
535 
536 		error.addExpectation("Minimum value", threshold);
537 		error.addError("Actual value", value);
538 
539 		throw error;
540 	}
541 }
542 
543 ///
544 unittest
545 {
546 	10.assertGreaterThanOrEqual(10);
547 	11.assertGreaterThanOrEqual(10);
548 
549 	// Assert a DUnitAssertError is thrown if assertGreaterThanOrEqual fails.
550 	11.assertGreaterThanOrEqual(12).assertThrow!(DUnitAssertError)("Failed asserting greater than or equal");
551 }
552 
553 /**
554  * Assert that an associative array contains a particular key.
555  *
556  * Params:
557  *     haystack = The associative array to interogate.
558  *     needle = The key the array should contain.
559  *     message = The error message to display.
560  *     file = The file name where the error occurred. The value is added automatically at the call site.
561  *     line = The line where the error occurred. The value is added automatically at the call site.
562  *
563  * Throws:
564  *     DUnitAssertError if the assertation fails.
565  */
566 public void assertHasKey(A, B)(A haystack, B needle, string message = "Failed asserting array has key", string file = __FILE__, size_t line = __LINE__) if (isAssociativeArray!(A))
567 {
568 	if (needle !in haystack)
569 	{
570 		auto error = new DUnitAssertError(message, file, line);
571 
572 		error.addInfo("Array type", typeof(haystack).stringof);
573 		error.addInfo("Elements", haystack);
574 		error.addTypedError("Missing key", needle);
575 
576 		throw error;
577 	}
578 }
579 
580 ///
581 unittest
582 {
583 	["foo":1, "bar":2, "baz":3, "qux":4].assertHasKey("foo");
584 	[1:"foo", 2:"bar", 3:"baz", 4:"qux"].assertHasKey(1);
585 
586 	// Assert a DUnitAssertError is thrown if assertHasKey fails.
587 	["foo":"bar"].assertHasKey("baz").assertThrow!(DUnitAssertError)("Failed asserting array has key");
588 }
589 
590 /**
591  * Assert that an array contains a particular value.
592  *
593  * Params:
594  *     haystack = The array to interogate.
595  *     needle = The value the array should contain.
596  *     message = The error message to display.
597  *     file = The file name where the error occurred. The value is added automatically at the call site.
598  *     line = The line where the error occurred. The value is added automatically at the call site.
599  *
600  * Throws:
601  *     DUnitAssertError if the assertation fails.
602  */
603 public void assertHasValue(A, B)(A haystack, B needle, string message = "Failed asserting array has value", string file = __FILE__, size_t line = __LINE__) if (isArray!(A) || isAssociativeArray!(A))
604 {
605 	static if (isArray!(A))
606 	{
607 		bool foundValue = canFind(haystack, needle);
608 	}
609 	else static if (isAssociativeArray!(A))
610 	{
611 		bool foundValue = canFind(haystack.values, needle);
612 	}
613 
614 	if (!foundValue)
615 	{
616 		auto error = new DUnitAssertError(message, file, line);
617 
618 		error.addInfo("Array type", typeof(haystack).stringof);
619 		error.addInfo("Elements", haystack);
620 		error.addTypedError("Missing value", needle);
621 
622 		throw error;
623 	}
624 }
625 
626 ///
627 unittest
628 {
629 	"Hello".assertHasValue("H");
630 	[1, 2, 3, 4].assertHasValue(2);
631 	["foo", "bar", "baz", "qux"].assertHasValue("foo");
632 	[["foo", "bar"], ["baz", "qux"]].assertHasValue(["foo", "bar"]);
633 	["foo":1, "bar":2, "baz":3, "qux":4].assertHasValue(4);
634 
635 	// Assert a DUnitAssertError is thrown if assertHasValue fails.
636 	["foo":"bar"].assertHasValue("baz").assertThrow!(DUnitAssertError)("Failed asserting array has value");
637 }
638 
639 /**
640  * Assert that a value is an instance of a type.
641  *
642  * Params:
643  *     value = The value used during the assertion.
644  *     message = The error message to display.
645  *     file = The file name where the error occurred. The value is added automatically at the call site.
646  *     line = The line where the error occurred. The value is added automatically at the call site.
647  *
648  * Throws:
649  *     DUnitAssertError if the assertation fails.
650  */
651 public void assertInstanceOf(A, B)(B value, string message = "Failed asserting instance of", string file = __FILE__, size_t line = __LINE__)
652 {
653 	if (!cast(A)value)
654 	{
655 		auto error = new DUnitAssertError(message, file, line);
656 
657 		error.addExpectation("Expected instance", A.stringof);
658 		error.addError("Non derived type", B.stringof);
659 
660 		throw error;
661 	}
662 }
663 
664 ///
665 unittest
666 {
667 	interface A {}
668 	class B : A {}
669 	class C : B {}
670 
671 	auto b = new B();
672 	auto c = new C();
673 
674 	b.assertInstanceOf!(Object)();
675 	b.assertInstanceOf!(A)();
676 	b.assertInstanceOf!(B)();
677 
678 	c.assertInstanceOf!(Object)();
679 	c.assertInstanceOf!(A)();
680 	c.assertInstanceOf!(B)();
681 	c.assertInstanceOf!(C)();
682 
683 	// Assert a DUnitAssertError is thrown if assertInstanceOf fails.
684 	b.assertInstanceOf!(C)().assertThrow!(DUnitAssertError)("Failed asserting instance of");
685 }
686 
687 /**
688  * Assert that a value is less than a threshold value.
689  *
690  * Params:
691  *     value = The value used during the assertion.
692  *     threshold = The threshold value.
693  *     message = The error message to display.
694  *     file = The file name where the error occurred. The value is added automatically at the call site.
695  *     line = The line where the error occurred. The value is added automatically at the call site.
696  *
697  * Throws:
698  *     DUnitAssertError if the assertation fails.
699  */
700 public void assertLessThan(A, B)(A value, B threshold, string message = "Failed asserting less than", string file = __FILE__, size_t line = __LINE__)
701 {
702 	if (value >= threshold)
703 	{
704 		auto error = new DUnitAssertError(message, file, line);
705 
706 		error.addExpectation("Maximum value", threshold - 1);
707 		error.addError("Actual value", value);
708 
709 		throw error;
710 	}
711 }
712 
713 ///
714 unittest
715 {
716 	9.assertLessThan(10);
717 
718 	// Assert a DUnitAssertError is thrown if assertLessThan fails.
719 	9.assertLessThan(8).assertThrow!(DUnitAssertError)("Failed asserting less than");
720 }
721 
722 /**
723  * Assert that a value is less than or equal to a threshold value.
724  *
725  * Params:
726  *     value = The value used during the assertion.
727  *     threshold = The threshold value.
728  *     message = The error message to display.
729  *     file = The file name where the error occurred. The value is added automatically at the call site.
730  *     line = The line where the error occurred. The value is added automatically at the call site.
731  *
732  * Throws:
733  *     DUnitAssertError if the assertation fails.
734  */
735 public void assertLessThanOrEqual(A, B)(A value, B threshold, string message = "Failed asserting less than or equal", string file = __FILE__, size_t line = __LINE__)
736 {
737 	if (value > threshold)
738 	{
739 		auto error = new DUnitAssertError(message, file, line);
740 
741 		error.addExpectation("Maximum value", threshold);
742 		error.addError("Actual value", value);
743 
744 		throw error;
745 	}
746 }
747 
748 ///
749 unittest
750 {
751 	10.assertLessThanOrEqual(10);
752 	9.assertLessThanOrEqual(10);
753 
754 	// Assert a DUnitAssertError is thrown if assertLessThanOrEqual fails.
755 	9.assertLessThanOrEqual(8).assertThrow!(DUnitAssertError)("Failed asserting less than or equal");
756 }
757 
758 /**
759  * Assert that a string matches a regular expression.
760  *
761  * Params:
762  *     value = The value used during the assertion.
763  *     pattern = The regular expression pattern.
764  *     message = The error message to display.
765  *     file = The file name where the error occurred. The value is added automatically at the call site.
766  *     line = The line where the error occurred. The value is added automatically at the call site.
767  *
768  * Throws:
769  *     DUnitAssertError if the assertation fails.
770  */
771 public void assertMatchRegex(string value, string pattern, string message = "Failed asserting match to regex", string file = __FILE__, size_t line = __LINE__)
772 {
773 	if (match(value, pattern).empty())
774 	{
775 		auto error = new DUnitAssertError(message, file, line);
776 
777 		error.addInfo("Regex", pattern);
778 		error.addError("Value", value);
779 
780 		throw error;
781 	}
782 }
783 
784 ///
785 unittest
786 {
787 	"foo".assertMatchRegex(r"^foo$");
788 	"192.168.0.1".assertMatchRegex(r"((?:[\d]{1,3}\.){3}[\d]{1,3})");
789 
790 	// Assert a DUnitAssertError is thrown if assertMatchRegex fails.
791 	"foo".assertMatchRegex(r"^bar$").assertThrow!(DUnitAssertError)("Failed asserting match to regex");
792 }
793 
794 /**
795  * Assert that a value is null.
796  *
797  * Params:
798  *     value = The value to assert as null.
799  *     message = The error message to display.
800  *     file = The file name where the error occurred. The value is added automatically at the call site.
801  *     line = The line where the error occurred. The value is added automatically at the call site.
802  *
803  * Throws:
804  *     DUnitAssertError if the assertation fails.
805  */
806 public void assertNull(A)(A value, string message = "Failed asserting null", string file = __FILE__, size_t line = __LINE__) if (A.init is null)
807 {
808 	if (value !is null)
809 	{
810 		auto error = new DUnitAssertError(message, file, line);
811 
812 		error.addTypedError("Actual value", value);
813 
814 		throw error;
815 
816 	}
817 }
818 
819 ///
820 unittest
821 {
822 	class T {}
823 
824 	string foo;
825 	int[] bar;
826 	T t;
827 
828 	foo.assertNull();
829 	bar.assertNull();
830 	t.assertNull();
831 	null.assertNull();
832 
833 	// Assert a DUnitAssertError is thrown if assertNull fails.
834 	"foo".assertNull().assertThrow!(DUnitAssertError)("Failed asserting null");
835 }
836 
837 /**
838  * Assert that a string starts with a particular string.
839  *
840  * Params:
841  *     value = The value used during the assertion.
842  *     prefix = The prefix to match.
843  *     message = The error message to display.
844  *     file = The file name where the error occurred. The value is added automatically at the call site.
845  *     line = The line where the error occurred. The value is added automatically at the call site.
846  *
847  * Throws:
848  *     DUnitAssertError if the assertation fails.
849  */
850 public void assertStartsWith(string value, string prefix, string message = "Failed asserting starts with", string file = __FILE__, size_t line = __LINE__)
851 {
852 	if (!startsWith(value, prefix))
853 	{
854 		auto error = new DUnitAssertError(message, file, line);
855 
856 		error.addExpectation("Expected start", prefix ~ "...");
857 		error.addError("Actual value", value);
858 
859 		throw error;
860 	}
861 }
862 
863 ///
864 unittest
865 {
866 	"foo bar".assertStartsWith("foo");
867 	"baz qux".assertStartsWith("baz");
868 
869 	// Assert a DUnitAssertError is thrown if assertStartsWith fails.
870 	"foo bar".assertStartsWith("baz").assertThrow!(DUnitAssertError)("Failed asserting starts with");
871 }
872 
873 /**
874  * Assert that an expression throws an exception.
875  *
876  * Params:
877  *     expression = The expression to evaluate in order to assert the exception is thrown.
878  *     expressionMsg = An optional expected message of the thrown exception.
879  *     message = The error message to display.
880  *     file = The file name where the error occurred. The value is added automatically at the call site.
881  *     line = The line where the error occurred. The value is added automatically at the call site.
882  *
883  * Throws:
884  *     DUnitAssertError if the assertation fails.
885  */
886 public void assertThrow(A : Throwable = Exception, B)(lazy B expression, string expressionMsg = null, string message = "Failed asserting throw", string file = __FILE__, size_t line = __LINE__)
887 {
888 	try
889 	{
890 		try
891 		{
892 			expression;
893 		}
894 		catch (A ex)
895 		{
896 			if (expressionMsg !is null && expressionMsg != ex.msg)
897 			{
898 				auto error = new DUnitAssertError(message, file, line);
899 
900 				error.addExpectation("Expected message", expressionMsg);
901 				error.addError("Thrown message", ex.msg);
902 
903 				throw error;
904 			}
905 			return;
906 		}
907 	}
908 	catch (Exception ex)
909 	{
910 		// If the expression throws an exception other than the one specified just let it pass.
911 		// We can't get any meaningful information about what was thrown anyway.
912 	}
913 
914 	auto error = new DUnitAssertError(message, file, line);
915 
916 	error.addError("Expected exception", A.stringof);
917 
918 	throw error;
919 }
920 
921 ///
922 unittest
923 {
924 	import core.exception : AssertError, RangeError;
925 
926 	class Foo : Exception
927 	{
928 		this(string message)
929 		{
930 			super(message);
931 		}
932 	}
933 
934 	class Bar
935 	{
936 		public void baz()
937 		{
938 			throw new Foo("Thrown from baz.");
939 		}
940 	}
941 
942 	auto bar = new Bar();
943 	bar.baz().assertThrow();
944 	bar.baz().assertThrow!(Foo)("Thrown from baz.");
945 
946 	delegate(){throw new Foo("Thrown from delegate.");}().assertThrow!(Exception)("Thrown from delegate.");
947 
948 	auto baz = [0, 1, 2];
949 	baz[3].assertThrow!(RangeError)();
950 
951 	assert(false).assertThrow!(AssertError)("Assertion failure");
952 
953 	// Assert a DUnitAssertError is thrown if assertThrow fails.
954 	null.assertThrow().assertThrow!(DUnitAssertError)("Failed asserting throw");
955 
956 	// Assert a DUnitAssertError is thrown if assertThrow fails due to mismatched error message.
957 	baz[3].assertThrow!(RangeError)("Foo").assertThrow!(DUnitAssertError)("Failed asserting throw");
958 }
959 
960 /**
961  * Assert that a boolean value is true.
962  *
963  * Params:
964  *     value = The value used during the assertion.
965  *     message = The error message to display.
966  *     file = The file name where the error occurred. The value is added automatically at the call site.
967  *     line = The line where the error occurred. The value is added automatically at the call site.
968  *
969  * Throws:
970  *     DUnitAssertError if the assertation fails.
971  */
972 public void assertTrue(T)(T value, string message = "Failed asserting true", string file = __FILE__, size_t line = __LINE__)
973 {
974 	value.assertType!(bool)("Wrong type for asserting true", file, line);
975 
976 	if (!value)
977 	{
978 		auto error = new DUnitAssertError(message, file, line);
979 
980 		error.addError("Value", value);
981 
982 		throw error;
983 	}
984 }
985 
986 ///
987 unittest
988 {
989 	true.assertTrue();
990 
991 	// Assert a DUnitAssertError is thrown if assertTrue fails.
992 	false.assertTrue().assertThrow!(DUnitAssertError)("Failed asserting true");
993 }
994 
995 /**
996  * Assert that a value evaluates as true.
997  *
998  * Params:
999  *     value = The value used during the assertion.
1000  *     message = The error message to display.
1001  *     file = The file name where the error occurred. The value is added automatically at the call site.
1002  *     line = The line where the error occurred. The value is added automatically at the call site.
1003  *
1004  * Throws:
1005  *     DUnitAssertError if the assertation fails.
1006  */
1007 public void assertTruthy(T)(T value, string message = "Failed asserting truthy", string file = __FILE__, size_t line = __LINE__)
1008 {
1009 	if (!value)
1010 	{
1011 		auto error = new DUnitAssertError(message, file, line);
1012 
1013 		error.addTypedInfo("Value", value);
1014 		error.addError("Evaluates to", !!value);
1015 
1016 		throw error;
1017 	}
1018 }
1019 
1020 ///
1021 unittest
1022 {
1023 	true.assertTruthy();
1024 	["foo"].assertTruthy();
1025 	1.assertTruthy();
1026 
1027 	// Assert a DUnitAssertError is thrown if assertTruthy fails.
1028 	false.assertTruthy().assertThrow!(DUnitAssertError)("Failed asserting truthy");
1029 }
1030 
1031 /**
1032  * Assert that a value is of a particular type.
1033  *
1034  * Params:
1035  *     value = The value used during the assertion.
1036  *     message = The error message to display.
1037  *     file = The file name where the error occurred. The value is added automatically at the call site.
1038  *     line = The line where the error occurred. The value is added automatically at the call site.
1039  *
1040  * Throws:
1041  *     DUnitAssertError if the assertation fails.
1042  */
1043 public void assertType(A, B)(B value, string message = "Failed asserting type", string file = __FILE__, size_t line = __LINE__)
1044 {
1045 	if (!is(A == B))
1046 	{
1047 		auto error = new DUnitAssertError(message, file, line);
1048 
1049 		error.addExpectation("Expected type", A.stringof);
1050 		error.addError("Actual type", B.stringof);
1051 
1052 		throw error;
1053 	}
1054 }
1055 
1056 ///
1057 unittest
1058 {
1059 	1.assertType!(int)();
1060 	"foo".assertType!(string)();
1061 	["bar"].assertType!(string[])();
1062 	['a'].assertType!(char[])();
1063 
1064 	// Assert a DUnitAssertError is thrown if assertType fails.
1065 	false.assertType!(string)().assertThrow!(DUnitAssertError)("Failed asserting type");
1066 }