1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2012 Juan Manuel Cabo
10 Copyright (C) 2017 Mario Kröplin
11 Copyright (C) 2019-2020 Aurora Free Open Source Software.
12 Copyright (C) 2019-2020 Luís Ferreira <luis@aurorafoss.org>
13 
14 This file is part of the Aurora Free Open Source Software. This
15 organization promote free and open source software that you can
16 redistribute and/or modify under the terms of the GNU Lesser General
17 Public License Version 3 as published by the Free Software Foundation or
18 (at your option) any later version approved by the Aurora Free Open Source
19 Software Organization. The license is available in the package root path
20 as 'LICENSE' file. Please review the following information to ensure the
21 GNU Lesser General Public License version 3 requirements will be met:
22 https://www.gnu.org/licenses/lgpl.html .
23 
24 Alternatively, this file may be used under the terms of the GNU General
25 Public License version 3 or later as published by the Free Software
26 Foundation. Please review the following information to ensure the GNU
27 General Public License requirements will be met:
28 https://www.gnu.org/licenses/gpl-3.0.html.
29 
30 NOTE: All products, services or anything associated to trademarks and
31 service marks used or referenced on this file are the property of their
32 respective companies/owners or its subsidiaries. Other names and brands
33 may be claimed as the property of others.
34 
35 For more info about intellectual property visit: aurorafoss.org or
36 directly send an email to: contact (at) aurorafoss.org .
37 
38 This file was based on DUnit framework.
39 More about DUnit: https://github.com/linkrope/dunit
40 */
41 
42 module aurorafw.unit.assertion;
43 
44 import core.thread;
45 import core.time;
46 import std.algorithm;
47 import std.array;
48 import std.conv;
49 import std.range;
50 import std.string;
51 import std.traits;
52 import std.typecons;
53 
54 public import std.exception;
55 public import core.exception;
56 
57 /**
58  * Thrown on an assertion failure.
59  */
60 @safe pure
61 class AssertException : Exception
62 {
63 	@safe pure nothrow this(string msg,
64 			string file = __FILE__,
65 			size_t line = __LINE__,
66 			Throwable next = null)
67 	{
68 		super(msg.empty ? "Assertion failure" : msg, file, line, next);
69 	}
70 }
71 
72 /**
73  * Thrown on an assertion failure.
74  */
75 @safe pure
76 class AssertAllException : AssertException
77 {
78 	private AssertException[] exceptions;
79 
80 	@safe pure nothrow this(AssertException[] exceptions,
81 			string file = __FILE__,
82 			size_t line = __LINE__,
83 			Throwable next = null)
84 	{
85 		string msg = heading(exceptions.length);
86 
87 		exceptions.each!(exception => msg ~= '\n' ~ exception.description);
88 		this.exceptions = exceptions;
89 		super(msg, file, line, next);
90 	}
91 
92 	private @safe pure nothrow static string heading(size_t count)
93 	{
94 		if (count == 1)
95 			return "1 assertion failure:";
96 		else
97 			return text(count, " assertion failures:");
98 	}
99 }
100 
101 @safe pure
102 @("Assertion: Assert Exceptions")
103 unittest
104 {
105 	try
106 	{
107 		throw new AssertAllException([new AssertException("simple assert from unittesting")]);
108 	}
109 	catch (AssertAllException e)
110 	{
111 		import std.algorithm.searching : startsWith;
112 
113 		assert(e.msg.startsWith("1 assertion failure:"));
114 	}
115 }
116 
117 /**
118  * Returns a description of the throwable.
119  */
120 @safe pure nothrow string description(Throwable throwable)
121 {
122 	with (throwable)
123 	{
124 		if (file.empty)
125 			return text(typeid(throwable).name, ": ", msg);
126 		else
127 			return text(typeid(throwable).name, "@", file, "(", line, "): ", msg);
128 	}
129 }
130 
131 ///
132 @safe pure
133 @("Assertion: Throwable description")
134 unittest
135 {
136 	assert(description(new Throwable("foobar"))
137 			== "object.Throwable: foobar");
138 
139 	assert(description(new Throwable("foobar", "foo.d", 42))
140 			== "object.Throwable@foo.d(42): foobar");
141 }
142 
143 /**
144  * Returns a description of the difference between the strings.
145  */
146 string description(string expected, string actual) @safe pure
147 {
148 	const MAX_LENGTH = 20;
149 	const result = diff(expected, actual);
150 	const oneLiner = max(result[0].length, result[1].length) <= MAX_LENGTH
151 		&& !result[0].canFind("\n", "\r")
152 		&& !result[1].canFind("\n", "\r");
153 
154 	if (oneLiner)
155 		return "expected: <" ~ result[0] ~ "> but was: <" ~ result[1] ~ ">";
156 	else
157 		return "expected:\n" ~ result[0] ~ "\nbut was:\n" ~ result[1];
158 }
159 
160 ///
161 @safe pure
162 @("Assertion: diff description")
163 unittest
164 {
165 	assert(description("ab", "Ab") == "expected: <<a>b> but was: <<A>b>");
166 	assert(description("a\nb", "A\nb") == "expected:\n<a>\nb\nbut was:\n<A>\nb");
167 }
168 
169 /**
170  * Returns a pair of strings that highlight the difference between lhs and rhs.
171  */
172 @safe pure
173 Tuple!(string, string) diff(string)(string lhs, string rhs)
174 {
175 	const MAX_LENGTH = 20;
176 
177 	if (lhs == rhs)
178 		return tuple(lhs, rhs);
179 
180 	auto rest = mismatch(lhs, rhs);
181 	auto retroDiff = mismatch(retro(rest[0]), retro(rest[1]));
182 	auto diff = tuple(retro(retroDiff[0]), retro(retroDiff[1]));
183 	string prefix = lhs[0 .. $ - rest[0].length];
184 	string suffix = lhs[prefix.length + diff[0].length .. $];
185 
186 	if (prefix.length > MAX_LENGTH)
187 		prefix = "..." ~ prefix[$ - MAX_LENGTH .. $];
188 	if (suffix.length > MAX_LENGTH)
189 		suffix = suffix[0 .. MAX_LENGTH] ~ "...";
190 
191 	return tuple(
192 			prefix ~ '<' ~ diff[0] ~ '>' ~ suffix,
193 			prefix ~ '<' ~ diff[1] ~ '>' ~ suffix);
194 }
195 
196 ///
197 @safe pure
198 @("Assertion: diff highlighter")
199 unittest
200 {
201 	assert(diff("abc", "abc") == tuple("abc", "abc"));
202 	// highlight difference
203 	assert(diff("abc", "Abc") == tuple("<a>bc", "<A>bc"));
204 	assert(diff("abc", "aBc") == tuple("a<b>c", "a<B>c"));
205 	assert(diff("abc", "abC") == tuple("ab<c>", "ab<C>"));
206 	assert(diff("abc", "") == tuple("<abc>", "<>"));
207 	assert(diff("abc", "abbc") == tuple("ab<>c", "ab<b>c"));
208 	// abbreviate long prefix or suffix
209 	assert(diff("_12345678901234567890a", "_12345678901234567890A")
210 			== tuple("...12345678901234567890<a>", "...12345678901234567890<A>"));
211 	assert(diff("a12345678901234567890_", "A12345678901234567890_")
212 			== tuple("<a>12345678901234567890...", "<A>12345678901234567890..."));
213 }
214 
215 /**
216  * Asserts that a condition is true.
217  * Throws: AssertException otherwise
218  */
219 @safe pure
220 void assertTrue(T)(T condition, lazy string msg = null,
221 		string file = __FILE__,
222 		size_t line = __LINE__)
223 {
224 	if (cast(bool) condition)
225 		return;
226 
227 	fail(msg, file, line);
228 }
229 
230 ///
231 @safe pure
232 @("Assertion: assertTrue")
233 unittest
234 {
235 	assertTrue(true);
236 	assertTrue("foo" in ["foo" : "bar"]);
237 
238 	auto exception = expectThrows!AssertException(assertTrue(false));
239 
240 	assertEquals("Assertion failure", exception.msg);
241 }
242 
243 /**
244  * Asserts that a condition is false.
245  * Throws: AssertException otherwise
246  */
247 @safe pure
248 void assertFalse(T)(T condition, lazy string msg = null,
249 		string file = __FILE__,
250 		size_t line = __LINE__)
251 {
252 	if (!cast(bool) condition)
253 		return;
254 
255 	fail(msg, file, line);
256 }
257 
258 ///
259 @safe pure
260 @("Assertion: assertFalse")
261 unittest
262 {
263 	assertFalse(false);
264 	assertFalse("foo" in ["bar" : "foo"]);
265 
266 	auto exception = expectThrows!AssertException(assertFalse(true));
267 
268 	assertEquals("Assertion failure", exception.msg);
269 }
270 
271 /**
272  * Asserts that the string values are equal.
273  * Throws: AssertException otherwise
274  */
275 @safe pure
276 void assertEquals(T, U)(T expected, U actual, lazy string msg = null,
277 		string file = __FILE__,
278 		size_t line = __LINE__) if (isSomeString!T)
279 {
280 	if (expected == actual)
281 		return;
282 
283 	string header = (msg.empty) ? null : msg ~ "; ";
284 
285 	fail(header ~ description(expected.to!string, actual.to!string),
286 			file, line);
287 }
288 
289 ///
290 @safe pure
291 @("Assertion: assertEquals for strings")
292 unittest
293 {
294 	assertEquals("foo", "foo");
295 
296 	auto exception = expectThrows!AssertException(assertEquals("bar", "baz"));
297 
298 	assertEquals("expected: <ba<r>> but was: <ba<z>>", exception.msg);
299 }
300 
301 /**
302  * Asserts that the floating-point values are approximately equal.
303  * Throws: AssertException otherwise
304  */
305 @safe
306 void assertEquals(T, U)(T expected, U actual, lazy string msg = null,
307 		string file = __FILE__,
308 		size_t line = __LINE__) if (isFloatingPoint!T || isFloatingPoint!U)
309 {
310 	import std.math : approxEqual;
311 
312 	if (approxEqual(expected, actual))
313 		return;
314 
315 	string header = (msg.empty) ? null : msg ~ "; ";
316 
317 	fail(header ~ format("expected: <%s> but was: <%s>", expected, actual),
318 			file, line);
319 }
320 
321 ///
322 @safe /*pure*/
323 @("Assertion: assertEquals for floating-points")
324 unittest  // format is impure for floating point values
325 {
326 	assertEquals(1, 1.01);
327 
328 	auto exception = expectThrows!AssertException(assertEquals(1, 1.1));
329 
330 	assertEquals("expected: <1> but was: <1.1>", exception.msg);
331 }
332 
333 /**
334  * Asserts that the values are equal.
335  * Throws: AssertException otherwise
336  */
337 void assertEquals(T, U)(T expected, U actual, lazy string msg = null,
338 		string file = __FILE__,
339 		size_t line = __LINE__) if (!isSomeString!T && !isFloatingPoint!T && !isFloatingPoint!U)
340 {
341 	if (expected == actual)
342 		return;
343 
344 	string header = (msg.empty) ? null : msg ~ "; ";
345 
346 	fail(header ~ format("expected: <%s> but was: <%s>", expected, actual),
347 			file, line);
348 }
349 
350 ///
351 @safe pure
352 @("Assertion: generalized assertEquals")
353 unittest
354 {
355 	assertEquals(42, 42);
356 
357 	auto exception = expectThrows!AssertException(assertEquals(42, 24));
358 
359 	assertEquals("expected: <42> but was: <24>", exception.msg);
360 }
361 
362 ///
363 @system
364 @("Assertion: assertEquals with Object")
365 unittest  // Object.opEquals is impure
366 {
367 	Object foo = new Object();
368 	Object bar = null;
369 
370 	assertEquals(foo, foo);
371 	assertEquals(bar, bar);
372 
373 	auto exception = expectThrows!AssertException(assertEquals(foo, bar));
374 
375 	assertEquals("expected: <object.Object> but was: <null>", exception.msg);
376 }
377 
378 /**
379  * Asserts that the arrays are equal.
380  * Throws: AssertException otherwise
381  */
382 @safe pure
383 void assertArrayEquals(T, U)(in T[] expected, in U[] actual, lazy string msg = null,
384 		string file = __FILE__,
385 		size_t line = __LINE__)
386 {
387 	assertRangeEquals(expected, actual,
388 			msg,
389 			file, line);
390 }
391 
392 /**
393  * Asserts that the static arrays are equal.
394  * Throws: AssertException otherwise
395  */
396 @safe pure
397 void assertArrayEquals(T, U, size_t l1, size_t l2)(
398 		auto ref const T[l1] expected, auto ref const U[l2] actual, lazy string msg = null,
399 		string file = __FILE__,
400 		size_t line = __LINE__)
401 {
402 	string header = (msg.empty) ? null : msg ~ "; ";
403 
404 	assertEquals(expected.length, actual.length,
405 			header ~ "length mismatch",
406 			file, line);
407 
408 	foreach (idx, val; expected)
409 		assertEquals(val, actual[idx],
410 				header ~ format("mismatch at index %s", idx),
411 				file, line);
412 }
413 
414 ///
415 @safe pure
416 @("Assertion: Static arrays")
417 unittest
418 {
419 	int[4] expected = [1, 2, 3, 4];
420 
421 	assertArrayEquals(expected, [1, 2, 3, 4]);
422 
423 	AssertException exception;
424 
425 	exception = expectThrows!AssertException(assertArrayEquals(expected, [1, 2]));
426 	assertEquals(`length mismatch; expected: <4> but was: <2>`, exception.msg);
427 	exception = expectThrows!AssertException(assertArrayEquals(expected, [1, 2, 5, 4]));
428 	assertEquals(`mismatch at index 2; expected: <3> but was: <5>`, exception.msg);
429 }
430 
431 /**
432  * Asserts that the associative arrays are equal.
433  * Throws: AssertException otherwise
434  */
435 pure
436 void assertArrayEquals(T, U, V)(in T[V] expected, in U[V] actual, lazy string msg = null,
437 		string file = __FILE__,
438 		size_t line = __LINE__)
439 {
440 	string header = (msg.empty) ? null : msg ~ "; ";
441 
442 	foreach (key; expected.byKey)
443 		if (key in actual)
444 		{
445 			assertEquals(expected[key], actual[key],
446 					format(header ~ "mismatch at key %s", key.repr),
447 					file, line);
448 		}
449 
450 	auto difference = setSymmetricDifference(expected.keys.sort(), actual.keys.sort());
451 
452 	assertEmpty(difference,
453 			format("key mismatch; difference: %(%s, %)", difference),
454 			file, line);
455 }
456 
457 /// ditto
458 void assertAssocArrayEquals(T, U, V)(in T[V] expected, in U[V] actual, lazy string msg = null,
459 		string file = __FILE__,
460 		size_t line = __LINE__)
461 {
462 	assertArrayEquals(expected, actual,
463 			msg,
464 			file, line);
465 }
466 
467 ///
468 @system pure
469 @("Assertion: Associative Arrays comparison")
470 unittest  // keys, values, byKey, byValue not usable in @safe context
471 {
472 	int[string] expected = ["foo" : 1, "bar" : 2];
473 
474 	assertArrayEquals(expected, ["foo" : 1, "bar" : 2]);
475 	assertAssocArrayEquals(expected, ["foo" : 1, "bar" : 2]);
476 
477 	AssertException exception;
478 
479 	exception = expectThrows!AssertException(assertArrayEquals(expected, ["foo" : 2]));
480 	assertEquals(`mismatch at key "foo"; expected: <1> but was: <2>`, exception.msg);
481 	exception = expectThrows!AssertException(assertArrayEquals(expected, ["foo" : 1]));
482 	assertEquals(`key mismatch; difference: "bar"`, exception.msg);
483 	exception = expectThrows!AssertException(assertAssocArrayEquals(expected, ["foo" : 2]));
484 	assertEquals(`mismatch at key "foo"; expected: <1> but was: <2>`, exception.msg);
485 	exception = expectThrows!AssertException(assertAssocArrayEquals(expected, ["foo" : 1]));
486 	assertEquals(`key mismatch; difference: "bar"`, exception.msg);
487 }
488 
489 /**
490  * Asserts that the ranges are equal.
491  * Throws: AssertException otherwise
492  */
493 void assertRangeEquals(R1, R2)(R1 expected, R2 actual, lazy string msg = null,
494 		string file = __FILE__,
495 		size_t line = __LINE__) if (isInputRange!R1 && isInputRange!R2 && is(typeof(expected.front == actual.front)))
496 {
497 	string header = (msg.empty) ? null : msg ~ "; ";
498 	size_t index = 0;
499 
500 	for (; !expected.empty && !actual.empty; ++index, expected.popFront, actual.popFront)
501 	{
502 		assertEquals(expected.front, actual.front,
503 				header ~ format("mismatch at index %s", index),
504 				file, line);
505 	}
506 	assertEmpty(expected,
507 			header ~ format("length mismatch at index %s; ", index) ~
508 			format("expected: <%s> but was: empty", expected.front),
509 			file, line);
510 	assertEmpty(actual,
511 			header ~ format("length mismatch at index %s; ", index) ~
512 			format("expected: empty but was: <%s>", actual.front),
513 			file, line);
514 }
515 
516 ///
517 @safe pure
518 @("Assertion: Array comparison")
519 unittest
520 {
521 	string expected = "atum";
522 
523 	assertArrayEquals(expected, "atum");
524 
525 	AssertException exception;
526 
527 	exception = expectThrows!AssertException(assertRangeEquals(expected, "atu"));
528 	assertEquals("length mismatch at index 3; expected: <m> but was: empty", exception.msg);
529 	exception = expectThrows!AssertException(assertRangeEquals(expected, "atumm"));
530 	assertEquals("length mismatch at index 4; expected: empty but was: <m>", exception.msg);
531 	exception = expectThrows!AssertException(assertArrayEquals(expected, "arum"));
532 	assertEquals("mismatch at index 1; expected: <t> but was: <r>", exception.msg);
533 }
534 
535 ///
536 @safe pure
537 @("Assertion: Range comparison")
538 unittest
539 {
540 	int[] expected = [0, 1];
541 
542 	assertRangeEquals(expected, [0, 1]);
543 
544 	AssertException exception;
545 
546 	exception = expectThrows!AssertException(assertRangeEquals(expected, [0]));
547 	assertEquals("length mismatch at index 1; expected: <1> but was: empty", exception.msg);
548 	exception = expectThrows!AssertException(assertRangeEquals(expected, [0, 1, 2]));
549 	assertEquals("length mismatch at index 2; expected: empty but was: <2>", exception.msg);
550 	exception = expectThrows!AssertException(assertArrayEquals(expected, [0, 2]));
551 	assertEquals("mismatch at index 1; expected: <1> but was: <2>", exception.msg);
552 }
553 
554 /**
555  * Asserts that the value is empty.
556  * Throws: AssertException otherwise
557  */
558 @safe pure
559 void assertEmpty(T)(T actual, lazy string msg = null,
560 		string file = __FILE__,
561 		size_t line = __LINE__)
562 {
563 	if (actual.empty)
564 		return;
565 
566 	fail(msg, file, line);
567 }
568 
569 ///
570 @safe pure
571 @("Assertion: assertEmpty")
572 unittest
573 {
574 	assertEmpty([]);
575 
576 	auto exception = expectThrows!AssertException(assertEmpty([1, 2, 3]));
577 
578 	assertEquals("Assertion failure", exception.msg);
579 }
580 
581 /**
582  * Asserts that the value is not empty.
583  * Throws: AssertException otherwise
584  */
585 @safe pure
586 void assertNotEmpty(T)(T actual, lazy string msg = null,
587 		string file = __FILE__,
588 		size_t line = __LINE__)
589 {
590 	if (!actual.empty)
591 		return;
592 
593 	fail(msg, file, line);
594 }
595 
596 ///
597 @safe pure
598 @("Assertion: assertNotEmpty")
599 unittest
600 {
601 	assertNotEmpty([1, 2, 3]);
602 
603 	auto exception = expectThrows!AssertException(assertNotEmpty([]));
604 
605 	assertEquals("Assertion failure", exception.msg);
606 }
607 
608 /**
609  * Asserts that the value is null.
610  * Throws: AssertException otherwise
611  */
612 @safe pure
613 void assertNull(T)(T actual, lazy string msg = null,
614 		string file = __FILE__,
615 		size_t line = __LINE__)
616 {
617 	if (actual is null)
618 		return;
619 
620 	fail(msg, file, line);
621 }
622 
623 ///
624 @safe pure
625 @("Assertion: assertNull")
626 unittest
627 {
628 	Object foo = new Object();
629 
630 	assertNull(null);
631 
632 	auto exception = expectThrows!AssertException(assertNull(foo));
633 
634 	assertEquals("Assertion failure", exception.msg);
635 }
636 
637 /**
638  * Asserts that the value is not null.
639  * Throws: AssertException otherwise
640  */
641 @safe pure
642 void assertNotNull(T)(T actual, lazy string msg = null,
643 		string file = __FILE__,
644 		size_t line = __LINE__)
645 {
646 	if (actual !is null)
647 		return;
648 
649 	fail(msg, file, line);
650 }
651 
652 ///
653 @safe pure
654 @("Assertion: assertNotNull")
655 unittest
656 {
657 	Object foo = new Object();
658 
659 	assertNotNull(foo);
660 
661 	auto exception = expectThrows!AssertException(assertNotNull(null));
662 
663 	assertEquals("Assertion failure", exception.msg);
664 }
665 
666 /**
667  * Asserts that the values are the same.
668  * Throws: AssertException otherwise
669  */
670 void assertSame(T, U)(T expected, U actual, lazy string msg = null,
671 		string file = __FILE__,
672 		size_t line = __LINE__)
673 {
674 	if (expected is actual)
675 		return;
676 
677 	string header = (msg.empty) ? null : msg ~ "; ";
678 
679 	fail(header ~ format("expected same: <%s> was not: <%s>", expected, actual),
680 			file, line);
681 }
682 
683 ///
684 @system
685 @("Assertion: assertSame")
686 unittest  // format is impure and not safe for Object
687 {
688 	Object foo = new Object();
689 	Object bar = new Object();
690 
691 	assertSame(foo, foo);
692 
693 	auto exception = expectThrows!AssertException(assertSame(foo, bar));
694 
695 	assertEquals("expected same: <object.Object> was not: <object.Object>", exception.msg);
696 }
697 
698 /**
699  * Asserts that the values are not the same.
700  * Throws: AssertException otherwise
701  */
702 @safe pure
703 void assertNotSame(T, U)(T expected, U actual, lazy string msg = null,
704 		string file = __FILE__,
705 		size_t line = __LINE__)
706 {
707 	if (expected !is actual)
708 		return;
709 
710 	string header = (msg.empty) ? null : msg ~ "; ";
711 
712 	fail(header ~ "expected not same",
713 			file, line);
714 }
715 
716 ///
717 @safe pure
718 @("Assertion: assertNotSame")
719 unittest
720 {
721 	Object foo = new Object();
722 	Object bar = new Object();
723 
724 	assertNotSame(foo, bar);
725 
726 	auto exception = expectThrows!AssertException(assertNotSame(foo, foo));
727 
728 	assertEquals("expected not same", exception.msg);
729 }
730 
731 @system pure
732 @("Assertion: unsafe assertNotSame")
733 unittest
734 {
735 	int foobar = 1;
736 	int* refFoobar = &foobar;
737 
738 	// use unsafe stuff on @safe function
739 	assertNotSame(null, refFoobar);
740 }
741 
742 /**
743  * Asserts that all assertions pass.
744  * Throws: AssertAllException otherwise
745  */
746 void assertAll(T)(T[] assertions...) if (isCallable!T)
747 {
748 	AssertException[] exceptions = null;
749 
750 	foreach (assertion; assertions)
751 		try
752 			assertion();
753 		catch (AssertException exception)
754 			exceptions ~= exception;
755 	if (!exceptions.empty)
756 	{
757 		// [Issue 16345] IFTI fails with lazy variadic function in some cases
758 		const file = null;
759 		const line = 0;
760 
761 		throw new AssertAllException(exceptions, file, line);
762 	}
763 }
764 
765 /// ditto
766 void assertAll(void delegate()[] assertions...)
767 {
768 	assertAll!(void delegate())(assertions);
769 }
770 
771 /// ditto
772 void assertAll(void function()[] assertions...)
773 {
774 	assertAll!(void function())(assertions);
775 }
776 
777 ///
778 @system
779 @("Assertion: assertAll")
780 unittest
781 {
782 	assertAll(
783 			assertTrue(true),
784 			assertFalse(false),
785 	);
786 
787 	auto exception = expectThrows!AssertException(assertAll(
788 			assertTrue(false),
789 			assertFalse(true),
790 	));
791 
792 	assertTrue(exception.msg.canFind("2 assertion failures"), exception.msg);
793 }
794 
795 ///
796 @safe
797 @("Assertion: safe assertAll")
798 unittest
799 {
800 	assertAll!(void delegate() @safe)(
801 			assertTrue(true),
802 			assertFalse(false),
803 	);
804 }
805 
806 ///
807 pure
808 @("Assertion: pure assertAll")
809 unittest
810 {
811 	assertAll!(void delegate() pure)(
812 			assertTrue(true),
813 			assertFalse(false),
814 	);
815 }
816 
817 /**
818  * Asserts that the expression throws the specified throwable.
819  * Throws: AssertException otherwise
820  * Returns: the caught throwable
821  */
822 T expectThrows(T : Throwable = Exception, E)(lazy E expression, lazy string msg = null,
823 		string file = __FILE__,
824 		size_t line = __LINE__)
825 {
826 	try
827 		expression();
828 	catch (T throwable)
829 		return throwable;
830 
831 	string header = (msg.empty) ? null : msg ~ "; ";
832 
833 	fail(header ~ format("expected <%s> was not thrown", T.stringof),
834 			file, line);
835 	assert(0);
836 }
837 
838 ///
839 @safe pure
840 @("Assertion: expectThrows")
841 unittest
842 {
843 	import std.exception : enforce;
844 
845 	auto exception = expectThrows(enforce(false));
846 
847 	assertEquals("Enforcement failed", exception.msg);
848 }
849 
850 ///
851 @safe pure
852 @("Assertion: Specific exception expectThrows")
853 unittest
854 {
855 	auto exception = expectThrows!AssertException(expectThrows(42));
856 
857 	assertEquals("expected <Exception> was not thrown", exception.msg);
858 }
859 
860 /**
861  * Fails a test.
862  * Throws: AssertException
863  */
864 @safe pure
865 void fail(string msg = null,
866 		string file = __FILE__,
867 		size_t line = __LINE__)
868 {
869 	throw new AssertException(msg, file, line);
870 }
871 
872 ///
873 @safe pure
874 @("Assertion: fail function")
875 unittest
876 {
877 	auto exception = expectThrows!AssertException(fail());
878 
879 	assertEquals("Assertion failure", exception.msg);
880 }
881 
882 alias assertGreaterThan = assertOp!">";
883 alias assertGreaterThanOrEqual = assertOp!">=";
884 alias assertLessThan = assertOp!"<";
885 alias assertLessThanOrEqual = assertOp!"<=";
886 alias assertIn = assertOp!"in";
887 alias assertNotIn = assertOp!"!in";
888 
889 /**
890  * Asserts that the condition (lhs op rhs) is satisfied.
891  * Throws: AssertException otherwise
892  * See_Also: http://d.puremagic.com/issues/show_bug.cgi?id=4653
893  */
894 template assertOp(string op)
895 {
896 	void assertOp(T, U)(T lhs, U rhs, lazy string msg = null,
897 			string file = __FILE__,
898 			size_t line = __LINE__)
899 	{
900 		mixin("if (lhs " ~ op ~ " rhs) return;");
901 
902 		string header = (msg.empty) ? null : msg ~ "; ";
903 
904 		fail(format("%scondition (%s %s %s) not satisfied",
905 				header, lhs.repr, op, rhs.repr),
906 				file, line);
907 	}
908 }
909 
910 ///
911 @safe pure
912 @("Assertion: assertOp's")
913 unittest
914 {
915 	assertLessThan(2, 3);
916 
917 	auto exception = expectThrows!AssertException(assertGreaterThanOrEqual(2, 3));
918 
919 	assertEquals("condition (2 >= 3) not satisfied", exception.msg);
920 }
921 
922 ///
923 @safe pure
924 @("Assertion: assertIn & assertNotIn")
925 unittest
926 {
927 	assertIn("foo", ["foo" : "bar"]);
928 
929 	auto exception = expectThrows!AssertException(assertNotIn("foo", ["foo" : "bar"]));
930 
931 	assertEquals(`condition ("foo" !in ["foo":"bar"]) not satisfied`, exception.msg);
932 }
933 
934 /**
935  * Checks a probe until the timeout expires. The assert error is produced
936  * if the probe fails to return 'true' before the timeout.
937  *
938  * The parameter timeout determines the maximum timeout to wait before
939  * asserting a failure (default is 500ms).
940  *
941  * The parameter delay determines how often the predicate will be
942  * checked (default is 10ms).
943  *
944  * This kind of assertion is very useful to check on code that runs in another
945  * thread. For instance, the thread that listens to a socket.
946  *
947  * Throws: AssertException when the probe fails to become true before timeout
948  */
949 void assertEventually(T)(T probe,
950 		Duration timeout = 500.msecs, Duration delay = 10.msecs,
951 		lazy string msg = null,
952 		string file = __FILE__,
953 		size_t line = __LINE__) if (isCallable!T)
954 {
955 	const startTime = TickDuration.currSystemTick();
956 
957 	while (!probe())
958 	{
959 		const elapsedTime = cast(Duration)(TickDuration.currSystemTick() - startTime);
960 
961 		if (elapsedTime >= timeout)
962 			fail(msg.empty ? "timed out" : msg, file, line);
963 
964 		Thread.sleep(delay);
965 	}
966 }
967 
968 /// ditto
969 void assertEventually(bool delegate() probe,
970 		Duration timeout = 500.msecs, Duration delay = 10.msecs,
971 		lazy string msg = null,
972 		string file = __FILE__,
973 		size_t line = __LINE__)
974 {
975 	assertEventually!(bool delegate())(probe, timeout, delay, msg,
976 			file, line);
977 }
978 
979 /// ditto
980 void assertEventually(bool function() probe,
981 		Duration timeout = 500.msecs, Duration delay = 10.msecs,
982 		lazy string msg = null,
983 		string file = __FILE__,
984 		size_t line = __LINE__)
985 {
986 	assertEventually!(bool function())(probe, timeout, delay, msg,
987 			file, line);
988 }
989 
990 ///
991 @("Assertation: assertEventually")
992 unittest
993 {
994 	// this should complete right after the first probe
995 	assertEventually({ return true; });
996 	// test using delegate implementation
997 	assertEventually(delegate() { return true; });
998 
999 	assertEventually(
1000 	{ static count = 0; return ++count > 23; }, // make sure every slow/heavy-loaded computers/CI do this unittest
1001 			// e.g.: Travis-CI due to high number of parallel jobs, can't do it in
1002 			// time.
1003 			1000.msecs,
1004 			1.msecs
1005 	);
1006 
1007 	// this should never complete and eventually timeout.
1008 	auto exception = expectThrows!AssertException(assertEventually({ return false; }));
1009 
1010 	assertEquals("timed out", exception.msg);
1011 }
1012 
1013 /**
1014  * Asserts that the item exists inside the given array
1015  * Throws: AssertException otherwise
1016  */
1017 @safe pure
1018 void assertExistsInArray(T, U)(T haystack, U needle, lazy string msg = null,
1019 		string file = __FILE__,
1020 		size_t line = __LINE__) if (isArray!T && is(typeof(haystack.front == needle || is(T == void[]))))
1021 {
1022 	string header = (msg.empty) ? null : msg ~ "; ";
1023 
1024 	if (haystack.empty)
1025 		fail(header ~ format("expected <%s> inside of the array but it was empty", needle),
1026 				file, line);
1027 
1028 	foreach (value; haystack)
1029 		if (value == needle)
1030 			return;
1031 
1032 	fail(header ~ format("expected <%s> inside of the array but not found", needle),
1033 			file, line);
1034 }
1035 
1036 /// ditto
1037 @safe pure
1038 void assertExists(T)(in T[] haystack, T needle, lazy string msg = null,
1039 		string file = __FILE__,
1040 		size_t line = __LINE__)
1041 {
1042 	assertExistsInArray(haystack, needle, msg,
1043 			file, line);
1044 }
1045 
1046 /**
1047  * Asserts that a item exists inside the given static array
1048  * Throws: AssertException otherwise
1049  */
1050 @safe pure
1051 void assertExists(T, size_t len)(auto ref const T[len] haystack, T needle, lazy string msg = null,
1052 		string file = __FILE__,
1053 		size_t line = __LINE__)
1054 {
1055 	assertExistsInArray(haystack, needle, msg,
1056 			file, line);
1057 }
1058 
1059 /**
1060  * Asserts that a item exists inside a range.
1061  * Throws: AssertException otherwise
1062  */
1063 @safe pure
1064 void assertExists(R, T)(R haystack, T needle, lazy string msg = null,
1065 		string file = __FILE__,
1066 		size_t line = __LINE__) if (isInputRange!R && is(typeof(haystack.front == needle)))
1067 {
1068 	string header = (msg.empty) ? null : msg ~ "; ";
1069 	size_t index = 0;
1070 
1071 	if (haystack.empty)
1072 		fail(header ~ format("expected <%s> inside of the range but it was empty", needle),
1073 				file, line);
1074 
1075 	for (; !haystack.empty; ++index, haystack.popFront)
1076 		if (haystack.front == needle)
1077 			return;
1078 
1079 	fail(header ~ format("expected <%s> inside of the range but not found", needle),
1080 			file, line);
1081 }
1082 
1083 @safe pure
1084 @("Assertion: Needle exists in a haystack")
1085 unittest
1086 {
1087 	int[] haystack = [1, 2, 3, 4, 5];
1088 	int[5] staticHaystack = [1, 2, 3, 4, 5];
1089 
1090 	assertExists(haystack, 5);
1091 	assertExists(staticHaystack, 5);
1092 
1093 	AssertException exception;
1094 
1095 	exception = expectThrows!AssertException(assertExists(haystack, 6));
1096 	assertEquals(`expected <6> inside of the range but not found`, exception.msg);
1097 	exception = expectThrows!AssertException(assertExists(staticHaystack, 6));
1098 	assertEquals(`expected <6> inside of the array but not found`, exception.msg);
1099 
1100 	int[] emptyHaystack = [];
1101 	// assuming dynamic array assigned to null, so it uses the range implementation
1102 	exception = expectThrows!AssertException(assertExists(emptyHaystack, 5));
1103 	assertEquals(`expected <5> inside of the range but it was empty`, exception.msg);
1104 	// assumming array on void[] type (useful on mixins and templates)
1105 	exception = expectThrows!AssertException(assertExists([], 5));
1106 	assertEquals(`expected <5> inside of the array but it was empty`, exception.msg);
1107 }
1108 
1109 pure
1110 @("Assertion: unsafe assertExists")
1111 unittest
1112 {
1113 	int a;
1114 	int b = 2;
1115 	int c = -5;
1116 	void*[] unsafeHaystack = [&a, &b, null];
1117 
1118 	assertExists(unsafeHaystack, &b);
1119 	// test with void*[] and typeof(null)
1120 	assertExists(unsafeHaystack, null);
1121 	expectThrows!AssertException(assertExists(unsafeHaystack, &c));
1122 }
1123 
1124 /**
1125  * Asserts that the array contains the given slice
1126  * Throws: AssertException otherwise
1127  */
1128 @safe pure
1129 void assertContains(T)(T[] array, T[] slice, lazy string msg = null,
1130 		string file = __FILE__,
1131 		size_t line = __LINE__)
1132 {
1133 	string header = (msg.empty) ? null : msg ~ "; ";
1134 
1135 	if (array.length < slice.length)
1136 		fail(header ~ format("slice length <%d> should be less or equal than <%d>",
1137 				slice.length, array.length),
1138 				file, line);
1139 
1140 	if (canFind(array, slice))
1141 		return;
1142 
1143 	fail(header ~ format("expected array containing the slice <%s>, but not found", slice),
1144 			file, line);
1145 }
1146 
1147 @safe pure
1148 @("Assertion: Array contains a slice")
1149 unittest
1150 {
1151 	int[] array = [1, 2, 3, 4, 5];
1152 	int[5] staticArray = [1, 2, 3, 4, 5];
1153 	int[2] staticSlice = [4, 5];
1154 
1155 	assertContains(array, [4, 5]);
1156 	assertContains(staticArray, [4, 5]);
1157 	assertContains(staticArray, staticSlice);
1158 	assertContains([1, 2, 3, 4, 5], [4, 5]);
1159 
1160 	// testing with void[] arrays
1161 	assertContains([0], []);
1162 	expectThrows!AssertException(assertContains([], [0]));
1163 
1164 	AssertException exception;
1165 
1166 	exception = expectThrows!AssertException(assertContains(array, [1, 4, 5, 5, 1, 7, 0, 1, 2]));
1167 	assertEquals(`slice length <9> should be less or equal than <5>`, exception.msg);
1168 	exception = expectThrows!AssertException(assertContains(array, [0]));
1169 	assertEquals(`expected array containing the slice <[0]>, but not found`, exception.msg);
1170 }
1171 
1172 pure
1173 @("Assertion: unsafe assertContains")
1174 unittest
1175 {
1176 	int a;
1177 	int b = 2;
1178 	int c = -5;
1179 	void*[] unsafeHaystack = [&a, &b, null];
1180 
1181 	assertContains(unsafeHaystack, [&b, null]);
1182 	// test with void*[] and typeof(null)[]
1183 	assertContains(unsafeHaystack, [null]);
1184 	expectThrows!AssertException(assertContains(unsafeHaystack, [&c]));
1185 }
1186 
1187 private string repr(T)(T value)
1188 {
1189 	// format string key with double quotes
1190 	return format("%(%s%)", value.only);
1191 }