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
testIntegers()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
testIntegerComparisons()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
testFloatingPoint()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
testPointers()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
testStrings()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
testToString()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
testArray()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
testStruct()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
testAA()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
testAttributes()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
testVoidArray()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
testTemporary()217 void testTemporary()
218 {
219 static struct Bad
220 {
221 ~this() @system {}
222 }
223
224 test!"!="(Bad(), Bad(), "Bad() == Bad()");
225 }
226
testEnum()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