1 module tests;
2 
3 import expected;
4 version (D_Exceptions) import std.exception;
5 
6 version (D_BetterC)
7 {
8     extern(C) void main()
9     {
10         import core.stdc.stdio;
11         import std.meta : AliasSeq;
12 
13         alias modules = AliasSeq!(expected, tests);
14         static foreach (m; modules)
15         {
16             static foreach(u; __traits(getUnitTests, m)) {
17                 debug printf("testing " ~ m.stringof ~ ": '" ~ __traits(getAttributes, u)[0] ~ "'\n");
18                 u();
19             }
20         }
21         printf("All unit tests have been run successfully.\n");
22     }
23 }
24 
25 @safe:
26 
27 @("Expected.init")
28 @system nothrow unittest
29 {
30     struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
31 
32     {
33         auto res = Expected!(int, string).init;
34         assert(!res.hasValue && !res.hasError);
35         assert(res);
36         version (D_Exceptions)
37         {
38             assertThrown!Throwable(res.value);
39             assertThrown!Throwable(res.error);
40         }
41         static assert(!__traits(compiles, res = 42));
42     }
43 
44     {
45         auto res = Expected!(int, string, EnableDefaultConstructor).init;
46         assert(!res.hasValue && !res.hasError);
47         assert(res);
48         assert(res.value == 0);
49         assert(res.error is null);
50         res = 42;
51         assert(res.value == 42);
52     }
53 
54     // T == void
55     {
56         auto res = Expected!(void, string).init;
57         static assert(!__traits(compiles, res.hasValue));
58         static assert(!__traits(compiles, res.value));
59         static assert(!__traits(compiles, res = "foo"));
60         assert(!res.hasError);
61         assert(res);
62         version (D_Exceptions) assertThrown!Throwable(res.error);
63     }
64 
65     // T == void
66     {
67         auto res = Expected!(void, string, EnableDefaultConstructor).init;
68         static assert(!__traits(compiles, res.hasValue));
69         static assert(!__traits(compiles, res.value));
70         assert(!res.hasError);
71         assert(res);
72         assert(res.error is null);
73         res = "foo";
74         assert(res.error == "foo");
75     }
76 }
77 
78 @("Default constructor - disabled")
79 unittest
80 {
81     static assert(!__traits(compiles, Expected!(int, string)()));
82 }
83 
84 @("Default constructor - enabled")
85 @system nothrow unittest
86 {
87     struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
88     {
89         auto res = Expected!(int, string, EnableDefaultConstructor)();
90         assert(!res.hasValue && !res.hasError);
91         assert(res);
92         assert(res.value == 0);
93         assert(res.error is null);
94         res = 42;
95         assert(res);
96         assert(res.value == 42);
97     }
98 
99     {
100         auto res = Expected!(void, string, EnableDefaultConstructor)();
101         assert(!res.hasError);
102         assert(res);
103         assert(res.error is null);
104         res = "foo";
105         assert(res.hasError);
106         assert(!res);
107         assert(res.error == "foo");
108     }
109 }
110 
111 @("Default types")
112 nothrow @nogc unittest
113 {
114     auto res = Expected!(int)(42);
115     assert(res);
116     assert(res.hasValue && !res.hasError);
117     assert(res.value == 42);
118     res.value = 43;
119     assert(res.value == 43);
120 }
121 
122 @("Default types with const payload")
123 nothrow @nogc unittest
124 {
125     alias Exp = Expected!(const(int));
126     static assert(is(typeof(Exp.init.value) == const(int)));
127     auto res = Exp(42);
128     assert(res);
129     assert(res.hasValue && !res.hasError);
130     assert(res.value == 42);
131     static assert(!__traits(compiles, res.value = res.value));
132 }
133 
134 @("Default types with immutable payload")
135 unittest
136 {
137     alias Exp = Expected!(immutable(int));
138     static assert(is(typeof(Exp.init.value) == immutable(int)));
139     auto res = Exp(42);
140     assert(res);
141     assert(res.hasValue && !res.hasError);
142     assert(res.value == 42);
143     static assert(!__traits(compiles, res.value = res.value));
144 }
145 
146 @("opAssign")
147 @system nothrow unittest
148 {
149     struct EnableDefaultConstructor { static immutable bool enableDefaultConstructor = true; }
150     // value
151     {
152         auto res = Expected!(int, string, EnableDefaultConstructor).init;
153         res = 42;
154         assert(res);
155         assert(res.hasValue && !res.hasError);
156         assert(res.value == 42);
157         res = 43;
158         version (D_Exceptions) assertThrown!Throwable(res = "foo");
159     }
160 
161     // error
162     {
163         auto res = Expected!(int, string, EnableDefaultConstructor)("42");
164         assert(!res.hasValue && res.hasError);
165         assert(res.error == "42");
166         res = "foo";
167         assert(res.error == "foo");
168         version (D_Exceptions) assertThrown!Throwable(res = 42);
169     }
170 }
171 
172 @("Same types")
173 @system nothrow unittest
174 {
175     // value
176     {
177         alias Exp = Expected!(int, int);
178         auto res = Exp(42);
179         assert(res);
180         assert(res.hasValue && !res.hasError);
181         assert(res.value == 42);
182         version (D_Exceptions) assertThrown!Throwable(res.error());
183     }
184 
185     // error
186     {
187         alias Exp = Expected!(int, int);
188         auto res = Exp(42, false);
189         assert(!res);
190         assert(!res.hasValue && res.hasError);
191         assert(res.error == 42);
192         version (D_Exceptions) assertThrown!Throwable(res.value());
193         assert(err!int(42).error == 42);
194     }
195 
196     // immutable value
197     {
198         alias Exp = Expected!(immutable(int), immutable(int));
199         auto res = Exp(immutable int(42));
200         assert(res);
201         assert(res.hasValue && !res.hasError);
202         assert(res.value == 42);
203         version (D_Exceptions) assertThrown!Throwable(res.error());
204     }
205 
206     // immutable error
207     {
208         alias Exp = Expected!(immutable(int), immutable(int));
209         auto res = Exp(immutable int(42), false);
210         assert(!res);
211         assert(!res.hasValue && res.hasError);
212         assert(res.error == 42);
213         version (D_Exceptions) assertThrown!Throwable(res.value());
214         assert(err!(immutable(int))(immutable int(42)).error == 42);
215     }
216 
217     // const mix
218     {
219         alias Exp = Expected!(const(int), int);
220         auto res = Exp(const int(42));
221         auto val = res.value;
222         static assert(is(typeof(val) == const int));
223         assert(res);
224         assert(res.hasValue && !res.hasError);
225         assert(res.value == 42);
226         version (D_Exceptions) assertThrown!Throwable(res.error);
227     }
228 
229     // const mix
230     {
231         alias Exp = Expected!(const(int), int);
232         auto res = Exp(42);
233         auto err = res.error;
234         static assert(is(typeof(err) == int));
235         assert(!res);
236         assert(!res.hasValue && res.hasError);
237         assert(res.error == 42);
238         version (D_Exceptions) assertThrown!Throwable(res.value);
239     }
240 
241     // immutable mix
242     {
243         alias Exp = Expected!(immutable(int), int);
244         auto res = Exp(immutable int(42));
245         auto val = res.value;
246         static assert(is(typeof(val) == immutable int));
247         assert(res);
248         assert(res.hasValue && !res.hasError);
249         assert(res.value == 42);
250         version (D_Exceptions) assertThrown!Throwable(res.error);
251     }
252 
253     // immutable mix
254     {
255         alias Exp = Expected!(immutable(int), int);
256         auto res = Exp(42);
257         auto err = res.error;
258         static assert(is(typeof(err) == int));
259         assert(!res);
260         assert(!res.hasValue && res.hasError);
261         assert(res.error == 42);
262         version (D_Exceptions) assertThrown!Throwable(res.value);
263     }
264 
265     // immutable mix reverse
266     {
267         alias Exp = Expected!(int, immutable(int));
268         auto res = Exp(immutable int(42));
269         auto err = res.error;
270         static assert(is(typeof(err) == immutable int));
271         assert(!res);
272         assert(!res.hasValue && res.hasError);
273         assert(res.error == 42);
274         version (D_Exceptions) assertThrown!Throwable(res.value);
275     }
276 
277     // immutable mix reverse
278     {
279         alias Exp = Expected!(int, immutable(int));
280         auto res = Exp(42);
281         auto val = res.value;
282         static assert(is(typeof(val) == int));
283         assert(res);
284         assert(res.hasValue && !res.hasError);
285         assert(res.value == 42);
286         version (D_Exceptions) assertThrown!Throwable(res.error);
287     }
288 }
289 
290 @("void payload")
291 nothrow @nogc unittest
292 {
293     alias Exp = Expected!(void, int);
294     static assert (!__traits(hasMember, Exp, "hasValue"));
295     static assert (!__traits(hasMember, Exp, "value"));
296 
297     {
298         auto res = Exp();
299         assert(res);
300         assert(!res.hasError);
301     }
302 
303     {
304         auto res = Exp(42);
305         assert(!res);
306         assert(res.hasError);
307         assert(res.error == 42);
308     }
309 }
310 
311 @("opEquals")
312 unittest
313 {
314     assert(ok(42) == 42);
315     assert(ok(42) != 43);
316     assert(ok("foo") == "foo");
317     assert(ok("foo") != "bar");
318     assert(ok("foo") == cast(const string)"foo");
319     assert(ok("foo") == cast(immutable string)"foo");
320     assert(ok(42) == ok(42));
321     assert(ok(42) != ok(43));
322     assert(ok(42) != err!int("42"));
323 
324     static assert(!__traits(compiles, err("foo") == "foo"));
325     assert(err(42) == err(42));
326     assert(err(42) != err(43));
327     assert(err("foo") == err("foo"));
328     assert(err("foo") != err("bar"));
329 }
330 
331 //FIXME: doesn't work - some older dmd error
332 static if (__VERSION__ >= 2082)
333 {
334     @("toHash")
335     unittest
336     {
337         assert(ok(42).hashOf == 42.hashOf);
338         assert(ok(42).hashOf != 43.hashOf);
339         assert(ok(42).hashOf == ok(42).hashOf);
340         assert(ok(42).hashOf != ok(43).hashOf);
341         assert(ok(42).hashOf == ok!bool(42).hashOf);
342         assert(ok(42).hashOf != err("foo").hashOf);
343         assert(err("foo").hashOf == err("foo").hashOf);
344     }
345 }
346 
347 @("range interface")
348 unittest
349 {
350     {
351         auto r = ok(42);
352         assert(!r.empty);
353         assert(r.front == 42);
354         r.popFront();
355         assert(r.empty);
356     }
357 
358     {
359         auto r = err!int("foo");
360         assert(r.empty);
361     }
362 
363     {
364         auto r = err("foo");
365         static assert(!__traits(compiles, r.empty));
366         static assert(!__traits(compiles, r.front));
367         static assert(!__traits(compiles, r.popFront));
368     }
369 
370     // with forced check
371     {
372         struct Hook {
373             static immutable bool enableCopyConstructor = false;
374             static void onUnchecked() @safe { assert(0); }
375         }
376 
377         auto res = ok(42);
378         assert(!res.empty);
379         assert(res.front == 42);
380         res.popFront(); assert(res.empty);
381     }
382 }
383 
384 @("Complex payload")
385 unittest
386 {
387     {
388         struct Value { int val; }
389 
390         assert(ok(Value(42)).hasValue);
391         assert(ok(const Value(42)).hasValue);
392         assert(ok(immutable Value(42)).hasValue);
393     }
394 
395     {
396         struct DisabledValue { int val; @disable this(this); }
397 
398         assert(ok(DisabledValue(42)).hasValue);
399         //FIXME?
400         //assert(ok(const DisabledValue(42)).hasValue);
401         // assert(ok(immutable DisabledValue(42)).hasValue);
402     }
403 }
404 
405 @("RC payload")
406 unittest
407 {
408     struct Hook {
409         static immutable bool enableRefCountedPayload = true;
410         static immutable bool enableDefaultConstructor = true;
411         static void onUnchecked() pure nothrow @nogc { assert(0, "result unchecked"); }
412     }
413 
414     static assert(isDefaultConstructorEnabled!Hook);
415 
416     {
417         auto e = ok!(bool, Hook)(42);
418         e = 43;
419         assert(e.value == 43);
420     }
421 
422     struct Value { int val; }
423 
424     auto res = ok!(bool, Hook)(Value(42));
425     assert(res.hasValue);
426 
427     assert(ok!(bool, Hook)(true).hasValue);
428     assert(err!(bool, Hook)(true).hasError);
429     assert(ok!(bool, Hook)(const Value(42)).hasValue);
430     assert(ok!(bool, Hook)(immutable Value(42)).hasValue);
431 
432     // same types
433     assert(ok!(int, Hook)(42).value == 42);
434     assert(err!(int, Hook)(42).error == 42);
435 
436     // forced check
437     () @trusted {
438         version (D_Exceptions) assertThrown!Throwable({ ok!(bool, Hook)(42); }());
439     }();
440 
441     //FIXME?
442     //immutable r = ok!(bool, Hook)(immutable Value(42));
443     // immutable r = Expected!(immutable(Value), bool, Hook)(immutable Value(42));
444     // assert(r.value == 42);
445 }
446 
447 @("void hook")
448 unittest
449 {
450     auto empty = Expected!(int, string, void).init;
451     assert(!empty.hasValue);
452     assert(!empty.hasError);
453     assert(empty.value == int.init);
454     assert(empty.error is null);
455 }
456 
457 @("system pred")
458 @system unittest
459 {
460     auto foo() @system { return ok; }
461     static assert(__traits(compiles, ok.andThen!foo));
462     static assert(__traits(compiles, err("foo").orElse!foo));
463 }
464 
465 @("disabled copy constructor")
466 @system unittest
467 {
468     import std.algorithm : move;
469 
470     static int destroyed;
471 
472     static struct Foo
473     {
474         int n;
475         @disable this(this);
476         ~this()
477         {
478             destroyed += n;
479         }
480     }
481 
482     static auto gen()
483     {
484         Foo f;
485         f.n = 42;
486         return ok!int(f.move());
487     }
488 
489     {
490         Foo f;
491         f = gen().expect("uh");
492     }
493     assert(destroyed == 42);
494 }