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 }