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