TestResult.cpp (25755B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <stdint.h> 8 #include <string.h> 9 #include "mozilla/Casting.h" 10 #include "mozilla/ResultVariant.h" 11 #include "mozilla/Try.h" 12 #include "mozilla/UniquePtr.h" 13 14 using mozilla::Err; 15 using mozilla::GenericErrorResult; 16 using mozilla::Ok; 17 using mozilla::Result; 18 using mozilla::UniquePtr; 19 20 #define MOZ_STATIC_AND_RELEASE_ASSERT(expr) \ 21 static_assert(expr); \ 22 MOZ_RELEASE_ASSERT(expr) 23 24 enum struct TestUnusedZeroEnum : int16_t { Ok = 0, NotOk = 1 }; 25 26 namespace mozilla::detail { 27 template <> 28 struct UnusedZero<TestUnusedZeroEnum> : UnusedZeroEnum<TestUnusedZeroEnum> {}; 29 } // namespace mozilla::detail 30 31 struct Failed {}; 32 33 namespace mozilla::detail { 34 template <> 35 struct UnusedZero<Failed> { 36 using StorageType = uintptr_t; 37 38 static constexpr bool value = true; 39 static constexpr StorageType nullValue = 0; 40 static constexpr StorageType GetDefaultValue() { return 2; } 41 42 static constexpr void AssertValid(StorageType aValue) {} 43 static constexpr Failed Inspect(const StorageType& aValue) { 44 return Failed{}; 45 } 46 static constexpr Failed Unwrap(StorageType aValue) { return Failed{}; } 47 static constexpr StorageType Store(Failed aValue) { 48 return GetDefaultValue(); 49 } 50 }; 51 52 } // namespace mozilla::detail 53 54 // V is trivially default-constructible, and E has UnusedZero<E>::value == true, 55 // for a reference type and for a non-reference type 56 static_assert(mozilla::detail::SelectResultImpl<uintptr_t, Failed>::value == 57 mozilla::detail::PackingStrategy::NullIsOk); 58 static_assert( 59 mozilla::detail::SelectResultImpl<Ok, TestUnusedZeroEnum>::value == 60 mozilla::detail::PackingStrategy::NullIsOk); 61 static_assert(mozilla::detail::SelectResultImpl<Ok, Failed>::value == 62 mozilla::detail::PackingStrategy::LowBitTagIsError); 63 64 static_assert(std::is_trivially_destructible_v<Result<uintptr_t, Failed>>); 65 static_assert(std::is_trivially_destructible_v<Result<Ok, TestUnusedZeroEnum>>); 66 static_assert(std::is_trivially_destructible_v<Result<Ok, Failed>>); 67 68 static_assert( 69 sizeof(Result<bool, TestUnusedZeroEnum>) <= sizeof(uintptr_t), 70 "Result with bool value type should not be larger than pointer-sized"); 71 static_assert(sizeof(Result<Ok, Failed>) == sizeof(uint8_t), 72 "Result with empty value type should be size 1"); 73 static_assert(sizeof(Result<int*, Failed>) == sizeof(uintptr_t), 74 "Result with two aligned pointer types should be pointer-sized"); 75 static_assert( 76 sizeof(Result<char*, Failed*>) > sizeof(char*), 77 "Result with unaligned success type `char*` must not be pointer-sized"); 78 static_assert( 79 sizeof(Result<int*, char*>) > sizeof(char*), 80 "Result with unaligned error type `char*` must not be pointer-sized"); 81 82 enum Foo8 : uint8_t {}; 83 enum Foo16 : uint16_t {}; 84 enum Foo32 : uint32_t {}; 85 static_assert(sizeof(Result<Ok, Foo8>) <= sizeof(uintptr_t), 86 "Result with small types should be pointer-sized"); 87 static_assert(sizeof(Result<Ok, Foo16>) <= sizeof(uintptr_t), 88 "Result with small types should be pointer-sized"); 89 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) || 90 sizeof(Result<Ok, Foo32>) <= sizeof(uintptr_t), 91 "Result with small types should be pointer-sized"); 92 93 static_assert(sizeof(Result<Foo16, Foo8>) <= sizeof(uintptr_t), 94 "Result with small types should be pointer-sized"); 95 static_assert(sizeof(Result<Foo8, Foo16>) <= sizeof(uintptr_t), 96 "Result with small types should be pointer-sized"); 97 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) || 98 sizeof(Result<Foo32, Foo16>) <= sizeof(uintptr_t), 99 "Result with small types should be pointer-sized"); 100 static_assert(sizeof(Foo32) >= sizeof(uintptr_t) || 101 sizeof(Result<Foo16, Foo32>) <= sizeof(uintptr_t), 102 "Result with small types should be pointer-sized"); 103 104 #if __cplusplus < 202002L 105 static_assert(std::is_literal_type_v<Result<int*, Failed>>); 106 static_assert(std::is_literal_type_v<Result<Ok, Failed>>); 107 static_assert(std::is_literal_type_v<Result<Ok, Foo8>>); 108 static_assert(std::is_literal_type_v<Result<Foo8, Foo16>>); 109 static_assert(!std::is_literal_type_v<Result<Ok, UniquePtr<int>>>); 110 #endif 111 112 static constexpr GenericErrorResult<Failed> Fail() { return Err(Failed{}); } 113 114 static constexpr GenericErrorResult<TestUnusedZeroEnum> 115 FailTestUnusedZeroEnum() { 116 return Err(TestUnusedZeroEnum::NotOk); 117 } 118 119 static constexpr Result<Ok, Failed> Task1(bool pass) { 120 if (!pass) { 121 return Fail(); // implicit conversion from GenericErrorResult to Result 122 } 123 return Ok(); 124 } 125 126 static constexpr Result<Ok, TestUnusedZeroEnum> Task1UnusedZeroEnumErr( 127 bool pass) { 128 if (!pass) { 129 return FailTestUnusedZeroEnum(); // implicit conversion from 130 // GenericErrorResult to Result 131 } 132 return Ok(); 133 } 134 135 static constexpr Result<int, Failed> Task2(bool pass, int value) { 136 MOZ_TRY( 137 Task1(pass)); // converts one type of result to another in the error case 138 return value; // implicit conversion from T to Result<T, E> 139 } 140 141 static constexpr Result<int, TestUnusedZeroEnum> Task2UnusedZeroEnumErr( 142 bool pass, int value) { 143 MOZ_TRY(Task1UnusedZeroEnumErr( 144 pass)); // converts one type of result to another in the error case 145 return value; // implicit conversion from T to Result<T, E> 146 } 147 148 static Result<int, Failed> Task3(bool pass1, bool pass2, int value) { 149 auto x = MOZ_TRY(Task2(pass1, value)); 150 auto y = MOZ_TRY(Task2(pass2, value)); 151 return x + y; 152 } 153 154 static void BasicTests() { 155 MOZ_STATIC_AND_RELEASE_ASSERT(Task1(true).isOk()); 156 MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(true).isErr()); 157 MOZ_STATIC_AND_RELEASE_ASSERT(!Task1(false).isOk()); 158 MOZ_STATIC_AND_RELEASE_ASSERT(Task1(false).isErr()); 159 160 MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(true).isOk()); 161 MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(true).isErr()); 162 MOZ_STATIC_AND_RELEASE_ASSERT(!Task1UnusedZeroEnumErr(false).isOk()); 163 MOZ_STATIC_AND_RELEASE_ASSERT(Task1UnusedZeroEnumErr(false).isErr()); 164 MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk == 165 Task1UnusedZeroEnumErr(false).inspectErr()); 166 MOZ_STATIC_AND_RELEASE_ASSERT(TestUnusedZeroEnum::NotOk == 167 Task1UnusedZeroEnumErr(false).unwrapErr()); 168 169 // MOZ_TRY works. 170 MOZ_RELEASE_ASSERT(Task2(true, 3).isOk()); 171 MOZ_RELEASE_ASSERT(Task2(true, 3).unwrap() == 3); 172 MOZ_RELEASE_ASSERT(Task2(true, 3).unwrapOr(6) == 3); 173 MOZ_RELEASE_ASSERT(Task2(false, 3).isErr()); 174 MOZ_RELEASE_ASSERT(Task2(false, 3).unwrapOr(6) == 6); 175 176 MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).isOk()); 177 MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrap() == 3); 178 MOZ_STATIC_AND_RELEASE_ASSERT(Task2UnusedZeroEnumErr(true, 3).unwrapOr(6) == 179 3); 180 MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).isErr()); 181 MOZ_RELEASE_ASSERT(Task2UnusedZeroEnumErr(false, 3).unwrapOr(6) == 6); 182 183 MOZ_RELEASE_ASSERT(Task3(true, true, 3).isOk()); 184 MOZ_RELEASE_ASSERT(Task3(true, true, 3).unwrap() == 6); 185 MOZ_RELEASE_ASSERT(Task3(true, false, 3).isErr()); 186 MOZ_RELEASE_ASSERT(Task3(false, true, 3).isErr()); 187 MOZ_RELEASE_ASSERT(Task3(false, true, 3).unwrapOr(6) == 6); 188 189 // Lvalues should work too. 190 { 191 constexpr Result<Ok, Failed> res1 = Task1(true); 192 MOZ_STATIC_AND_RELEASE_ASSERT(res1.isOk()); 193 MOZ_STATIC_AND_RELEASE_ASSERT(!res1.isErr()); 194 195 constexpr Result<Ok, Failed> res2 = Task1(false); 196 MOZ_STATIC_AND_RELEASE_ASSERT(!res2.isOk()); 197 MOZ_STATIC_AND_RELEASE_ASSERT(res2.isErr()); 198 } 199 200 { 201 Result<int, Failed> res = Task2(true, 3); 202 MOZ_RELEASE_ASSERT(res.isOk()); 203 MOZ_RELEASE_ASSERT(res.unwrap() == 3); 204 205 res = Task2(false, 4); 206 MOZ_RELEASE_ASSERT(res.isErr()); 207 } 208 209 // Some tests for pointer tagging. 210 { 211 int i = 123; 212 213 Result<int*, Failed> res = &i; 214 static_assert(sizeof(res) == sizeof(uintptr_t), 215 "should use pointer tagging to fit in a word"); 216 217 MOZ_RELEASE_ASSERT(res.isOk()); 218 MOZ_RELEASE_ASSERT(*res.unwrap() == 123); 219 220 res = Err(Failed()); 221 MOZ_RELEASE_ASSERT(res.isErr()); 222 } 223 } 224 225 struct NonCopyableNonMovable { 226 explicit constexpr NonCopyableNonMovable(uint32_t aValue) : mValue(aValue) {} 227 228 NonCopyableNonMovable(const NonCopyableNonMovable&) = delete; 229 NonCopyableNonMovable(NonCopyableNonMovable&&) = delete; 230 NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete; 231 NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete; 232 233 uint32_t mValue; 234 }; 235 236 static void InPlaceConstructionTests() { 237 { 238 // PackingStrategy == NullIsOk 239 static_assert(mozilla::detail::SelectResultImpl<NonCopyableNonMovable, 240 Failed>::value == 241 mozilla::detail::PackingStrategy::NullIsOk); 242 constexpr Result<NonCopyableNonMovable, Failed> result{std::in_place, 42u}; 243 MOZ_STATIC_AND_RELEASE_ASSERT(42 == result.inspect().mValue); 244 } 245 246 { 247 // PackingStrategy == Variant 248 static_assert( 249 mozilla::detail::SelectResultImpl<NonCopyableNonMovable, int>::value == 250 mozilla::detail::PackingStrategy::Variant); 251 const Result<NonCopyableNonMovable, int> result{std::in_place, 42}; 252 MOZ_RELEASE_ASSERT(42 == result.inspect().mValue); 253 } 254 } 255 256 /* * */ 257 258 struct Snafu : Failed {}; 259 260 static Result<Ok, Snafu*> Explode() { 261 static Snafu snafu; 262 return Err(&snafu); 263 } 264 265 static Result<Ok, Failed*> ErrorGeneralization() { 266 MOZ_TRY(Explode()); // change error type from Snafu* to more general Failed* 267 return Ok(); 268 } 269 270 static void TypeConversionTests() { 271 MOZ_RELEASE_ASSERT(ErrorGeneralization().isErr()); 272 273 { 274 const Result<Ok, Failed*> res = Explode(); 275 MOZ_RELEASE_ASSERT(res.isErr()); 276 } 277 278 { 279 const Result<Ok, Failed*> res = Result<Ok, Snafu*>{Ok{}}; 280 MOZ_RELEASE_ASSERT(res.isOk()); 281 } 282 } 283 284 static void EmptyValueTest() { 285 struct Fine {}; 286 mozilla::Result<Fine, Failed> res((Fine())); 287 res.unwrap(); 288 MOZ_RELEASE_ASSERT(res.isOk()); 289 static_assert(sizeof(res) == sizeof(uint8_t), 290 "Result with empty value and error types should be size 1"); 291 } 292 293 static void MapTest() { 294 struct MyError { 295 int x; 296 297 explicit MyError(int y) : x(y) {} 298 }; 299 300 // Mapping over success values, to the same success type. 301 { 302 Result<int, MyError> res(5); 303 bool invoked = false; 304 auto res2 = res.map([&invoked](int x) { 305 MOZ_RELEASE_ASSERT(x == 5); 306 invoked = true; 307 return 6; 308 }); 309 MOZ_RELEASE_ASSERT(res2.isOk()); 310 MOZ_RELEASE_ASSERT(invoked); 311 MOZ_RELEASE_ASSERT(res2.unwrap() == 6); 312 } 313 314 // Mapping over success values, to a different success type. 315 { 316 Result<int, MyError> res(5); 317 bool invoked = false; 318 auto res2 = res.map([&invoked](int x) { 319 MOZ_RELEASE_ASSERT(x == 5); 320 invoked = true; 321 return "hello"; 322 }); 323 MOZ_RELEASE_ASSERT(res2.isOk()); 324 MOZ_RELEASE_ASSERT(invoked); 325 MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0); 326 } 327 328 // Mapping over success values (constexpr). 329 { 330 constexpr uint64_t kValue = 42u; 331 constexpr auto res2a = Result<int32_t, Failed>{5}.map([](int32_t x) { 332 MOZ_RELEASE_ASSERT(x == 5); 333 return kValue; 334 }); 335 MOZ_STATIC_AND_RELEASE_ASSERT(res2a.isOk()); 336 MOZ_STATIC_AND_RELEASE_ASSERT(kValue == res2a.inspect()); 337 } 338 339 // Mapping over error values. 340 { 341 MyError err(1); 342 Result<char, MyError> res(err); 343 MOZ_RELEASE_ASSERT(res.isErr()); 344 Result<char, MyError> res2 = res.map([](int x) { 345 MOZ_RELEASE_ASSERT(false); 346 return 'a'; 347 }); 348 MOZ_RELEASE_ASSERT(res2.isErr()); 349 MOZ_RELEASE_ASSERT(res2.unwrapErr().x == err.x); 350 } 351 352 // Function pointers instead of lambdas as the mapping function. 353 { 354 Result<const char*, MyError> res("hello"); 355 auto res2 = res.map(strlen); 356 MOZ_RELEASE_ASSERT(res2.isOk()); 357 MOZ_RELEASE_ASSERT(res2.unwrap() == 5); 358 } 359 } 360 361 static void MapErrTest() { 362 struct MyError { 363 int x; 364 365 explicit MyError(int y) : x(y) {} 366 }; 367 368 struct MyError2 { 369 int a; 370 371 explicit MyError2(int b) : a(b) {} 372 }; 373 374 // Mapping over error values, to the same error type. 375 { 376 MyError err(1); 377 Result<char, MyError> res(err); 378 MOZ_RELEASE_ASSERT(res.isErr()); 379 bool invoked = false; 380 auto res2 = res.mapErr([&invoked](const auto err) { 381 MOZ_RELEASE_ASSERT(err.x == 1); 382 invoked = true; 383 return MyError(2); 384 }); 385 MOZ_RELEASE_ASSERT(res2.isErr()); 386 MOZ_RELEASE_ASSERT(invoked); 387 MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2); 388 } 389 390 // Mapping over error values, to a different error type. 391 { 392 MyError err(1); 393 Result<char, MyError> res(err); 394 MOZ_RELEASE_ASSERT(res.isErr()); 395 bool invoked = false; 396 auto res2 = res.mapErr([&invoked](const auto err) { 397 MOZ_RELEASE_ASSERT(err.x == 1); 398 invoked = true; 399 return MyError2(2); 400 }); 401 MOZ_RELEASE_ASSERT(res2.isErr()); 402 MOZ_RELEASE_ASSERT(invoked); 403 MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2); 404 } 405 406 // Mapping over success values. 407 { 408 Result<int, MyError> res(5); 409 auto res2 = res.mapErr([](const auto err) { 410 MOZ_RELEASE_ASSERT(false); 411 return MyError(1); 412 }); 413 MOZ_RELEASE_ASSERT(res2.isOk()); 414 MOZ_RELEASE_ASSERT(res2.unwrap() == 5); 415 } 416 417 // Function pointers instead of lambdas as the mapping function. 418 { 419 Result<Ok, const char*> res("hello"); 420 auto res2 = res.mapErr(strlen); 421 MOZ_RELEASE_ASSERT(res2.isErr()); 422 MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5); 423 } 424 } 425 426 static Result<Ok, size_t> strlen_ResultWrapper(const char* aValue) { 427 return Err(strlen(aValue)); 428 } 429 430 static void OrElseTest() { 431 struct MyError { 432 int x; 433 434 explicit constexpr MyError(int y) : x(y) {} 435 }; 436 437 struct MyError2 { 438 int a; 439 440 explicit constexpr MyError2(int b) : a(b) {} 441 }; 442 443 // `orElse`ing over error values, to Result<V, E> (the same error type) error 444 // variant. 445 { 446 MyError err(1); 447 Result<char, MyError> res(err); 448 MOZ_RELEASE_ASSERT(res.isErr()); 449 bool invoked = false; 450 auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> { 451 MOZ_RELEASE_ASSERT(err.x == 1); 452 invoked = true; 453 if (err.x != 42) { 454 return Err(MyError(2)); 455 } 456 return 'a'; 457 }); 458 MOZ_RELEASE_ASSERT(res2.isErr()); 459 MOZ_RELEASE_ASSERT(invoked); 460 MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2); 461 } 462 463 // `orElse`ing over error values, to Result<V, E> (the same error type) 464 // success variant. 465 { 466 MyError err(42); 467 Result<char, MyError> res(err); 468 MOZ_RELEASE_ASSERT(res.isErr()); 469 bool invoked = false; 470 auto res2 = res.orElse([&invoked](const auto err) -> Result<char, MyError> { 471 MOZ_RELEASE_ASSERT(err.x == 42); 472 invoked = true; 473 if (err.x != 42) { 474 return Err(MyError(2)); 475 } 476 return 'a'; 477 }); 478 MOZ_RELEASE_ASSERT(res2.isOk()); 479 MOZ_RELEASE_ASSERT(invoked); 480 MOZ_RELEASE_ASSERT(res2.unwrap() == 'a'); 481 } 482 483 // `orElse`ing over error values, to Result<V, E2> (a different error type) 484 // error variant. 485 { 486 MyError err(1); 487 Result<char, MyError> res(err); 488 MOZ_RELEASE_ASSERT(res.isErr()); 489 bool invoked = false; 490 auto res2 = 491 res.orElse([&invoked](const auto err) -> Result<char, MyError2> { 492 MOZ_RELEASE_ASSERT(err.x == 1); 493 invoked = true; 494 if (err.x != 42) { 495 return Err(MyError2(2)); 496 } 497 return 'a'; 498 }); 499 MOZ_RELEASE_ASSERT(res2.isErr()); 500 MOZ_RELEASE_ASSERT(invoked); 501 MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2); 502 } 503 504 // `orElse`ing over error values, to Result<V, E2> (a different error type) 505 // success variant. 506 { 507 MyError err(42); 508 Result<char, MyError> res(err); 509 MOZ_RELEASE_ASSERT(res.isErr()); 510 bool invoked = false; 511 auto res2 = 512 res.orElse([&invoked](const auto err) -> Result<char, MyError2> { 513 MOZ_RELEASE_ASSERT(err.x == 42); 514 invoked = true; 515 if (err.x != 42) { 516 return Err(MyError2(2)); 517 } 518 return 'a'; 519 }); 520 MOZ_RELEASE_ASSERT(res2.isOk()); 521 MOZ_RELEASE_ASSERT(invoked); 522 MOZ_RELEASE_ASSERT(res2.unwrap() == 'a'); 523 } 524 525 // `orElse`ing over success values. 526 { 527 Result<int, MyError> res(5); 528 auto res2 = res.orElse([](const auto err) -> Result<int, MyError> { 529 MOZ_RELEASE_ASSERT(false); 530 return Err(MyError(1)); 531 }); 532 MOZ_RELEASE_ASSERT(res2.isOk()); 533 MOZ_RELEASE_ASSERT(res2.unwrap() == 5); 534 } 535 536 // Function pointers instead of lambdas as the `orElse`ing function. 537 { 538 Result<Ok, const char*> res("hello"); 539 auto res2 = res.orElse(strlen_ResultWrapper); 540 MOZ_RELEASE_ASSERT(res2.isErr()); 541 MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5); 542 } 543 } 544 545 static void AndThenTest() { 546 // `andThen`ing over success results. 547 { 548 Result<int, const char*> r1(10); 549 Result<int, const char*> r2 = 550 r1.andThen([](int x) { return Result<int, const char*>(x + 1); }); 551 MOZ_RELEASE_ASSERT(r2.isOk()); 552 MOZ_RELEASE_ASSERT(r2.unwrap() == 11); 553 } 554 555 // `andThen`ing over success results (constexpr). 556 { 557 constexpr Result<int, Failed> r2a = Result<int, Failed>{10}.andThen( 558 [](int x) { return Result<int, Failed>(x + 1); }); 559 MOZ_STATIC_AND_RELEASE_ASSERT(r2a.isOk()); 560 MOZ_STATIC_AND_RELEASE_ASSERT(r2a.inspect() == 11); 561 } 562 563 // `andThen`ing over error results. 564 { 565 Result<int, const char*> r3("error"); 566 Result<int, const char*> r4 = r3.andThen([](int x) { 567 MOZ_RELEASE_ASSERT(false); 568 return Result<int, const char*>(1); 569 }); 570 MOZ_RELEASE_ASSERT(r4.isErr()); 571 MOZ_RELEASE_ASSERT(r3.unwrapErr() == r4.unwrapErr()); 572 } 573 574 // andThen with a function accepting an rvalue 575 { 576 Result<int, const char*> r1(10); 577 Result<int, const char*> r2 = 578 r1.andThen([](int&& x) { return Result<int, const char*>(x + 1); }); 579 MOZ_RELEASE_ASSERT(r2.isOk()); 580 MOZ_RELEASE_ASSERT(r2.unwrap() == 11); 581 } 582 583 // `andThen`ing over error results (constexpr). 584 { 585 constexpr Result<int, Failed> r4a = 586 Result<int, Failed>{Failed{}}.andThen([](int x) { 587 MOZ_RELEASE_ASSERT(false); 588 return Result<int, Failed>(1); 589 }); 590 MOZ_STATIC_AND_RELEASE_ASSERT(r4a.isErr()); 591 } 592 } 593 594 using UniqueResult = Result<UniquePtr<int>, const char*>; 595 596 static UniqueResult UniqueTask() { return mozilla::MakeUnique<int>(3); } 597 static UniqueResult UniqueTaskError() { return Err("bad"); } 598 599 using UniqueErrorResult = Result<int, UniquePtr<int>>; 600 static UniqueErrorResult UniqueError() { 601 return Err(mozilla::MakeUnique<int>(4)); 602 } 603 604 static Result<Ok, UniquePtr<int>> TryUniqueErrorResult() { 605 MOZ_TRY(UniqueError()); 606 return Ok(); 607 } 608 609 static void UniquePtrTest() { 610 { 611 auto result = UniqueTask(); 612 MOZ_RELEASE_ASSERT(result.isOk()); 613 auto ptr = result.unwrap(); 614 MOZ_RELEASE_ASSERT(ptr); 615 MOZ_RELEASE_ASSERT(*ptr == 3); 616 auto moved = result.unwrap(); 617 MOZ_RELEASE_ASSERT(!moved); 618 } 619 620 { 621 auto err = UniqueTaskError(); 622 MOZ_RELEASE_ASSERT(err.isErr()); 623 auto ptr = err.unwrapOr(mozilla::MakeUnique<int>(4)); 624 MOZ_RELEASE_ASSERT(ptr); 625 MOZ_RELEASE_ASSERT(*ptr == 4); 626 } 627 628 { 629 auto result = UniqueTaskError(); 630 result = UniqueResult(mozilla::MakeUnique<int>(6)); 631 MOZ_RELEASE_ASSERT(result.isOk()); 632 MOZ_RELEASE_ASSERT(result.inspect() && *result.inspect() == 6); 633 } 634 635 { 636 auto result = UniqueError(); 637 MOZ_RELEASE_ASSERT(result.isErr()); 638 MOZ_RELEASE_ASSERT(result.inspectErr()); 639 MOZ_RELEASE_ASSERT(*result.inspectErr() == 4); 640 auto err = result.unwrapErr(); 641 MOZ_RELEASE_ASSERT(!result.inspectErr()); 642 MOZ_RELEASE_ASSERT(err); 643 MOZ_RELEASE_ASSERT(*err == 4); 644 645 result = UniqueErrorResult(0); 646 MOZ_RELEASE_ASSERT(result.isOk() && result.unwrap() == 0); 647 } 648 649 { 650 auto result = TryUniqueErrorResult(); 651 MOZ_RELEASE_ASSERT(result.isErr()); 652 auto err = result.unwrapErr(); 653 MOZ_RELEASE_ASSERT(err && *err == 4); 654 MOZ_RELEASE_ASSERT(!result.inspectErr()); 655 } 656 } 657 658 struct ZeroIsUnusedStructForPointer { 659 int x = 1; 660 }; 661 enum class ZeroIsUnusedEnum1 : uint8_t { 662 V1 = 1, 663 V2 = 2, 664 }; 665 enum class ZeroIsUnusedEnum2 : uint16_t { 666 V1 = 1, 667 V2 = 2, 668 }; 669 enum class ZeroIsUnusedEnum4 : uint32_t { 670 V1 = 1, 671 V2 = 2, 672 }; 673 enum class ZeroIsUnusedEnum8 : uint64_t { 674 V1 = 1, 675 V2 = 2, 676 }; 677 struct EmptyErrorStruct {}; 678 679 template <> 680 struct mozilla::detail::UnusedZero<ZeroIsUnusedStructForPointer*> { 681 static const bool value = true; 682 }; 683 template <> 684 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum1> { 685 static const bool value = true; 686 }; 687 template <> 688 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum2> { 689 static const bool value = true; 690 }; 691 template <> 692 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum4> { 693 static const bool value = true; 694 }; 695 template <> 696 struct mozilla::detail::UnusedZero<ZeroIsUnusedEnum8> { 697 static const bool value = true; 698 }; 699 700 static void ZeroIsEmptyErrorTest() { 701 { 702 ZeroIsUnusedStructForPointer s; 703 704 using V = ZeroIsUnusedStructForPointer*; 705 706 mozilla::Result<V, EmptyErrorStruct> result(&s); 707 MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V)); 708 709 MOZ_RELEASE_ASSERT(result.isOk()); 710 MOZ_RELEASE_ASSERT(result.inspect() == &s); 711 } 712 713 { 714 using V = ZeroIsUnusedStructForPointer*; 715 716 mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct{})); 717 718 MOZ_RELEASE_ASSERT(result.isErr()); 719 MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<V*>(&result) == nullptr); 720 } 721 722 { 723 ZeroIsUnusedEnum1 e = ZeroIsUnusedEnum1::V1; 724 725 using V = ZeroIsUnusedEnum1; 726 727 mozilla::Result<V, EmptyErrorStruct> result(e); 728 MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V)); 729 730 MOZ_RELEASE_ASSERT(result.isOk()); 731 MOZ_RELEASE_ASSERT(result.inspect() == e); 732 } 733 734 { 735 using V = ZeroIsUnusedEnum1; 736 737 mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct())); 738 739 MOZ_RELEASE_ASSERT(result.isErr()); 740 MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint8_t*>(&result) == 0); 741 } 742 743 { 744 ZeroIsUnusedEnum2 e = ZeroIsUnusedEnum2::V1; 745 746 using V = ZeroIsUnusedEnum2; 747 748 mozilla::Result<V, EmptyErrorStruct> result(e); 749 MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V)); 750 751 MOZ_RELEASE_ASSERT(result.isOk()); 752 MOZ_RELEASE_ASSERT(result.inspect() == e); 753 } 754 755 { 756 using V = ZeroIsUnusedEnum2; 757 758 mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct())); 759 760 MOZ_RELEASE_ASSERT(result.isErr()); 761 MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint16_t*>(&result) == 0); 762 } 763 764 { 765 ZeroIsUnusedEnum4 e = ZeroIsUnusedEnum4::V1; 766 767 using V = ZeroIsUnusedEnum4; 768 769 mozilla::Result<V, EmptyErrorStruct> result(e); 770 MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V)); 771 772 MOZ_RELEASE_ASSERT(result.isOk()); 773 MOZ_RELEASE_ASSERT(result.inspect() == e); 774 } 775 776 { 777 using V = ZeroIsUnusedEnum4; 778 779 mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct())); 780 781 MOZ_RELEASE_ASSERT(result.isErr()); 782 MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint32_t*>(&result) == 0); 783 } 784 785 { 786 ZeroIsUnusedEnum8 e = ZeroIsUnusedEnum8::V1; 787 788 using V = ZeroIsUnusedEnum8; 789 790 mozilla::Result<V, EmptyErrorStruct> result(e); 791 MOZ_RELEASE_ASSERT(sizeof(result) == sizeof(V)); 792 793 MOZ_RELEASE_ASSERT(result.isOk()); 794 MOZ_RELEASE_ASSERT(result.inspect() == e); 795 } 796 797 { 798 using V = ZeroIsUnusedEnum8; 799 800 mozilla::Result<V, EmptyErrorStruct> result(Err(EmptyErrorStruct())); 801 802 MOZ_RELEASE_ASSERT(result.isErr()); 803 MOZ_RELEASE_ASSERT(*mozilla::BitwiseCast<uint64_t*>(&result) == 0); 804 } 805 } 806 807 class Foo {}; 808 809 class C1 {}; 810 class C2 : public C1 {}; 811 812 class E1 {}; 813 class E2 : public E1 {}; 814 815 void UpcastTest() { 816 { 817 C2 c2; 818 819 mozilla::Result<C2*, Failed> result(&c2); 820 mozilla::Result<C1*, Failed> copied(std::move(result)); 821 822 MOZ_RELEASE_ASSERT(copied.inspect() == &c2); 823 } 824 825 { 826 E2 e2; 827 828 mozilla::Result<Foo, E2*> result(Err(&e2)); 829 mozilla::Result<Foo, E1*> copied(std::move(result)); 830 831 MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2); 832 } 833 834 { 835 C2 c2; 836 837 mozilla::Result<C2*, E2*> result(&c2); 838 mozilla::Result<C1*, E1*> copied(std::move(result)); 839 840 MOZ_RELEASE_ASSERT(copied.inspect() == &c2); 841 } 842 843 { 844 E2 e2; 845 846 mozilla::Result<C2*, E2*> result(Err(&e2)); 847 mozilla::Result<C1*, E1*> copied(std::move(result)); 848 849 MOZ_RELEASE_ASSERT(copied.inspectErr() == &e2); 850 } 851 } 852 853 void EqualityTest() { 854 { 855 Result<int, bool> result(1); 856 Result<int, bool> other(1); 857 858 MOZ_RELEASE_ASSERT(result == other); 859 } 860 861 { 862 Result<int, bool> result(true); 863 Result<int, bool> other(true); 864 865 MOZ_RELEASE_ASSERT(result == other); 866 } 867 868 { 869 Result<int, bool> result(1); 870 Result<int, bool> other(2); 871 872 MOZ_RELEASE_ASSERT(result != other); 873 } 874 875 { 876 Result<int, bool> result(true); 877 Result<int, bool> other(false); 878 879 MOZ_RELEASE_ASSERT(result != other); 880 } 881 882 { 883 Result<int, bool> result(0); 884 Result<int, bool> other(false); 885 886 MOZ_RELEASE_ASSERT(result != other); 887 } 888 889 { 890 Result<int, unsigned int> result(1); 891 Result<int, unsigned int> other(1u); 892 893 MOZ_RELEASE_ASSERT(result != other); 894 } 895 } 896 897 /* * */ 898 899 int main() { 900 BasicTests(); 901 InPlaceConstructionTests(); 902 TypeConversionTests(); 903 EmptyValueTest(); 904 MapTest(); 905 MapErrTest(); 906 OrElseTest(); 907 AndThenTest(); 908 UniquePtrTest(); 909 ZeroIsEmptyErrorTest(); 910 UpcastTest(); 911 EqualityTest(); 912 return 0; 913 }