1 // SemiTwist Library 2 // Written in the D programming language. 3 4 module semitwist.util.unittests; 5 6 // deferEnsure requires this to exist in the calling context 7 public import semitwist.util.reflect : _deferAssert_ExprTypeOf = ExprTypeOf; 8 9 import std.conv; 10 import std.demangle; 11 import std.stdio; 12 import std.traits; 13 14 import semitwist.util.all; 15 16 /** 17 Sounds like a contradiction of terms, but this is just 18 intended to allow unittests to output ALL failures instead 19 of only outputting the first one and then stopping. 20 */ 21 template deferAssert(string condStr, string msg="") 22 { 23 enum deferAssert = 24 // The "_deferAssert_line" is a workaround for DMD Bug #2887 25 "{ enum long _deferAssert_line = __LINE__;\n"~ 26 " try\n"~ 27 " {\n"~ 28 " bool _deferAssert_condResult = ("~condStr~")?true:false;\n"~ 29 " _deferAssert!(_deferAssert_line, __FILE__, "~condStr.stringof~", "~msg.stringof~")(_deferAssert_condResult);\n"~ 30 " }\n"~ 31 " catch(Throwable _deferAssert_e)\n"~ 32 " _deferAssertException!(_deferAssert_line, __FILE__, "~condStr.stringof~", "~msg.stringof~")(_deferAssert_e);\n"~ 33 "}\n"; 34 } 35 36 bool _deferAssert(long line, string file, string condStr, string msg="")(bool condResult) 37 { 38 if(!condResult) 39 { 40 assertCount++; 41 writefln("%s(%s): Assert Failed (%s)%s", 42 file, line, condStr, 43 msg=="" ? "" : ": " ~ msg); 44 writeln(); 45 } 46 47 return condResult; 48 } 49 50 void _deferAssertException(long line, string file, string condStr, string msg="")(Object thrown) 51 { 52 assertCount++; 53 writef("%s(%s): Assert Threw (%s)%s:\nThrew: ", 54 file, line, condStr, 55 msg=="" ? "" : ": " ~ msg); 56 Exception e = cast(Exception)thrown; 57 if(e) 58 writeln(thrown); 59 else 60 writefln("Object: type '%s': %s", thrown.classinfo.name, thrown); 61 writeln(); 62 } 63 64 //TODO: Something like: mixin(blah!(`_1 == (_2 ~ _3)`, `"Hello"`, `"He"`, `"llo"`)); 65 66 template deferEnsure(string value, string condStr, string msg="") 67 { 68 enum deferEnsure = 69 // The "_deferAssert_line" is a workaround for DMD Bug #2887 70 "{ enum long _deferAssert_line = __LINE__;\n"~ 71 " try\n"~ 72 " {\n"~ 73 " auto _ = ("~value~");\n"~ 74 " bool _deferAssert_condResult = ("~condStr~")?true:false;\n"~ 75 " _deferEnsure!(_deferAssert_line, __FILE__, "~value.stringof~", "~condStr.stringof~", _deferAssert_ExprTypeOf!(typeof("~value~")), "~msg.stringof~")(_, _deferAssert_condResult);\n"~ 76 " }\n"~ 77 " catch(Throwable _deferAssert_e)\n"~ 78 " _deferEnsureException!(_deferAssert_line, __FILE__, "~value.stringof~", "~condStr.stringof~", "~msg.stringof~")(_deferAssert_e);\n"~ 79 "}\n"; 80 } 81 82 bool _deferEnsure(long line, string file, string valueStr, string condStr, T, string msg="")(T valueResult, bool condResult) 83 { 84 if(!condResult) 85 { 86 assertCount++; 87 writefln("%s(%s): Ensure Failed%s\n"~ 88 "Expression '%s':\n"~ 89 "Expected: %s\n"~ 90 "Actual: %s", 91 file, line, msg=="" ? "" : ": " ~ msg, 92 valueStr, condStr, valueResult); 93 writeln(); 94 } 95 96 return condResult; 97 } 98 99 void _deferEnsureException(long line, string file, string valueStr, string condStr, string msg="")(Object thrown) 100 { 101 assertCount++; 102 writef("%s(%s): Ensure Threw%s:\n"~ 103 "Expression '%s':\n"~ 104 "Expected: %s\n"~ 105 "Threw: ", 106 file, line, msg=="" ? "" : ": " ~ msg, 107 valueStr, condStr); 108 Exception e = cast(Exception)thrown; 109 if(e) 110 writeln(thrown); 111 else 112 writefln("Object: type '%s': %s", thrown.classinfo.name, thrown); 113 writeln(); 114 } 115 116 template deferEnsureThrows(string stmtStr, TExpected, string msg="") 117 { 118 enum deferEnsureThrows = 119 // The "_deferAssert_line" is a workaround for DMD Bug #2887 120 "{ enum long _deferAssert_line = __LINE__;\n"~ 121 " Object _deferAssert_caught=null;\n"~ 122 " try\n"~ 123 " {"~stmtStr~"}\n"~ 124 " catch(Throwable _deferAssert_e)\n"~ 125 " _deferAssert_caught = _deferAssert_e;\n"~ 126 " _deferEnsureThrows!(_deferAssert_line, __FILE__, "~stmtStr.stringof~", "~TExpected.stringof~", "~msg.stringof~")(_deferAssert_caught);\n"~ 127 "}\n"; 128 } 129 130 void _deferEnsureThrows(long line, string file, string stmtStr, TExpected, string msg="")(Object thrown) 131 { 132 string actualType = (thrown is null)? "{null}" : thrown.classinfo.name; 133 134 if(actualType != TExpected.classinfo.name) 135 { 136 assertCount++; 137 writef("%s(%s): Ensure Throw Failed%s\n"~ 138 "Statement '%s':\n"~ 139 "Expected: %s\n"~ 140 "Actual: ", 141 file, line, msg=="" ? "" : ": " ~ msg, 142 stmtStr, TExpected.classinfo.name, actualType); 143 Throwable e = cast(Exception)thrown; 144 if(e) 145 writeln(e); //e.writeOut( (string msg) {Stdout(msg);} ); 146 else 147 writefln("%s: %s", actualType, thrown); 148 writeln(); 149 } 150 } 151 152 private uint assertCount=0; 153 uint getAssertCount() 154 { 155 return assertCount; 156 } 157 void resetAssertCount() 158 { 159 assertCount = 0; 160 } 161 162 void flushAsserts() 163 { 164 if(assertCount > 0) 165 { 166 uint saveAssertCount = assertCount; 167 assertCount = 0; 168 stdout.flush(); 169 assert(false, 170 to!(string)(saveAssertCount) ~ 171 " Assert Failure" ~ 172 (saveAssertCount == 1 ? "" : "s") 173 ); 174 } 175 } 176 177 /++ 178 To be mixed in. 179 180 Note that if DMD Issue #2887 ever gets fixed, the line numbers for errors 181 in unittestBody may get messed up. 182 183 Suggested Usage: 184 ------------------- 185 alias unittestSection!"MyProject_unittest" unittestMyProject; 186 187 mixin(unittestMyProject(q{ 188 // put unittests here 189 })); 190 191 mixin(unittestMyProject("This is for class Foo", q{ 192 // put unittests here 193 })); 194 ------------------- 195 196 That will create a named unittest section that will only run 197 when -unittest and -debug=MyProject_unittest are passed to DMD. 198 When run, the following headings will be displayed: 199 200 == unittest: the.module.name 201 == unittest: the.module.name: This is for class Foo 202 +/ 203 string unittestSection(string debugIdent, bool autoThrow=false)(string sectionName, string unittestBody=null) 204 { 205 // Allow these two forms (without getting in the way of aliasing): 206 // unittestSection!debugIdent(unittestBody) 207 // unittestSection!debugIdent(sectionName, unittestBody) 208 if(unittestBody==null) 209 { 210 unittestBody = sectionName; 211 sectionName = ""; 212 } 213 sectionName = escapeDDQS!string( ( sectionName==""? "" : ": "~sectionName ) ); 214 auto autoThrowStr = autoThrow? "true" : "false"; 215 216 return 217 "debug("~debugIdent~") "~ 218 "{ "~ 219 " unittest "~ 220 " { "~ 221 " auto saveAutoThrow = semitwist.util.unittests.autoThrow; "~ 222 " semitwist.util.unittests.autoThrow = "~autoThrowStr~"; "~ 223 " scope(exit) semitwist.util.unittests.autoThrow = saveAutoThrow; "~ 224 " "~ 225 " int _unittestSection_dummy_; "~ 226 " auto _unittestSection_moduleName_ = "~ 227 " unittestSection_demangle( qualifiedName!_unittestSection_dummy_() ) "~ 228 " [ "~ 229 " \"void \".length .. "~ 230 " ctfe_find(unittestSection_demangle( qualifiedName!_unittestSection_dummy_() ), \".__unittest\") "~ 231 " ]; "~ 232 " "~ 233 " writeUnittestSection( "~ 234 " _unittestSection_moduleName_ ~ "~ 235 " "~sectionName~" "~ 236 " ); "~ 237 " "~unittestBody~" "~ 238 " } "~ 239 "} "; 240 } 241 alias mangledName unittestSection_mangledName; 242 alias demangle unittestSection_demangle; 243 244 void writeUnittestSection(string sectionName) 245 { 246 writeln("== unittest: ", sectionName); 247 } 248 249 alias unittestSection!"SemiTwistDLib_unittest" unittestSemiTwistDLib; 250 251 /////////////////////////////////////////////////////////////////////////////// 252 253 /// A modification of Jonathan M Davis's unittest routines below: 254 255 //Ideally, this would be safe, I suppose, but it's enough of 256 //a pain at the moment to make stuff safe that I'm just going to 257 //mark it as trusted for the moment. 258 @trusted 259 260 261 import std.stdio; 262 263 import core.exception; 264 265 import std.algorithm; 266 import std.array; 267 import std.conv; 268 import std.exception; 269 import std.functional; 270 import std.range; 271 import std..string; 272 import std.traits; 273 274 /// This does not currently affect the defer* functions above. 275 bool autoThrow = true; 276 277 private void throwException(Throwable e) 278 { 279 if(autoThrow) 280 throw e; 281 else 282 { 283 assertCount++; 284 writeln(e); 285 writeln(); 286 } 287 } 288 289 version(unittest) 290 { 291 import std.datetime; 292 } 293 294 295 mixin(unittestSemiTwistDLib("assertPred: Overview Examples", q{ 296 autoThrow = true; 297 298 //Verify Examples. 299 assertPred!"=="(5 * 7, 35); 300 301 assertPred!("opCmp", ">")(std.datetime.Clock.currTime(), std.datetime.SysTime(Date(1970, 1, 1))); 302 303 assertPred!"opAssign"(std.datetime.SysTime(Date(1970, 1, 1)), 304 std.datetime.SysTime(Date(2010, 12, 31))); 305 306 assertPred!"+"(5, 7, 12); 307 308 assertPred!"+="(std.datetime.SysTime(Date(1970, 1, 1)), 309 core.time.dur!"days"(3), 310 std.datetime.SysTime(Date(1970, 1, 4))); 311 312 assertPred!"a == 7"(12 - 5); 313 314 assertPred!"a == b + 5"(12, 7); 315 316 assertPred!((int a, int b, int c){return a + b < c;})(4, 12, 50); 317 })); 318 319 320 void assertPred(string op, L, R) 321 (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) 322 if((op == "<" || 323 op == "<=" || 324 op == "==" || 325 op == "!=" || 326 op == ">=" || 327 op == ">") && 328 __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && 329 isPrintable!L && 330 isPrintable!R) 331 { 332 immutable result = mixin("lhs " ~ op ~ " rhs"); 333 334 if(!result) 335 { 336 immutable tail = msg.empty ? "." : ": " ~ msg; 337 338 throwException( new AssertError(format("assertPred!\"%s\" failed:\n[%s] (lhs)\n[%s] (rhs)%s", op, lhs, rhs, tail), 339 file, 340 line) 341 ); 342 } 343 } 344 345 mixin(unittestSemiTwistDLib("assertPred: Comparison Operators", q{ 346 autoThrow = true; 347 348 struct IntWrapper 349 { 350 int value; 351 352 this(int value) 353 { 354 this.value = value; 355 } 356 357 string toString() const 358 { 359 return to!string(value); 360 } 361 } 362 363 //Test ==. 364 assertNotThrown!AssertError(assertPred!"=="(6, 6)); 365 assertNotThrown!AssertError(assertPred!"=="(6, 6.0)); 366 assertNotThrown!AssertError(assertPred!"=="(IntWrapper(6), IntWrapper(6))); 367 368 assertThrown!AssertError(assertPred!"=="(6, 7)); 369 assertThrown!AssertError(assertPred!"=="(6, 6.1)); 370 assertThrown!AssertError(assertPred!"=="(IntWrapper(6), IntWrapper(7))); 371 assertThrown!AssertError(assertPred!"=="(IntWrapper(7), IntWrapper(6))); 372 373 assertPred!"=="(collectExceptionMsg(assertPred!"=="(6, 7)), 374 "assertPred!\"==\" failed:\n[6] (lhs)\n[7] (rhs)."); 375 assertPred!"=="(collectExceptionMsg(assertPred!"=="(6, 7, "It failed!")), 376 "assertPred!\"==\" failed:\n[6] (lhs)\n[7] (rhs): It failed!"); 377 378 //Test !=. 379 assertNotThrown!AssertError(assertPred!"!="(6, 7)); 380 assertNotThrown!AssertError(assertPred!"!="(6, 6.1)); 381 assertNotThrown!AssertError(assertPred!"!="(IntWrapper(6), IntWrapper(7))); 382 assertNotThrown!AssertError(assertPred!"!="(IntWrapper(7), IntWrapper(6))); 383 384 assertThrown!AssertError(assertPred!"!="(6, 6)); 385 assertThrown!AssertError(assertPred!"!="(6, 6.0)); 386 assertThrown!AssertError(assertPred!"!="(IntWrapper(6), IntWrapper(6))); 387 388 assertPred!"=="(collectExceptionMsg(assertPred!"!="(6, 6)), 389 "assertPred!\"!=\" failed:\n[6] (lhs)\n[6] (rhs)."); 390 assertPred!"=="(collectExceptionMsg(assertPred!"!="(6, 6, "It failed!")), 391 "assertPred!\"!=\" failed:\n[6] (lhs)\n[6] (rhs): It failed!"); 392 393 //Test <, <=, >=, >. 394 assertNotThrown!AssertError(assertPred!"<"(5, 7)); 395 assertNotThrown!AssertError(assertPred!"<="(5, 7)); 396 assertNotThrown!AssertError(assertPred!"<="(5, 5)); 397 assertNotThrown!AssertError(assertPred!">="(7, 7)); 398 assertNotThrown!AssertError(assertPred!">="(7, 5)); 399 assertNotThrown!AssertError(assertPred!">"(7, 5)); 400 401 assertThrown!AssertError(assertPred!"<"(7, 5)); 402 assertThrown!AssertError(assertPred!"<="(7, 5)); 403 assertThrown!AssertError(assertPred!">="(5, 7)); 404 assertThrown!AssertError(assertPred!">"(5, 7)); 405 406 assertPred!"=="(collectExceptionMsg(assertPred!"<"(7, 5)), 407 "assertPred!\"<\" failed:\n[7] (lhs)\n[5] (rhs)."); 408 assertPred!"=="(collectExceptionMsg(assertPred!"<"(7, 5, "It failed!")), 409 "assertPred!\"<\" failed:\n[7] (lhs)\n[5] (rhs): It failed!"); 410 411 //Test default arguments. 412 assertPred!"=="(12, 12); 413 assertPred!"=="(12, 12, "msg"); 414 assertPred!"=="(12, 12, "msg", "file"); 415 assertPred!"=="(12, 12, "msg", "file", 42); 416 417 //Verify Examples. 418 assertPred!"<"(5 / 2 + 4, 27); 419 420 assertPred!"<="(4, 5); 421 422 assertPred!"=="(1 * 2.1, 2.1); 423 424 assertPred!"!="("hello " ~ "world", "goodbye world"); 425 426 assertPred!">="(14.2, 14); 427 428 assertPred!">"(15, 2 + 1); 429 430 assert(collectExceptionMsg(assertPred!"=="("hello", "goodbye")) == 431 "assertPred!\"==\" failed:\n" ~ 432 "[hello] (lhs)\n" ~ 433 "[goodbye] (rhs)."); 434 435 assert(collectExceptionMsg(assertPred!"<"(5, 2, "My test failed!")) == 436 "assertPred!\"<\" failed:\n" ~ 437 "[5] (lhs)\n" ~ 438 "[2] (rhs): My test failed!"); 439 })); 440 441 442 void assertPred(string func, string expected, L, R) 443 (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) 444 if(func == "opCmp" && 445 (expected == "<" || 446 expected == "==" || 447 expected == ">") && 448 __traits(compiles, lhs.opCmp(rhs)) && 449 isPrintable!L && 450 isPrintable!R) 451 { 452 immutable result = lhs.opCmp(rhs); 453 454 if(mixin("result " ~ expected ~ " 0")) 455 return; 456 457 immutable tail = msg.empty ? "." : ": " ~ msg; 458 immutable actual = result < 0 ? "<" : (result == 0 ? "==" : ">"); 459 460 throwException( new AssertError(format("assertPred!(\"opCmp\", \"%s\") failed:\n[%s] %s\n[%s]%s", expected, lhs, actual, rhs, tail), 461 file, 462 line) 463 ); 464 } 465 466 mixin(unittestSemiTwistDLib("assertPred: opCmp", q{ 467 autoThrow = true; 468 469 struct IntWrapper 470 { 471 int value; 472 473 this(int value) 474 { 475 this.value = value; 476 } 477 478 int opCmp(const ref IntWrapper rhs) const 479 { 480 if(value < rhs.value) 481 return -1; 482 else if(value > rhs.value) 483 return 1; 484 485 return 0; 486 } 487 488 string toString() const 489 { 490 return to!string(value); 491 } 492 } 493 494 assertNotThrown!AssertError(assertPred!("opCmp", "<")(IntWrapper(0), IntWrapper(6))); 495 assertNotThrown!AssertError(assertPred!("opCmp", "<")(IntWrapper(6), IntWrapper(7))); 496 assertNotThrown!AssertError(assertPred!("opCmp", "==")(IntWrapper(6), IntWrapper(6))); 497 assertNotThrown!AssertError(assertPred!("opCmp", "==")(IntWrapper(0), IntWrapper(0))); 498 assertNotThrown!AssertError(assertPred!("opCmp", ">")(IntWrapper(6), IntWrapper(0))); 499 assertNotThrown!AssertError(assertPred!("opCmp", ">")(IntWrapper(7), IntWrapper(6))); 500 501 assertThrown!AssertError(assertPred!("opCmp", "<")(IntWrapper(6), IntWrapper(6))); 502 assertThrown!AssertError(assertPred!("opCmp", "<")(IntWrapper(7), IntWrapper(6))); 503 assertThrown!AssertError(assertPred!("opCmp", "==")(IntWrapper(6), IntWrapper(7))); 504 assertThrown!AssertError(assertPred!("opCmp", "==")(IntWrapper(7), IntWrapper(6))); 505 assertThrown!AssertError(assertPred!("opCmp", ">")(IntWrapper(6), IntWrapper(6))); 506 assertThrown!AssertError(assertPred!("opCmp", ">")(IntWrapper(6), IntWrapper(7))); 507 508 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "<")(IntWrapper(5), IntWrapper(5))), 509 "assertPred!(\"opCmp\", \"<\") failed:\n[5] ==\n[5]."); 510 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "<")(IntWrapper(5), IntWrapper(5), "It failed!")), 511 "assertPred!(\"opCmp\", \"<\") failed:\n[5] ==\n[5]: It failed!"); 512 513 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "<")(IntWrapper(14), IntWrapper(7))), 514 "assertPred!(\"opCmp\", \"<\") failed:\n[14] >\n[7]."); 515 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "<")(IntWrapper(14), IntWrapper(7), "It failed!")), 516 "assertPred!(\"opCmp\", \"<\") failed:\n[14] >\n[7]: It failed!"); 517 518 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "==")(IntWrapper(5), IntWrapper(7))), 519 "assertPred!(\"opCmp\", \"==\") failed:\n[5] <\n[7]."); 520 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "==")(IntWrapper(5), IntWrapper(7), "It failed!")), 521 "assertPred!(\"opCmp\", \"==\") failed:\n[5] <\n[7]: It failed!"); 522 523 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "==")(IntWrapper(14), IntWrapper(7))), 524 "assertPred!(\"opCmp\", \"==\") failed:\n[14] >\n[7]."); 525 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", "==")(IntWrapper(14), IntWrapper(7), "It failed!")), 526 "assertPred!(\"opCmp\", \"==\") failed:\n[14] >\n[7]: It failed!"); 527 528 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", ">")(IntWrapper(5), IntWrapper(7))), 529 "assertPred!(\"opCmp\", \">\") failed:\n[5] <\n[7]."); 530 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", ">")(IntWrapper(5), IntWrapper(7), "It failed!")), 531 "assertPred!(\"opCmp\", \">\") failed:\n[5] <\n[7]: It failed!"); 532 533 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", ">")(IntWrapper(7), IntWrapper(7))), 534 "assertPred!(\"opCmp\", \">\") failed:\n[7] ==\n[7]."); 535 assertPred!"=="(collectExceptionMsg(assertPred!("opCmp", ">")(IntWrapper(7), IntWrapper(7), "It failed!")), 536 "assertPred!(\"opCmp\", \">\") failed:\n[7] ==\n[7]: It failed!"); 537 538 //Test default arguments. 539 assertPred!("opCmp", "<")(Date(2010, 11, 30), Date(2010, 12, 31)); 540 assertPred!("opCmp", "<")(Date(2010, 11, 30), Date(2010, 12, 31), "msg"); 541 assertPred!("opCmp", "<")(Date(2010, 11, 30), Date(2010, 12, 31), "msg", "file"); 542 assertPred!("opCmp", "<")(Date(2010, 11, 30), Date(2010, 12, 31), "msg", "file", 42); 543 544 assertPred!("opCmp", "==")(Date(2010, 12, 31), Date(2010, 12, 31)); 545 assertPred!("opCmp", "==")(Date(2010, 12, 31), Date(2010, 12, 31), "msg"); 546 assertPred!("opCmp", "==")(Date(2010, 12, 31), Date(2010, 12, 31), "msg", "file"); 547 assertPred!("opCmp", "==")(Date(2010, 12, 31), Date(2010, 12, 31), "msg", "file", 42); 548 549 assertPred!("opCmp", ">")(Date(2010, 12, 31), Date(2010, 11, 30)); 550 assertPred!("opCmp", ">")(Date(2010, 12, 31), Date(2010, 11, 30), "msg"); 551 assertPred!("opCmp", ">")(Date(2010, 12, 31), Date(2010, 11, 30), "msg", "file"); 552 assertPred!("opCmp", ">")(Date(2010, 12, 31), Date(2010, 11, 30), "msg", "file", 42); 553 })); 554 555 mixin(unittestSemiTwistDLib("assertPred: opCmp: Examples", q{ 556 autoThrow = true; 557 558 //Verify Examples 559 assertPred!("opCmp", "<")(std.datetime.SysTime(Date(1970, 1, 1)), 560 std.datetime.SysTime(Date(2010, 12, 31))); 561 562 assertPred!("opCmp", "==")(std.datetime.SysTime(Date(1970, 1, 1)), 563 std.datetime.SysTime(Date(1970, 1, 1))); 564 565 assertPred!("opCmp", ">")(std.datetime.SysTime(Date(2010, 12, 31)), 566 std.datetime.SysTime(Date(1970, 1, 1))); 567 568 assert(collectExceptionMsg(assertPred!("opCmp", "<")(std.datetime.SysTime(Date(2010, 12, 31)), 569 std.datetime.SysTime(Date(1970, 1, 1)))) == 570 "assertPred!(\"opCmp\", \"<\") failed:\n" ~ 571 "[2010-Dec-31 00:00:00] >\n" ~ 572 "[1970-Jan-01 00:00:00]."); 573 574 assert(collectExceptionMsg(assertPred!("opCmp", "==")(std.datetime.SysTime(Date(1970, 1, 1)), 575 std.datetime.SysTime(Date(2010, 12, 31)))) == 576 "assertPred!(\"opCmp\", \"==\") failed:\n" ~ 577 "[1970-Jan-01 00:00:00] <\n" ~ 578 "[2010-Dec-31 00:00:00]."); 579 580 assert(collectExceptionMsg(assertPred!("opCmp", ">")(std.datetime.SysTime(Date(1970, 1, 1)), 581 std.datetime.SysTime(Date(1970, 1, 1)))) == 582 "assertPred!(\"opCmp\", \">\") failed:\n" ~ 583 "[1970-Jan-01 00:00:00] ==\n" ~ 584 "[1970-Jan-01 00:00:00]."); 585 })); 586 587 588 void assertPred(string func, L, R) 589 (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) 590 if(func == "opAssign" && 591 __traits(compiles, lhs = rhs) && 592 __traits(compiles, lhs == rhs) && 593 __traits(compiles, (lhs = rhs) == rhs) && 594 isPrintable!L && 595 isPrintable!R) 596 { 597 auto result = lhs = rhs; 598 599 if(lhs != rhs) 600 { 601 immutable tail = msg.empty ? "." : ": " ~ msg; 602 603 throwException( new AssertError(format("assertPred!\"opAssign\" failed: lhs was assigned to\n[%s] instead of\n[%s]%s", 604 lhs, 605 rhs, 606 tail), 607 file, 608 line) 609 ); 610 } 611 612 if(result != rhs) 613 { 614 immutable tail = msg.empty ? "." : ": " ~ msg; 615 616 throwException( new AssertError(format("assertPred!\"opAssign\" failed:\n[%s] (return value) !=\n[%s] (assigned value)%s", 617 result, 618 rhs, 619 tail), 620 file, 621 line) 622 ); 623 } 624 } 625 626 mixin(unittestSemiTwistDLib("assertPred: opAssign", q{ 627 autoThrow = true; 628 629 struct IntWrapper 630 { 631 int value; 632 633 this(int value) 634 { 635 this.value = value; 636 } 637 638 IntWrapper opAssign(IntWrapper rhs) 639 { 640 this.value = rhs.value; 641 642 return this; 643 } 644 645 string toString() const 646 { 647 return to!string(value); 648 } 649 } 650 651 struct IntWrapper_BadAssign 652 { 653 int value; 654 655 this(int value) 656 { 657 this.value = value; 658 } 659 660 IntWrapper_BadAssign opAssign(IntWrapper_BadAssign rhs) 661 { 662 this.value = -rhs.value; 663 664 return IntWrapper_BadAssign(rhs.value); 665 } 666 667 string toString() const 668 { 669 return to!string(value); 670 } 671 } 672 673 struct IntWrapper_BadReturn 674 { 675 int value; 676 677 this(int value) 678 { 679 this.value = value; 680 } 681 682 IntWrapper_BadReturn opAssign(IntWrapper_BadReturn rhs) 683 { 684 this.value = rhs.value; 685 686 return IntWrapper_BadReturn(-rhs.value); 687 } 688 689 string toString() const 690 { 691 return to!string(value); 692 } 693 } 694 695 assertNotThrown!AssertError(assertPred!"opAssign"(IntWrapper(5), IntWrapper(2))); 696 697 assertThrown!AssertError(assertPred!"opAssign"(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2))); 698 assertThrown!AssertError(assertPred!"opAssign"(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2))); 699 700 assertPred!"=="(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2))), 701 "assertPred!\"opAssign\" failed: lhs was assigned to\n[-2] instead of\n[2]."); 702 assertPred!"=="(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadAssign(5), 703 IntWrapper_BadAssign(2), 704 "It failed!")), 705 "assertPred!\"opAssign\" failed: lhs was assigned to\n[-2] instead of\n[2]: It failed!"); 706 707 assertPred!"=="(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2))), 708 "assertPred!\"opAssign\" failed:\n[-2] (return value) !=\n[2] (assigned value)."); 709 assertPred!"=="(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadReturn(5), 710 IntWrapper_BadReturn(2), 711 "It failed!")), 712 "assertPred!\"opAssign\" failed:\n[-2] (return value) !=\n[2] (assigned value): It failed!"); 713 714 //Test default arguments. 715 assertPred!"opAssign"(0, 12); 716 assertPred!"opAssign"(0, 12, "msg"); 717 assertPred!"opAssign"(0, 12, "msg", "file"); 718 assertPred!"opAssign"(0, 12, "msg", "file", 42); 719 })); 720 721 mixin(unittestSemiTwistDLib("assertPred: opAssign: Examples", q{ 722 autoThrow = true; 723 724 //Verify Examples 725 assertPred!"opAssign"(std.datetime.SysTime(Date(1970, 1, 1)), std.datetime.SysTime(Date(2000, 12, 12))); 726 727 struct IntWrapper_BadAssign 728 { 729 int value; 730 731 IntWrapper_BadAssign opAssign(IntWrapper_BadAssign rhs) 732 { 733 this.value = -rhs.value; 734 735 return IntWrapper_BadAssign(rhs.value); 736 } 737 738 string toString() const { return to!string(value); } 739 } 740 741 assert(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2))) == 742 "assertPred!\"opAssign\" failed: lhs was assigned to\n" ~ 743 "[-2] instead of\n" ~ 744 "[2]."); 745 746 assert(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadAssign(5), 747 IntWrapper_BadAssign(2), 748 "It failed!")) == 749 "assertPred!\"opAssign\" failed: lhs was assigned to\n" ~ 750 "[-2] instead of\n" ~ 751 "[2]: It failed!"); 752 753 754 struct IntWrapper_BadReturn 755 { 756 int value; 757 758 IntWrapper_BadReturn opAssign(IntWrapper_BadReturn rhs) 759 { 760 this.value = rhs.value; 761 762 return IntWrapper_BadReturn(-rhs.value); 763 } 764 765 string toString() const { return to!string(value); } 766 } 767 768 assert(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2))) == 769 "assertPred!\"opAssign\" failed:\n" ~ 770 "[-2] (return value) !=\n" ~ 771 "[2] (assigned value)."); 772 773 assert(collectExceptionMsg(assertPred!"opAssign"(IntWrapper_BadReturn(5), 774 IntWrapper_BadReturn(2), 775 "It failed!")) == 776 "assertPred!\"opAssign\" failed:\n" ~ 777 "[-2] (return value) !=\n" ~ 778 "[2] (assigned value): It failed!"); 779 })); 780 781 782 void assertPred(string op, L, R, E) 783 (L lhs, R rhs, E expected, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) 784 if((op == "+" || 785 op == "-" || 786 op == "*" || 787 op == "/" || 788 op == "%" || 789 op == "^^" || 790 op == "&" || 791 op == "|" || 792 op == "^" || 793 op == "<<" || 794 op == ">>" || 795 op == ">>>" || 796 op == "~") && 797 __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && 798 __traits(compiles, mixin("(lhs " ~ op ~ " rhs) == expected")) && 799 isPrintable!L && 800 isPrintable!R) 801 { 802 const result = mixin("lhs " ~ op ~ " rhs"); 803 804 if(result != expected) 805 { 806 immutable tail = msg.empty ? "." : ": " ~ msg; 807 808 throwException( new AssertError(format("assertPred!\"%s\" failed: [%s] %s [%s]:\n[%s] (actual)\n[%s] (expected)%s", 809 op, 810 lhs, 811 op, 812 rhs, 813 result, 814 expected, 815 tail), 816 file, 817 line) 818 ); 819 } 820 } 821 822 mixin(unittestSemiTwistDLib("assertPred: Operators", q{ 823 autoThrow = true; 824 825 assertNotThrown!AssertError(assertPred!"+"(7, 5, 12)); 826 assertNotThrown!AssertError(assertPred!"-"(7, 5, 2)); 827 assertNotThrown!AssertError(assertPred!"*"(7, 5, 35)); 828 assertNotThrown!AssertError(assertPred!"/"(7, 5, 1)); 829 assertNotThrown!AssertError(assertPred!"%"(7, 5, 2)); 830 assertNotThrown!AssertError(assertPred!"^^"(7, 5, 16_807)); 831 assertNotThrown!AssertError(assertPred!"&"(7, 5, 5)); 832 assertNotThrown!AssertError(assertPred!"|"(7, 5, 7)); 833 assertNotThrown!AssertError(assertPred!"^"(7, 5, 2)); 834 assertNotThrown!AssertError(assertPred!"<<"(7, 1, 14)); 835 assertNotThrown!AssertError(assertPred!">>"(7, 1, 3)); 836 assertNotThrown!AssertError(assertPred!">>>"(-7, 1, 2_147_483_644)); 837 assertNotThrown!AssertError(assertPred!"~"("hello ", "world", "hello world")); 838 839 assertThrown!AssertError(assertPred!"+"(7, 5, 0)); 840 assertThrown!AssertError(assertPred!"-"(7, 5, 0)); 841 assertThrown!AssertError(assertPred!"*"(7, 5, 0)); 842 assertThrown!AssertError(assertPred!"/"(7, 5, 0)); 843 assertThrown!AssertError(assertPred!"%"(7, 5, 0)); 844 assertThrown!AssertError(assertPred!"^^"(7, 5, 0)); 845 assertThrown!AssertError(assertPred!"&"(7, 5, 0)); 846 assertThrown!AssertError(assertPred!"|"(7, 5, 0)); 847 assertThrown!AssertError(assertPred!"^"(7, 5, 0)); 848 assertThrown!AssertError(assertPred!"<<"(7, 1, 0)); 849 assertThrown!AssertError(assertPred!">>"(7, 1, 0)); 850 assertThrown!AssertError(assertPred!">>>"(-7, 1, 0)); 851 assertThrown!AssertError(assertPred!"~"("hello ", "world", "goodbye world")); 852 853 assertPred!"=="(collectExceptionMsg(assertPred!"+"(7, 5, 11)), 854 "assertPred!\"+\" failed: [7] + [5]:\n[12] (actual)\n[11] (expected)."); 855 assertPred!"=="(collectExceptionMsg(assertPred!"+"(7, 5, 11, "It failed!")), 856 "assertPred!\"+\" failed: [7] + [5]:\n[12] (actual)\n[11] (expected): It failed!"); 857 858 assertPred!"=="(collectExceptionMsg(assertPred!"^^"(7, 5, 42)), 859 "assertPred!\"^^\" failed: [7] ^^ [5]:\n[16807] (actual)\n[42] (expected)."); 860 assertPred!"=="(collectExceptionMsg(assertPred!"^^"(7, 5, 42, "It failed!")), 861 "assertPred!\"^^\" failed: [7] ^^ [5]:\n[16807] (actual)\n[42] (expected): It failed!"); 862 863 assertPred!"=="(collectExceptionMsg(assertPred!"~"("hello ", "world", "goodbye world")), 864 "assertPred!\"~\" failed: [hello ] ~ [world]:\n[hello world] (actual)\n[goodbye world] (expected)."); 865 assertPred!"=="(collectExceptionMsg(assertPred!"~"("hello ", "world", "goodbye world", "It failed!")), 866 "assertPred!\"~\" failed: [hello ] ~ [world]:\n[hello world] (actual)\n[goodbye world] (expected): It failed!"); 867 868 //Verify Examples 869 assertPred!"+"(7, 5, 12); 870 assertPred!"-"(7, 5, 2); 871 assertPred!"*"(7, 5, 35); 872 assertPred!"/"(7, 5, 1); 873 assertPred!"%"(7, 5, 2); 874 assertPred!"^^"(7, 5, 16_807); 875 assertPred!"&"(7, 5, 5); 876 assertPred!"|"(7, 5, 7); 877 assertPred!"^"(7, 5, 2); 878 assertPred!"<<"(7, 1, 14); 879 assertPred!">>"(7, 1, 3); 880 assertPred!">>>"(-7, 1, 2_147_483_644); 881 assertPred!"~"("hello ", "world", "hello world"); 882 883 assert(collectExceptionMsg(assertPred!"+"(7, 5, 11)) == 884 "assertPred!\"+\" failed: [7] + [5]:\n" ~ 885 "[12] (actual)\n" ~ 886 "[11] (expected)."); 887 888 assert(collectExceptionMsg(assertPred!"/"(11, 2, 6, "It failed!")) == 889 "assertPred!\"/\" failed: [11] / [2]:\n" ~ 890 "[5] (actual)\n" ~ 891 "[6] (expected): It failed!"); 892 893 //Test default arguments. 894 assertPred!"+"(0, 12, 12); 895 assertPred!"+"(0, 12, 12, "msg"); 896 assertPred!"+"(0, 12, 12, "msg", "file"); 897 assertPred!"+"(0, 12, 12, "msg", "file", 42); 898 })); 899 900 901 void assertPred(string op, L, R, E) 902 (L lhs, R rhs, E expected, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) 903 if((op == "+=" || 904 op == "-=" || 905 op == "*=" || 906 op == "/=" || 907 op == "%=" || 908 op == "^^=" || 909 op == "&=" || 910 op == "|=" || 911 op == "^=" || 912 op == "<<=" || 913 op == ">>=" || 914 op == ">>>=" || 915 op == "~=") && 916 __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && 917 __traits(compiles, mixin("(lhs " ~ op ~ " rhs) == expected")) && 918 isPrintable!L && 919 isPrintable!R) 920 { 921 immutable origLHSStr = to!string(lhs); 922 const result = mixin("lhs " ~ op ~ " rhs"); 923 924 if(lhs != expected) 925 { 926 immutable tail = msg.empty ? "." : ": " ~ msg; 927 928 throwException( new AssertError(format("assertPred!\"%s\" failed: After [%s] %s [%s], lhs was assigned to\n[%s] instead of\n[%s]%s", 929 op, 930 origLHSStr, 931 op, 932 rhs, 933 lhs, 934 expected, 935 tail), 936 file, 937 line) 938 ); 939 } 940 941 if(result != expected) 942 { 943 immutable tail = msg.empty ? "." : ": " ~ msg; 944 945 throwException( new AssertError(format("assertPred!\"%s\" failed: Return value of [%s] %s [%s] was\n[%s] instead of\n[%s]%s", 946 op, 947 origLHSStr, 948 op, 949 rhs, 950 result, 951 expected, 952 tail), 953 file, 954 line) 955 ); 956 } 957 } 958 959 mixin(unittestSemiTwistDLib("assertPred: Assignment Operators", q{ 960 autoThrow = true; 961 962 assertNotThrown!AssertError(assertPred!"+="(7, 5, 12)); 963 assertNotThrown!AssertError(assertPred!"-="(7, 5, 2)); 964 assertNotThrown!AssertError(assertPred!"*="(7, 5, 35)); 965 assertNotThrown!AssertError(assertPred!"/="(7, 5, 1)); 966 assertNotThrown!AssertError(assertPred!"%="(7, 5, 2)); 967 assertNotThrown!AssertError(assertPred!"^^="(7, 5, 16_807)); 968 assertNotThrown!AssertError(assertPred!"&="(7, 5, 5)); 969 assertNotThrown!AssertError(assertPred!"|="(7, 5, 7)); 970 assertNotThrown!AssertError(assertPred!"^="(7, 5, 2)); 971 assertNotThrown!AssertError(assertPred!"<<="(7, 1, 14)); 972 assertNotThrown!AssertError(assertPred!">>="(7, 1, 3)); 973 assertNotThrown!AssertError(assertPred!">>>="(-7, 1, 2_147_483_644)); 974 assertNotThrown!AssertError(assertPred!"~="("hello ", "world", "hello world")); 975 976 assertThrown!AssertError(assertPred!"+="(7, 5, 0)); 977 assertThrown!AssertError(assertPred!"-="(7, 5, 0)); 978 assertThrown!AssertError(assertPred!"*="(7, 5, 0)); 979 assertThrown!AssertError(assertPred!"/="(7, 5, 0)); 980 assertThrown!AssertError(assertPred!"%="(7, 5, 0)); 981 assertThrown!AssertError(assertPred!"^^="(7, 5, 0)); 982 assertThrown!AssertError(assertPred!"&="(7, 5, 0)); 983 assertThrown!AssertError(assertPred!"|="(7, 5, 0)); 984 assertThrown!AssertError(assertPred!"^="(7, 5, 0)); 985 assertThrown!AssertError(assertPred!"<<="(7, 1, 0)); 986 assertThrown!AssertError(assertPred!">>="(7, 1, 0)); 987 assertThrown!AssertError(assertPred!">>>="(-7, 1, 0)); 988 assertThrown!AssertError(assertPred!"~="("hello ", "world", "goodbye world")); 989 990 assertPred!"=="(collectExceptionMsg(assertPred!"+="(7, 5, 11)), 991 "assertPred!\"+=\" failed: After [7] += [5], lhs was assigned to\n[12] instead of\n[11]."); 992 assertPred!"=="(collectExceptionMsg(assertPred!"+="(7, 5, 11, "It failed!")), 993 "assertPred!\"+=\" failed: After [7] += [5], lhs was assigned to\n[12] instead of\n[11]: It failed!"); 994 995 assertPred!"=="(collectExceptionMsg(assertPred!"^^="(7, 5, 42)), 996 "assertPred!\"^^=\" failed: After [7] ^^= [5], lhs was assigned to\n[16807] instead of\n[42]."); 997 assertPred!"=="(collectExceptionMsg(assertPred!"^^="(7, 5, 42, "It failed!")), 998 "assertPred!\"^^=\" failed: After [7] ^^= [5], lhs was assigned to\n[16807] instead of\n[42]: It failed!"); 999 1000 assertPred!"=="(collectExceptionMsg(assertPred!"~="("hello ", "world", "goodbye world")), 1001 "assertPred!\"~=\" failed: After [hello ] ~= [world], lhs was assigned to\n[hello world] instead of\n[goodbye world]."); 1002 assertPred!"=="(collectExceptionMsg(assertPred!"~="("hello ", "world", "goodbye world", "It failed!")), 1003 "assertPred!\"~=\" failed: After [hello ] ~= [world], lhs was assigned to\n[hello world] instead of\n[goodbye world]: It failed!"); 1004 1005 struct IntWrapper 1006 { 1007 int value; 1008 1009 this(int value) 1010 { 1011 this.value = value; 1012 } 1013 1014 IntWrapper opOpAssign(string op)(IntWrapper rhs) 1015 { 1016 mixin("this.value " ~ op ~ "= rhs.value;"); 1017 1018 return this; 1019 } 1020 1021 string toString() const 1022 { 1023 return to!string(value); 1024 } 1025 } 1026 1027 struct IntWrapper_BadAssign 1028 { 1029 int value; 1030 1031 this(int value) 1032 { 1033 this.value = value; 1034 } 1035 1036 IntWrapper_BadAssign opOpAssign(string op)(IntWrapper_BadAssign rhs) 1037 { 1038 auto old = this.value; 1039 1040 mixin("this.value " ~ op ~ "= -rhs.value;"); 1041 1042 return IntWrapper_BadAssign(mixin("old " ~ op ~ " rhs.value")); 1043 } 1044 1045 string toString() const 1046 { 1047 return to!string(value); 1048 } 1049 } 1050 1051 struct IntWrapper_BadReturn 1052 { 1053 int value; 1054 1055 this(int value) 1056 { 1057 this.value = value; 1058 } 1059 1060 IntWrapper_BadReturn opOpAssign(string op)(IntWrapper_BadReturn rhs) 1061 { 1062 mixin("this.value " ~ op ~ "= rhs.value;"); 1063 1064 return IntWrapper_BadReturn(rhs.value); 1065 } 1066 1067 string toString() const 1068 { 1069 return to!string(value); 1070 } 1071 } 1072 1073 assertNotThrown!AssertError(assertPred!"+="(IntWrapper(5), IntWrapper(2), IntWrapper(7))); 1074 assertNotThrown!AssertError(assertPred!"*="(IntWrapper(5), IntWrapper(2), IntWrapper(10))); 1075 1076 assertThrown!AssertError(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))); 1077 assertThrown!AssertError(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))); 1078 assertThrown!AssertError(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))); 1079 assertThrown!AssertError(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))); 1080 1081 assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))), 1082 "assertPred!\"+=\" failed: After [5] += [2], lhs was assigned to\n[3] instead of\n[7]."); 1083 assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7), "It failed!")), 1084 "assertPred!\"+=\" failed: After [5] += [2], lhs was assigned to\n[3] instead of\n[7]: It failed!"); 1085 1086 assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))), 1087 "assertPred!\"+=\" failed: Return value of [5] += [2] was\n[2] instead of\n[7]."); 1088 assertPred!"=="(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7), "It failed!")), 1089 "assertPred!\"+=\" failed: Return value of [5] += [2] was\n[2] instead of\n[7]: It failed!"); 1090 1091 assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))), 1092 "assertPred!\"*=\" failed: After [5] *= [2], lhs was assigned to\n[-10] instead of\n[10]."); 1093 assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10), "It failed!")), 1094 "assertPred!\"*=\" failed: After [5] *= [2], lhs was assigned to\n[-10] instead of\n[10]: It failed!"); 1095 1096 assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))), 1097 "assertPred!\"*=\" failed: Return value of [5] *= [2] was\n[2] instead of\n[10]."); 1098 assertPred!"=="(collectExceptionMsg(assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10), "It failed!")), 1099 "assertPred!\"*=\" failed: Return value of [5] *= [2] was\n[2] instead of\n[10]: It failed!"); 1100 1101 //Test default arguments. 1102 assertPred!"+="(0, 12, 12); 1103 assertPred!"+="(0, 12, 12, "msg"); 1104 assertPred!"+="(0, 12, 12, "msg", "file"); 1105 assertPred!"+="(0, 12, 12, "msg", "file", 42); 1106 })); 1107 1108 mixin(unittestSemiTwistDLib("assertPred: Assignment Operators: Examples", q{ 1109 autoThrow = true; 1110 1111 //Verify Examples 1112 assertPred!"+="(5, 7, 12); 1113 assertPred!"-="(7, 5, 2); 1114 assertPred!"*="(7, 5, 35); 1115 assertPred!"/="(7, 5, 1); 1116 assertPred!"%="(7, 5, 2); 1117 assertPred!"^^="(7, 5, 16_807); 1118 assertPred!"&="(7, 5, 5); 1119 assertPred!"|="(7, 5, 7); 1120 assertPred!"^="(7, 5, 2); 1121 assertPred!"<<="(7, 1, 14); 1122 assertPred!">>="(7, 1, 3); 1123 assertPred!">>>="(-7, 1, 2_147_483_644); 1124 assertPred!"~="("hello ", "world", "hello world"); 1125 1126 struct IntWrapper_BadAssign 1127 { 1128 int value; 1129 1130 IntWrapper_BadAssign opOpAssign(string op)(IntWrapper_BadAssign rhs) 1131 { 1132 auto old = this.value; 1133 1134 mixin("this.value " ~ op ~ "= -rhs.value;"); 1135 1136 return IntWrapper_BadAssign(mixin("old " ~ op ~ " rhs.value")); 1137 } 1138 1139 string toString() const { return to!string(value); } 1140 } 1141 1142 assert(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), 1143 IntWrapper_BadAssign(2), 1144 IntWrapper_BadAssign(7))) == 1145 "assertPred!\"+=\" failed: After [5] += [2], lhs was assigned to\n" ~ 1146 "[3] instead of\n" ~ 1147 "[7]."); 1148 1149 assert(collectExceptionMsg(assertPred!"+="(IntWrapper_BadAssign(5), 1150 IntWrapper_BadAssign(2), 1151 IntWrapper_BadAssign(7), 1152 "It failed!")) == 1153 "assertPred!\"+=\" failed: After [5] += [2], lhs was assigned to\n" ~ 1154 "[3] instead of\n" ~ 1155 "[7]: It failed!"); 1156 1157 struct IntWrapper_BadReturn 1158 { 1159 int value; 1160 1161 IntWrapper_BadReturn opOpAssign(string op)(IntWrapper_BadReturn rhs) 1162 { 1163 mixin("this.value " ~ op ~ "= rhs.value;"); 1164 1165 return IntWrapper_BadReturn(rhs.value); 1166 } 1167 1168 string toString() const { return to!string(value); } 1169 } 1170 1171 assert(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), 1172 IntWrapper_BadReturn(2), 1173 IntWrapper_BadReturn(7))) == 1174 "assertPred!\"+=\" failed: Return value of [5] += [2] was\n" ~ 1175 "[2] instead of\n" ~ 1176 "[7]."); 1177 1178 assert(collectExceptionMsg(assertPred!"+="(IntWrapper_BadReturn(5), 1179 IntWrapper_BadReturn(2), 1180 IntWrapper_BadReturn(7), 1181 "It failed!")) == 1182 "assertPred!\"+=\" failed: Return value of [5] += [2] was\n" ~ 1183 "[2] instead of\n" ~ 1184 "[7]: It failed!"); 1185 })); 1186 1187 1188 void assertPred(string pred, string msg = null, string file = __FILE__, size_t line = __LINE__, T) 1189 (T a) 1190 if(__traits(compiles, unaryFun!pred(a)) && 1191 is(typeof(unaryFun!pred(a)) : bool) && 1192 isPrintable!T) 1193 { 1194 if(!unaryFun!pred(a)) 1195 { 1196 immutable tail = msg.empty ? "." : ": " ~ msg; 1197 1198 throwException( new AssertError(format(`assertPred!"%s" failed: [%s] (a)%s`, pred, a, tail), 1199 file, 1200 line) 1201 ); 1202 } 1203 } 1204 1205 mixin(unittestSemiTwistDLib("assertPred: unaryFun", q{ 1206 autoThrow = true; 1207 1208 assertNotThrown!AssertError(assertPred!"a == 1"(1)); 1209 assertNotThrown!AssertError(assertPred!"a"(true)); 1210 assertNotThrown!AssertError(assertPred!"!a"(false)); 1211 1212 assertThrown!AssertError(assertPred!"a == 1"(2)); 1213 assertThrown!AssertError(assertPred!"a"(false)); 1214 assertThrown!AssertError(assertPred!"!a"(true)); 1215 1216 assertPred!"=="(collectExceptionMsg(assertPred!"a == 1"(2)), 1217 `assertPred!"a == 1" failed: [2] (a).`); 1218 assertPred!"=="(collectExceptionMsg(assertPred!("a == 1", "It failed!")(2)), 1219 `assertPred!"a == 1" failed: [2] (a): It failed!`); 1220 1221 //Test default arguments. 1222 assertPred!"a == 7"(7); 1223 assertPred!("a == 7", "msg")(7); 1224 assertPred!("a == 7", "msg", "file")(7); 1225 assertPred!("a == 7", "msg", "file", 42)(7); 1226 1227 //Verify Examples. 1228 assertPred!"a == 1"(1); 1229 1230 assertPred!"a * 2.0 == 4.0"(2); 1231 1232 assert(collectExceptionMsg(assertPred!"a == 1"(2)), 1233 `assertPred!"a == 1" failed: [2] (a).`); 1234 1235 assert(collectExceptionMsg(assertPred!("a * 2.0 == 4.0", "Woe is me!")(7)), 1236 `assertPred!"a * 2.0 == 4.0" failed: [7] (a): Woe is me!`); 1237 })); 1238 1239 1240 void assertPred(string pred, string msg = null, string file = __FILE__, size_t line = __LINE__, T, U) 1241 (T a, U b) 1242 if(__traits(compiles, binaryFun!pred(a, b)) && 1243 is(typeof(binaryFun!pred(a, b)) : bool) && 1244 isPrintable!T) 1245 { 1246 if(!binaryFun!pred(a, b)) 1247 { 1248 immutable tail = msg.empty ? "." : ": " ~ msg; 1249 1250 throwException( new AssertError(format(`assertPred!"%s" failed: [%s] (a), [%s] (b)%s`, pred, a, b, tail), 1251 file, 1252 line) 1253 ); 1254 } 1255 } 1256 1257 mixin(unittestSemiTwistDLib("assertPred: binaryFun", q{ 1258 autoThrow = true; 1259 1260 assertNotThrown!AssertError(assertPred!"a == b"(1, 1)); 1261 assertNotThrown!AssertError(assertPred!"a * b == 2.0"(1, 2.0)); 1262 1263 assertThrown!AssertError(assertPred!"a == b"(1, 2)); 1264 assertThrown!AssertError(assertPred!"a * b == 2.0"(2, 2.0)); 1265 1266 assertPred!"=="(collectExceptionMsg(assertPred!"a == b"(1, 2)), 1267 `assertPred!"a == b" failed: [1] (a), [2] (b).`); 1268 assertPred!"=="(collectExceptionMsg(assertPred!("a == b", "It failed!")(1, 2)), 1269 `assertPred!"a == b" failed: [1] (a), [2] (b): It failed!`); 1270 1271 //Test default arguments. 1272 assertPred!"a == b"(7, 7); 1273 assertPred!("a == b", "msg")(7, 7); 1274 assertPred!("a == b", "msg", "file")(7, 7); 1275 assertPred!("a == b", "msg", "file", 42)(7, 7); 1276 1277 //Verify Examples. 1278 assertPred!"a == b"(42, 42); 1279 1280 assertPred!`a ~ b == "hello world"`("hello ", "world"); 1281 1282 assertPred!"=="(collectExceptionMsg(assertPred!"a == b"(1, 2)), 1283 `assertPred!"a == b" failed: [1] (a), [2] (b).`); 1284 1285 assertPred!"=="(collectExceptionMsg(assertPred!("a * b == 7", "It failed!")(2, 3)), 1286 `assertPred!"a * b == 7" failed: [2] (a), [3] (b): It failed!`); 1287 })); 1288 1289 1290 void assertPred(alias pred, string msg = null, string file = __FILE__, size_t line = __LINE__, T...) 1291 (T args) 1292 if(isCallable!pred && 1293 is(ReturnType!pred == bool) && 1294 __traits(compiles, pred(args)) && 1295 isPrintable!T) 1296 { 1297 immutable result = pred(args); 1298 1299 if(!result) 1300 { 1301 string argsStr; 1302 1303 if(args.length > 0) 1304 { 1305 foreach(value; args) 1306 argsStr ~= format("[%s], ", to!string(value)); 1307 1308 argsStr.popBackN(", ".length); 1309 } 1310 else 1311 argsStr = "none"; 1312 1313 immutable tail = msg.empty ? "." : ": " ~ msg; 1314 1315 throwException( new AssertError(format("assertPred failed: arguments: %s%s", argsStr, tail), file, line) ); 1316 } 1317 } 1318 1319 mixin(unittestSemiTwistDLib("assertPred: Delegates", q{ 1320 autoThrow = true; 1321 1322 assertNotThrown!AssertError(assertPred!({return true;})()); 1323 assertNotThrown!AssertError(assertPred!((bool a){return a;})(true)); 1324 assertNotThrown!AssertError(assertPred!((int a, int b){return a == b;})(5, 5)); 1325 assertNotThrown!AssertError(assertPred!((int a, int b, int c){return a == b && b == c;})(5, 5, 5)); 1326 assertNotThrown!AssertError(assertPred!((int a, int b, int c, float d){return a * b < c * d;})(2, 4, 5, 1.7)); 1327 1328 assertThrown!AssertError(assertPred!({return false;})()); 1329 assertThrown!AssertError(assertPred!((bool a){return a;})(false)); 1330 assertThrown!AssertError(assertPred!((int a, int b){return a == b;})(5, 6)); 1331 assertThrown!AssertError(assertPred!((int a, int b, int c){return a == b && b == c;})(5, 5, 6)); 1332 assertThrown!AssertError(assertPred!((int a, int b, int c, float d){return a * b < c * d;})(3, 4, 5, 1.7)); 1333 1334 //Test default arguments. 1335 assertPred!((int a, int b){return a == b;})(7, 7); 1336 assertPred!((int a, int b){return a == b;}, "msg")(7, 7); 1337 assertPred!((int a, int b){return a == b;}, "msg", "file")(7, 7); 1338 assertPred!((int a, int b){return a == b;}, "msg", "file", 42)(7, 7); 1339 1340 //Verify Examples. 1341 assertPred!((int[] range, int i){return canFind(range, i);})([1, 5, 7, 2], 7); 1342 1343 assertPred!((int a, int b, int c){return a == b && b == c;})(5, 5, 5); 1344 1345 assert(collectExceptionMsg(assertPred!((int a, int b, int c, float d){return a * b < c * d;}) 1346 (22, 4, 5, 1.7)) == 1347 "assertPred failed: arguments: [22], [4], [5], [1.7]."); 1348 1349 // Crashes DMD 2.054 (DMD Issue #6351): 1350 //assert(collectExceptionMsg(assertPred!((string[] s...){return canFind(s, "hello");}, "Failure!") 1351 // ("goodbye", "old", "friend")) == 1352 // "assertPred failed: arguments: [goodbye], [old], [friend]: Failure!"); 1353 1354 })); 1355 1356 //============================================================================== 1357 // Private Section. 1358 // 1359 // Note: assertNotThrown, assertThrown and collectExceptionMsg are included in 1360 // this module because they're used by assertPred's unittests and haven't been 1361 // added to Phobos just yet. But they're private becuase they're going to be 1362 // in Phobos soon. 1363 //============================================================================== 1364 private: 1365 1366 void assertNotThrown(T : Throwable = Exception, F) 1367 (lazy F funcToCall, string msg = null, string file = __FILE__, size_t line = __LINE__) 1368 { 1369 try 1370 funcToCall(); 1371 catch(T t) 1372 { 1373 immutable tail = msg.empty ? "." : ": " ~ msg; 1374 1375 throwException( new AssertError(format("assertNotThrown failed: %s was thrown%s", T.stringof, tail), file, line, t) ); 1376 } 1377 } 1378 1379 mixin(unittestSemiTwistDLib("private assertNotThrown", q{ 1380 autoThrow = true; 1381 1382 void throwEx(Throwable t) 1383 { 1384 throw t; 1385 } 1386 1387 void nothrowEx() 1388 { 1389 } 1390 1391 try 1392 assertNotThrown!Exception(nothrowEx()); 1393 catch(AssertError) 1394 assert(0); 1395 1396 try 1397 assertNotThrown!Exception(nothrowEx(), "It's a message"); 1398 catch(AssertError) 1399 assert(0); 1400 1401 try 1402 assertNotThrown!AssertError(nothrowEx()); 1403 catch(AssertError) 1404 assert(0); 1405 1406 try 1407 assertNotThrown!AssertError(nothrowEx(), "It's a message"); 1408 catch(AssertError) 1409 assert(0); 1410 1411 1412 { 1413 bool thrown = false; 1414 try 1415 assertNotThrown!Exception(throwEx(new Exception("It's an Exception"))); 1416 catch(AssertError) 1417 thrown = true; 1418 1419 assert(thrown); 1420 } 1421 1422 { 1423 bool thrown = false; 1424 try 1425 assertNotThrown!Exception(throwEx(new Exception("It's an Exception")), "It's a message"); 1426 catch(AssertError) 1427 thrown = true; 1428 1429 assert(thrown); 1430 } 1431 1432 { 1433 bool thrown = false; 1434 try 1435 assertNotThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__))); 1436 catch(AssertError) 1437 thrown = true; 1438 1439 assert(thrown); 1440 } 1441 1442 { 1443 bool thrown = false; 1444 try 1445 assertNotThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)), "It's a message"); 1446 catch(AssertError) 1447 thrown = true; 1448 1449 assert(thrown); 1450 } 1451 1452 //Verify Examples. 1453 assertNotThrown!DateTimeException(std.datetime.TimeOfDay(0, 0, 0)); 1454 assertNotThrown!DateTimeException(std.datetime.TimeOfDay(12, 30, 27)); 1455 assertNotThrown(std.datetime.TimeOfDay(23, 59, 59)); //Exception is default. 1456 1457 assert(collectExceptionMsg(assertNotThrown!TimeException(std.datetime.TimeOfDay(12, 0, 60))) == 1458 "assertNotThrown failed: TimeException was thrown."); 1459 1460 assert(collectExceptionMsg(assertNotThrown!TimeException(std.datetime.TimeOfDay(25, 0, 0), "error!")) == 1461 "assertNotThrown failed: TimeException was thrown: error!"); 1462 })); 1463 1464 1465 void assertThrown(T : Throwable = Exception, F) 1466 (lazy F funcToCall, string msg = null, string file = __FILE__, size_t line = __LINE__) 1467 { 1468 bool thrown = false; 1469 1470 try 1471 funcToCall(); 1472 catch(T t) 1473 thrown = true; 1474 1475 if(!thrown) 1476 { 1477 immutable tail = msg.empty ? "." : ": " ~ msg; 1478 1479 throwException( new AssertError(format("assertThrown failed: No %s was thrown%s", T.stringof, tail), file, line) ); 1480 } 1481 } 1482 1483 mixin(unittestSemiTwistDLib("private assertThrown", q{ 1484 autoThrow = true; 1485 1486 void throwEx(Throwable t) 1487 { 1488 throw t; 1489 } 1490 1491 void nothrowEx() 1492 { 1493 } 1494 1495 try 1496 assertThrown!Exception(throwEx(new Exception("It's an Exception"))); 1497 catch(AssertError) 1498 assert(0); 1499 1500 try 1501 assertThrown!Exception(throwEx(new Exception("It's an Exception")), "It's a message"); 1502 catch(AssertError) 1503 assert(0); 1504 1505 try 1506 assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__))); 1507 catch(AssertError) 1508 assert(0); 1509 1510 try 1511 assertThrown!AssertError(throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)), "It's a message"); 1512 catch(AssertError) 1513 assert(0); 1514 1515 1516 { 1517 bool thrown = false; 1518 try 1519 assertThrown!Exception(nothrowEx()); 1520 catch(AssertError) 1521 thrown = true; 1522 1523 assert(thrown); 1524 } 1525 1526 { 1527 bool thrown = false; 1528 try 1529 assertThrown!Exception(nothrowEx(), "It's a message"); 1530 catch(AssertError) 1531 thrown = true; 1532 1533 assert(thrown); 1534 } 1535 1536 { 1537 bool thrown = false; 1538 try 1539 assertThrown!AssertError(nothrowEx()); 1540 catch(AssertError) 1541 thrown = true; 1542 1543 assert(thrown); 1544 } 1545 1546 { 1547 bool thrown = false; 1548 try 1549 assertThrown!AssertError(nothrowEx(), "It's a message"); 1550 catch(AssertError) 1551 thrown = true; 1552 1553 assert(thrown); 1554 } 1555 1556 //Verify Examples. 1557 assertThrown!DateTimeException(std.datetime.TimeOfDay(-1, 15, 30)); 1558 assertThrown!DateTimeException(std.datetime.TimeOfDay(12, 60, 30)); 1559 assertThrown(std.datetime.TimeOfDay(12, 15, 60)); //Exception is default. 1560 1561 1562 assert(collectExceptionMsg(assertThrown!AssertError(std.datetime.TimeOfDay(12, 0, 0))) == 1563 "assertThrown failed: No AssertError was thrown."); 1564 1565 assert(collectExceptionMsg(assertThrown!AssertError(std.datetime.TimeOfDay(12, 0, 0), "error!")) == 1566 "assertThrown failed: No AssertError was thrown: error!"); 1567 })); 1568 1569 1570 string collectExceptionMsg(T)(lazy T funcCall) 1571 { 1572 try 1573 { 1574 funcCall(); 1575 1576 return cast(string)null; 1577 } 1578 catch(Throwable t) 1579 return t.msg; 1580 } 1581 1582 mixin(unittestSemiTwistDLib("private collectExceptionMsg", q{ 1583 autoThrow = true; 1584 1585 //Verify Example. 1586 void throwFunc() {throw new Exception("My Message.");} 1587 assert(collectExceptionMsg(throwFunc()) == "My Message."); 1588 1589 void nothrowFunc() {} 1590 assert(collectExceptionMsg(nothrowFunc()) is null); 1591 })); 1592 1593 1594 /+ 1595 Whether the given type can be converted to a string. 1596 +/ 1597 template isPrintable(T...) 1598 { 1599 static if(T.length == 0) 1600 enum isPrintable = true; 1601 else static if(T.length == 1) 1602 { 1603 enum isPrintable = (!isArray!(T[0]) && __traits(compiles, to!string(T[0].init))) || 1604 (isArray!(T[0]) && __traits(compiles, to!string(T[0].init[0]))); 1605 } 1606 else 1607 { 1608 enum isPrintable = isPrintable!(T[0]) && isPrintable!(T[1 .. $]); 1609 } 1610 }