1 import core.stdc.stdio : fprintf, stderr; 2 import core.internal.dassert : _d_assert_fail; 3 4 void test(string comp = "==", A, B)(A a, B b, string msg, size_t line = __LINE__) 5 { 6 test(_d_assert_fail!(A)(comp, a, b), msg, line); 7 } 8 9 void test(const string actual, const string expected, size_t line = __LINE__) 10 { 11 import core.exception : AssertError; 12 13 if (actual != expected) 14 { 15 const msg = "Mismatch!\nExpected: <" ~ expected ~ ">\nActual: <" ~ actual ~ '>'; 16 throw new AssertError(msg, __FILE__, line); 17 } 18 } 19 20 void testIntegers() 21 { 22 test(1, 2, "1 != 2"); 23 test(-10, 8, "-10 != 8"); 24 test(byte.min, byte.max, "-128 != 127"); 25 test(ubyte.min, ubyte.max, "0 != 255"); 26 test(short.min, short.max, "-32768 != 32767"); 27 test(ushort.min, ushort.max, "0 != 65535"); 28 test(int.min, int.max, "-2147483648 != 2147483647"); 29 test(uint.min, uint.max, "0 != 4294967295"); 30 test(long.min, long.max, "-9223372036854775808 != 9223372036854775807"); 31 test(ulong.min, ulong.max, "0 != 18446744073709551615"); 32 test(shared(ulong).min, shared(ulong).max, "0 != 18446744073709551615"); 33 34 int testFun() { return 1; } 35 test(testFun(), 2, "1 != 2"); 36 } 37 38 void testIntegerComparisons() 39 { 40 test!"!="(2, 2, "2 == 2"); 41 test!"<"(2, 1, "2 >= 1"); 42 test!"<="(2, 1, "2 > 1"); 43 test!">"(1, 2, "1 <= 2"); 44 test!">="(1, 2, "1 < 2"); 45 } 46 47 void testFloatingPoint() 48 { 49 if (__ctfe) 50 { 51 test(float.max, -float.max, "<float not supported> != <float not supported>"); 52 test(double.max, -double.max, "<double not supported> != <double not supported>"); 53 test(real(1), real(-1), "<real not supported> != <real not supported>"); 54 } 55 else 56 { 57 test(1.5, 2.5, "1.5 != 2.5"); 58 test(float.max, -float.max, "3.40282e+38 != -3.40282e+38"); 59 test(double.max, -double.max, "1.79769e+308 != -1.79769e+308"); 60 test(real(1), real(-1), "1 != -1"); 61 } 62 } 63 64 void testPointers() 65 { 66 static struct S 67 { 68 string toString() const { return "S(...)"; } 69 } 70 71 static if ((void*).sizeof == 4) 72 enum ptr = "0x12345670"; 73 else 74 enum ptr = "0x123456789abcdef0"; 75 76 int* p = cast(int*) mixin(ptr); 77 test(cast(S*) p, p, ptr ~ " != " ~ ptr); 78 } 79 80 void testStrings() 81 { 82 test("foo", "bar", `"foo" != "bar"`); 83 test("", "bar", `"" != "bar"`); 84 85 char[] dlang = "dlang".dup; 86 const(char)[] rust = "rust"; 87 test(dlang, rust, `"dlang" != "rust"`); 88 89 // https://issues.dlang.org/show_bug.cgi?id=20322 90 test("left"w, "right"w, `"left" != "right"`); 91 test("left"d, "right"d, `"left" != "right"`); 92 93 test('A', 'B', "'A' != 'B'"); 94 test(wchar('❤'), wchar('∑'), "'❤' != '∑'"); 95 test(dchar('❤'), dchar('∑'), "'❤' != '∑'"); 96 97 // Detect invalid code points 98 test(char(255), 'B', "cast(char) 255 != 'B'"); 99 test(wchar(0xD888), wchar('∑'), "cast(wchar) 55432 != '∑'"); 100 test(dchar(0xDDDD), dchar('∑'), "cast(dchar) 56797 != '∑'"); 101 } 102 103 void testToString() 104 { 105 class Foo 106 { 107 this(string payload) { 108 this.payload = payload; 109 } 110 111 string payload; 112 override string toString() { 113 return "Foo(" ~ payload ~ ")"; 114 } 115 } 116 test(new Foo("a"), new Foo("b"), "Foo(a) != Foo(b)"); 117 118 scope f = cast(shared) new Foo("a"); 119 if (!__ctfe) // Ref somehow get's lost in CTFE 120 test!"!="(f, f, "Foo(a) == Foo(a)"); 121 122 // Verifiy that the const toString is selected if present 123 static struct Overloaded 124 { 125 string toString() 126 { 127 return "Mutable"; 128 } 129 130 string toString() const 131 { 132 return "Const"; 133 } 134 } 135 136 test!"!="(Overloaded(), Overloaded(), "Const == Const"); 137 138 Foo fnull = null; 139 test!"!is"(fnull, fnull, "`null` is `null`"); 140 } 141 142 143 void testArray() 144 { 145 test([1], [0], "[1] != [0]"); 146 test([1, 2, 3], [0], "[1, 2, 3] != [0]"); 147 148 // test with long arrays 149 int[] arr; 150 foreach (i; 0 .. 100) 151 arr ~= i; 152 test(arr, [0], "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, ...] != [0]"); 153 154 // Ignore fake arrays 155 static struct S 156 { 157 int[2] arr; 158 int[] get() return { return arr[]; } 159 alias get this; 160 } 161 162 const a = S([1, 2]); 163 test(a, S([3, 4]), "S([1, 2]) != S([3, 4])"); 164 } 165 166 void testStruct() 167 { 168 struct S { int s; } 169 struct T { T[] t; } 170 test(S(0), S(1), "S(0) != S(1)"); 171 test(T([T(null)]), T(null), "T([T([])]) != T([])"); 172 173 // https://issues.dlang.org/show_bug.cgi?id=20323 174 static struct NoCopy 175 { 176 @disable this(this); 177 } 178 179 NoCopy n; 180 test(_d_assert_fail!(typeof(n))("!=", n, n), "NoCopy() == NoCopy()"); 181 182 shared NoCopy sn; 183 test(_d_assert_fail!(typeof(sn))("!=", sn, sn), "NoCopy() == NoCopy()"); 184 } 185 186 void testAA() 187 { 188 test([1:"one"], [2: "two"], `[1: "one"] != [2: "two"]`); 189 test!"in"(1, [2: 3], "1 !in [2: 3]"); 190 test!"in"("foo", ["bar": true], `"foo" !in ["bar": true]`); 191 } 192 193 void testAttributes() @safe pure @nogc nothrow 194 { 195 int a; 196 string s = _d_assert_fail!(int, char)("==", a, 'c', 1, 'd'); 197 assert(s == `(0, 'c') != (1, 'd')`); 198 199 string s2 = _d_assert_fail!int("", a); 200 assert(s2 == `0 != true`); 201 } 202 203 // https://issues.dlang.org/show_bug.cgi?id=20066 204 void testVoidArray() 205 { 206 test!"!is"([], null, (__ctfe ? "<void[] not supported>" : "[]") ~ " is `null`"); 207 test!"!is"(null, null, "`null` is `null`"); 208 test([1], null, "[1] != `null`"); 209 test("s", null, "\"s\" != `null`"); 210 test(['c'], null, "\"c\" != `null`"); 211 test!"!="(null, null, "`null` == `null`"); 212 213 const void[] chunk = [byte(1), byte(2), byte(3)]; 214 test(chunk, null, (__ctfe ? "<void[] not supported>" : "[1, 2, 3]") ~ " != `null`"); 215 } 216 217 void testTemporary() 218 { 219 static struct Bad 220 { 221 ~this() @system {} 222 } 223 224 test!"!="(Bad(), Bad(), "Bad() == Bad()"); 225 } 226 227 void testEnum() 228 { 229 static struct UUID { 230 union 231 { 232 ubyte[] data = [1]; 233 } 234 } 235 236 ubyte[] data; 237 enum ctfe = UUID(); 238 test(_d_assert_fail!(ubyte[])("==", ctfe.data, data), "[1] != []"); 239 } 240 241 void testUnary() 242 { 243 test(_d_assert_fail!int("", 9), "9 != true"); 244 test(_d_assert_fail!(int[])("!", [1, 2, 3]), "[1, 2, 3] == true"); 245 } 246 247 void testTuple() 248 { 249 test(_d_assert_fail("=="), "() != ()"); 250 test(_d_assert_fail("!="), "() == ()"); 251 test(_d_assert_fail(">="), "() < ()"); 252 } 253 254 void testStructEquals() 255 { 256 struct T { 257 bool b; 258 int i; 259 float f1 = 2.5; 260 float f2 = 0; 261 string s1 = "bar"; 262 string s2; 263 } 264 265 T t1; 266 test!"!="(t1, t1, `T(false, 0, 2.5, 0, "bar", "") == T(false, 0, 2.5, 0, "bar", "")`); 267 T t2 = {s1: "bari"}; 268 test(t1, t2, `T(false, 0, 2.5, 0, "bar", "") != T(false, 0, 2.5, 0, "bari", "")`); 269 } 270 271 void testStructEquals2() 272 { 273 struct T { 274 bool b; 275 int i; 276 float f1 = 2.5; 277 float f2 = 0; 278 } 279 280 T t1; 281 test!"!="(t1, t1, `T(false, 0, 2.5, 0) == T(false, 0, 2.5, 0)`); 282 T t2 = {i: 2}; 283 test(t1, t2, `T(false, 0, 2.5, 0) != T(false, 2, 2.5, 0)`); 284 } 285 286 void testStructEquals3() 287 { 288 struct T { 289 bool b; 290 int i; 291 string s1 = "bar"; 292 string s2; 293 } 294 295 T t1; 296 test!"!="(t1, t1, `T(false, 0, "bar", "") == T(false, 0, "bar", "")`); 297 T t2 = {s1: "bari"}; 298 test(t1, t2, `T(false, 0, "bar", "") != T(false, 0, "bari", "")`); 299 } 300 301 void testStructEquals4() 302 { 303 struct T { 304 float f1 = 2.5; 305 float f2 = 0; 306 string s1 = "bar"; 307 string s2; 308 } 309 310 T t1; 311 test!"!="(t1, t1, `T(2.5, 0, "bar", "") == T(2.5, 0, "bar", "")`); 312 T t2 = {s1: "bari"}; 313 test(t1, t2, `T(2.5, 0, "bar", "") != T(2.5, 0, "bari", "")`); 314 } 315 316 void testStructEquals5() 317 { 318 struct T { 319 bool b; 320 int i; 321 float f2 = 0; 322 string s2; 323 } 324 325 T t1; 326 test!"!="(t1, t1, `T(false, 0, 0, "") == T(false, 0, 0, "")`); 327 T t2 = {b: true}; 328 test(t1, t2, `T(false, 0, 0, "") != T(true, 0, 0, "")`); 329 } 330 331 void testStructEquals6() 332 { 333 class C { override string toString() { return "C()"; }} 334 struct T { 335 bool b; 336 int i; 337 float f2 = 0; 338 string s2; 339 int[] arr; 340 C c; 341 } 342 343 T t1; 344 test!"!="(t1, t1, "T(false, 0, 0, \"\", [], `null`) == T(false, 0, 0, \"\", [], `null`)"); 345 T t2 = {arr: [1]}; 346 test(t1, t2, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [1], `null`)"); 347 T t3 = {c: new C()}; 348 test(t1, t3, "T(false, 0, 0, \"\", [], `null`) != T(false, 0, 0, \"\", [], C())"); 349 } 350 351 void testContextPointer() 352 { 353 int i; 354 struct T 355 { 356 int j; 357 int get() 358 { 359 return i * j; 360 } 361 } 362 T t = T(1); 363 t.tupleof[$-1] = cast(void*) 0xABCD; // Deterministic context pointer 364 test(t, t, `T(1, <context>: 0xabcd) != T(1, <context>: 0xabcd)`); 365 } 366 367 void testExternClasses() 368 { 369 { 370 extern(C++) static class Cpp 371 { 372 int a; 373 this(int a) { this.a = a; } 374 } 375 scope a = new Cpp(1); 376 scope b = new Cpp(2); 377 test(a, b, "Cpp(1) != Cpp(2)"); 378 test(a, Cpp.init, "Cpp(1) != null"); 379 } 380 { 381 extern(C++) static class CppToString 382 { 383 int a; 384 this(int a) { this.a = a; } 385 extern(D) string toString() const { return a == 0 ? "hello" : "world"; } 386 } 387 scope a = new CppToString(0); 388 scope b = new CppToString(1); 389 test(a, b, "hello != world"); 390 } 391 if (!__ctfe) 392 { 393 extern(C++) static class Opaque; 394 Opaque null_ = null; 395 Opaque notNull = cast(Opaque) &null_; 396 test(null_, notNull, "null != <Opaque>"); 397 } 398 { 399 extern(C++) static interface Stuff {} 400 scope Stuff stuff = new class Stuff {}; 401 test(stuff, Stuff.init, "Stuff() != null"); 402 } 403 } 404 405 void testShared() 406 { 407 static struct Small 408 { 409 int i; 410 } 411 412 auto s1 = shared Small(1); 413 const s2 = shared Small(2); 414 test(s1, s2, "Small(1) != Small(2)"); 415 416 static struct Big 417 { 418 long[10] l; 419 } 420 421 auto b1 = shared Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 422 const b2 = shared Big(); 423 test(b1, b2, "Big([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) != Big([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])"); 424 425 // Sanity check: Big shouldn't be supported by atomicLoad 426 import core.atomic : atomicLoad; 427 static assert( __traits(compiles, atomicLoad(s1))); 428 static assert(!__traits(compiles, atomicLoad(b1))); 429 430 static struct Fail 431 { 432 int value; 433 434 @safe pure nothrow @nogc: 435 bool opCast () shared const scope { return true; } 436 } 437 438 shared Fail fail = { value: 1 }; 439 assert(_d_assert_fail!(shared Fail)("==", fail) == "Fail(1) != true"); 440 assert(_d_assert_fail!(shared Fail)("==", fail, fail) == "Fail(1) != Fail(1)"); 441 } 442 443 void testException() 444 { 445 static struct MayThrow 446 { 447 int i; 448 string toString() 449 { 450 if (i == 1) 451 throw new Exception("Error"); 452 return "Some message"; 453 } 454 } 455 456 test(MayThrow(0), MayThrow(1), `Some message != <toString() failed: "Error", called on MayThrow(1)>`); 457 } 458 459 void testOverlappingFields() 460 { 461 static struct S 462 { 463 union 464 { 465 double num; 466 immutable(char)[] name; 467 } 468 } 469 470 test(S(1.0), S(2.0), "S(<overlapped field>, <overlapped field>) != S(<overlapped field>, <overlapped field>)"); 471 472 static struct S2 473 { 474 int valid; 475 union 476 { 477 double num; 478 immutable(char)[] name; 479 } 480 } 481 482 test(S2(4, 1.0), S2(5, 2.0), "S2(4, <overlapped field>, <overlapped field>) != S2(5, <overlapped field>, <overlapped field>)"); 483 484 static struct S3 485 { 486 union 487 { 488 double num; 489 immutable(char)[] name; 490 } 491 int valid; 492 } 493 S3 a = { 494 num: 1.0, 495 valid: 8 496 }; 497 498 S3 b = { 499 num: 1.0, 500 valid: 8 501 }; 502 test(a, b, "S3(<overlapped field>, <overlapped field>, 8) != S3(<overlapped field>, <overlapped field>, 8)"); 503 } 504 505 void testDestruction() 506 { 507 static class Test 508 { 509 __gshared string unary, binary; 510 __gshared bool run; 511 512 ~this() 513 { 514 run = true; 515 unary = _d_assert_fail!int("", 1); 516 binary = _d_assert_fail!int("==", 1, 2); 517 } 518 } 519 520 static void createGarbage() 521 { 522 new Test(); 523 new long[100]; 524 } 525 526 import core.memory : GC; 527 createGarbage(); 528 GC.collect(); 529 530 assert(Test.run); 531 assert(Test.unary == "Assertion failed (rich formatting is disabled in finalizers)"); 532 assert(Test.binary == "Assertion failed (rich formatting is disabled in finalizers)"); 533 } 534 535 int main() 536 { 537 testIntegers(); 538 testIntegerComparisons(); 539 testFloatingPoint(); 540 testPointers(); 541 testStrings(); 542 testToString(); 543 testArray(); 544 testStruct(); 545 testAA(); 546 testAttributes(); 547 testVoidArray(); 548 testTemporary(); 549 testEnum(); 550 testUnary(); 551 testTuple(); 552 if (!__ctfe) 553 testStructEquals(); 554 if (!__ctfe) 555 testStructEquals2(); 556 testStructEquals3(); 557 if (!__ctfe) 558 testStructEquals4(); 559 if (!__ctfe) 560 testStructEquals5(); 561 if (!__ctfe) 562 testStructEquals6(); 563 testContextPointer(); 564 testExternClasses(); 565 testShared(); 566 testException(); 567 testOverlappingFields(); 568 if (!__ctfe) 569 testDestruction(); 570 571 if (!__ctfe) 572 fprintf(stderr, "success.\n"); 573 return 0; 574 } 575 576 enum forceCTFE = main(); 577