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 }