| // Copyright 2020 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "absl/status/statusor.h" | |
| #include <any> | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <initializer_list> | |
| #include <map> | |
| #include <memory> | |
| #include <ostream> | |
| #include <sstream> | |
| #include <stdexcept> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "absl/base/casts.h" | |
| #include "absl/memory/memory.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/types/source_location.h" | |
| namespace { | |
| using ::absl_testing::IsOk; | |
| using ::absl_testing::IsOkAndHolds; | |
| using ::testing::AllOf; | |
| using ::testing::AnyOf; | |
| using ::testing::AnyWith; | |
| using ::testing::ElementsAre; | |
| using ::testing::EndsWith; | |
| using ::testing::Field; | |
| using ::testing::HasSubstr; | |
| using ::testing::Ne; | |
| using ::testing::Not; | |
| using ::testing::Pointee; | |
| using ::testing::StartsWith; | |
| using ::testing::VariantWith; | |
| struct CopyDetector { | |
| CopyDetector() = default; | |
| explicit CopyDetector(int xx) : x(xx) {} | |
| CopyDetector(CopyDetector&& d) noexcept | |
| : x(d.x), copied(false), moved(true) {} | |
| CopyDetector(const CopyDetector& d) : x(d.x), copied(true), moved(false) {} | |
| CopyDetector& operator=(const CopyDetector& c) { | |
| x = c.x; | |
| copied = true; | |
| moved = false; | |
| return *this; | |
| } | |
| CopyDetector& operator=(CopyDetector&& c) noexcept { | |
| x = c.x; | |
| copied = false; | |
| moved = true; | |
| return *this; | |
| } | |
| int x = 0; | |
| bool copied = false; | |
| bool moved = false; | |
| }; | |
| testing::Matcher<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) { | |
| return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b), | |
| Field(&CopyDetector::copied, c)); | |
| } | |
| class Base1 { | |
| public: | |
| virtual ~Base1() = default; | |
| int pad; | |
| }; | |
| class Base2 { | |
| public: | |
| virtual ~Base2() = default; | |
| int yetotherpad; | |
| }; | |
| class Derived : public Base1, public Base2 { | |
| public: | |
| ~Derived() override = default; | |
| int evenmorepad; | |
| }; | |
| class CopyNoAssign { | |
| public: | |
| explicit CopyNoAssign(int value) : foo(value) {} | |
| CopyNoAssign(const CopyNoAssign& other) = default; | |
| int foo; | |
| private: | |
| const CopyNoAssign& operator=(const CopyNoAssign&); | |
| }; | |
| absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() { | |
| // Uses implicit constructor from T&& | |
| return std::make_unique<int>(0); | |
| } | |
| TEST(StatusOr, ElementType) { | |
| static_assert(std::is_same<absl::StatusOr<int>::value_type, int>()); | |
| static_assert(std::is_same<absl::StatusOr<char>::value_type, char>()); | |
| } | |
| TEST(StatusOr, TestMoveOnlyInitialization) { | |
| absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); | |
| ASSERT_TRUE(thing.ok()); | |
| EXPECT_EQ(0, **thing); | |
| int* previous = thing->get(); | |
| thing = ReturnUniquePtr(); | |
| EXPECT_TRUE(thing.ok()); | |
| EXPECT_EQ(0, **thing); | |
| EXPECT_NE(previous, thing->get()); | |
| } | |
| TEST(StatusOr, TestMoveOnlyValueExtraction) { | |
| absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); | |
| ASSERT_TRUE(thing.ok()); | |
| std::unique_ptr<int> ptr = *std::move(thing); | |
| EXPECT_EQ(0, *ptr); | |
| thing = std::move(ptr); | |
| ptr = std::move(*thing); | |
| EXPECT_EQ(0, *ptr); | |
| } | |
| TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { | |
| std::unique_ptr<int> ptr(*ReturnUniquePtr()); | |
| EXPECT_EQ(0, *ptr); | |
| } | |
| TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { | |
| static_assert( | |
| std::is_same< | |
| const int&&, | |
| decltype(std::declval<const absl::StatusOr<int>&&>().value())>(), | |
| "value() for const temporaries should return const T&&"); | |
| } | |
| TEST(StatusOr, TestMoveOnlyConversion) { | |
| absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr()); | |
| EXPECT_TRUE(const_thing.ok()); | |
| EXPECT_EQ(0, **const_thing); | |
| // Test rvalue converting assignment | |
| const int* const_previous = const_thing->get(); | |
| const_thing = ReturnUniquePtr(); | |
| EXPECT_TRUE(const_thing.ok()); | |
| EXPECT_EQ(0, **const_thing); | |
| EXPECT_NE(const_previous, const_thing->get()); | |
| } | |
| TEST(StatusOr, TestMoveOnlyVector) { | |
| // Sanity check that absl::StatusOr<MoveOnly> works in vector. | |
| std::vector<absl::StatusOr<std::unique_ptr<int>>> vec; | |
| vec.push_back(ReturnUniquePtr()); | |
| vec.resize(2); | |
| auto another_vec = std::move(vec); | |
| EXPECT_EQ(0, **another_vec[0]); | |
| EXPECT_EQ(absl::UnknownError(""), another_vec[1].status()); | |
| } | |
| TEST(StatusOr, TestDefaultCtor) { | |
| absl::StatusOr<int> thing; | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); | |
| } | |
| TEST(StatusOr, StatusCtorForwards) { | |
| absl::Status status(absl::StatusCode::kInternal, "Some error"); | |
| EXPECT_EQ(absl::StatusOr<int>(status).status().message(), "Some error"); | |
| EXPECT_EQ(status.message(), "Some error"); | |
| EXPECT_EQ(absl::StatusOr<int>(std::move(status)).status().message(), | |
| "Some error"); | |
| EXPECT_NE(status.message(), "Some error"); | |
| } | |
| TEST(BadStatusOrAccessTest, CopyConstructionWhatOk) { | |
| absl::Status error = | |
| absl::InternalError("some arbitrary message too big for the sso buffer"); | |
| absl::BadStatusOrAccess e1{error}; | |
| absl::BadStatusOrAccess e2{e1}; | |
| EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); | |
| EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); | |
| } | |
| TEST(BadStatusOrAccessTest, CopyAssignmentWhatOk) { | |
| absl::Status error = | |
| absl::InternalError("some arbitrary message too big for the sso buffer"); | |
| absl::BadStatusOrAccess e1{error}; | |
| absl::BadStatusOrAccess e2{absl::InternalError("other")}; | |
| e2 = e1; | |
| EXPECT_THAT(e1.what(), HasSubstr(error.ToString())); | |
| EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); | |
| } | |
| TEST(BadStatusOrAccessTest, MoveConstructionWhatOk) { | |
| absl::Status error = | |
| absl::InternalError("some arbitrary message too big for the sso buffer"); | |
| absl::BadStatusOrAccess e1{error}; | |
| absl::BadStatusOrAccess e2{std::move(e1)}; | |
| EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); | |
| } | |
| TEST(BadStatusOrAccessTest, MoveAssignmentWhatOk) { | |
| absl::Status error = | |
| absl::InternalError("some arbitrary message too big for the sso buffer"); | |
| absl::BadStatusOrAccess e1{error}; | |
| absl::BadStatusOrAccess e2{absl::InternalError("other")}; | |
| e2 = std::move(e1); | |
| EXPECT_THAT(e2.what(), HasSubstr(error.ToString())); | |
| } | |
| // Define `EXPECT_DEATH_OR_THROW` to test the behavior of `StatusOr::value`, | |
| // which either throws `BadStatusOrAccess` or `LOG(FATAL)` based on whether | |
| // exceptions are enabled. | |
| #ifdef ABSL_HAVE_EXCEPTIONS | |
| #define EXPECT_DEATH_OR_THROW(statement, status_) \ | |
| EXPECT_THROW( \ | |
| { \ | |
| try { \ | |
| statement; \ | |
| } catch (const absl::BadStatusOrAccess& e) { \ | |
| EXPECT_EQ(e.status(), status_); \ | |
| EXPECT_THAT(e.what(), HasSubstr(e.status().ToString())); \ | |
| throw; \ | |
| } \ | |
| }, \ | |
| absl::BadStatusOrAccess); | |
| #else // ABSL_HAVE_EXCEPTIONS | |
| #define EXPECT_DEATH_OR_THROW(statement, status) \ | |
| EXPECT_DEATH_IF_SUPPORTED(statement, status.ToString()); | |
| #endif // ABSL_HAVE_EXCEPTIONS | |
| TEST(StatusOrDeathTest, TestDefaultCtorValue) { | |
| absl::StatusOr<int> thing; | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); | |
| const absl::StatusOr<int> thing2; | |
| EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError("")); | |
| } | |
| TEST(StatusOrDeathTest, TestValueNotOk) { | |
| absl::StatusOr<int> thing(absl::CancelledError()); | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); | |
| } | |
| TEST(StatusOrDeathTest, TestValueNotOkConst) { | |
| const absl::StatusOr<int> thing(absl::UnknownError("")); | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); | |
| } | |
| TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { | |
| absl::StatusOr<int*> thing; | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); | |
| } | |
| TEST(StatusOrDeathTest, TestPointerValueNotOk) { | |
| absl::StatusOr<int*> thing(absl::CancelledError()); | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); | |
| } | |
| TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { | |
| const absl::StatusOr<int*> thing(absl::CancelledError()); | |
| EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); | |
| } | |
| #if GTEST_HAS_DEATH_TEST | |
| TEST(StatusOrDeathTest, TestStatusCtorStatusOk) { | |
| EXPECT_DEBUG_DEATH( | |
| { | |
| // This will DCHECK | |
| absl::StatusOr<int> thing(absl::OkStatus()); | |
| // In optimized mode, we are actually going to get error::INTERNAL for | |
| // status here, rather than crashing, so check that. | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); | |
| }, | |
| "An OK status is not a valid constructor argument"); | |
| } | |
| TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) { | |
| EXPECT_DEBUG_DEATH( | |
| { | |
| absl::StatusOr<int*> thing(absl::OkStatus()); | |
| // In optimized mode, we are actually going to get error::INTERNAL for | |
| // status here, rather than crashing, so check that. | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); | |
| }, | |
| "An OK status is not a valid constructor argument"); | |
| } | |
| #endif | |
| TEST(StatusOr, ValueAccessor) { | |
| const int kIntValue = 110; | |
| { | |
| absl::StatusOr<int> status_or(kIntValue); | |
| EXPECT_EQ(kIntValue, status_or.value()); | |
| EXPECT_EQ(kIntValue, std::move(status_or).value()); | |
| } | |
| { | |
| absl::StatusOr<CopyDetector> status_or(kIntValue); | |
| EXPECT_THAT(status_or, | |
| IsOkAndHolds(CopyDetectorHas(kIntValue, false, false))); | |
| CopyDetector copy_detector = status_or.value(); | |
| EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true)); | |
| copy_detector = std::move(status_or).value(); | |
| EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false)); | |
| } | |
| } | |
| TEST(StatusOr, BadValueAccess) { | |
| const absl::Status kError = absl::CancelledError("message"); | |
| absl::StatusOr<int> status_or(kError); | |
| EXPECT_DEATH_OR_THROW(status_or.value(), kError); | |
| } | |
| TEST(StatusOr, TestStatusCtor) { | |
| absl::StatusOr<int> thing(absl::CancelledError()); | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestValueCtor) { | |
| const int kI = 4; | |
| const absl::StatusOr<int> thing(kI); | |
| EXPECT_TRUE(thing.ok()); | |
| EXPECT_EQ(kI, *thing); | |
| } | |
| struct Foo { | |
| const int x; | |
| explicit Foo(int y) : x(y) {} | |
| }; | |
| TEST(StatusOr, InPlaceConstruction) { | |
| EXPECT_THAT(absl::StatusOr<Foo>(std::in_place, 10), | |
| IsOkAndHolds(Field(&Foo::x, 10))); | |
| } | |
| struct InPlaceHelper { | |
| InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy) | |
| : x(xs), y(std::move(yy)) {} | |
| const std::vector<int> x; | |
| std::unique_ptr<int> y; | |
| }; | |
| TEST(StatusOr, InPlaceInitListConstruction) { | |
| absl::StatusOr<InPlaceHelper> status_or(std::in_place, {10, 11, 12}, | |
| std::make_unique<int>(13)); | |
| EXPECT_THAT(status_or, IsOkAndHolds(AllOf( | |
| Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)), | |
| Field(&InPlaceHelper::y, Pointee(13))))); | |
| } | |
| TEST(StatusOr, Emplace) { | |
| absl::StatusOr<Foo> status_or_foo(10); | |
| status_or_foo.emplace(20); | |
| EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); | |
| status_or_foo = absl::InvalidArgumentError("msg"); | |
| EXPECT_FALSE(status_or_foo.ok()); | |
| EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument); | |
| EXPECT_EQ(status_or_foo.status().message(), "msg"); | |
| status_or_foo.emplace(20); | |
| EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); | |
| } | |
| TEST(StatusOr, EmplaceInitializerList) { | |
| absl::StatusOr<InPlaceHelper> status_or(std::in_place, {10, 11, 12}, | |
| std::make_unique<int>(13)); | |
| status_or.emplace({1, 2, 3}, std::make_unique<int>(4)); | |
| EXPECT_THAT(status_or, | |
| IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), | |
| Field(&InPlaceHelper::y, Pointee(4))))); | |
| status_or = absl::InvalidArgumentError("msg"); | |
| EXPECT_FALSE(status_or.ok()); | |
| EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument); | |
| EXPECT_EQ(status_or.status().message(), "msg"); | |
| status_or.emplace({1, 2, 3}, std::make_unique<int>(4)); | |
| EXPECT_THAT(status_or, | |
| IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), | |
| Field(&InPlaceHelper::y, Pointee(4))))); | |
| } | |
| #ifdef ABSL_HAVE_EXCEPTIONS | |
| class ThrowOnEmplace { | |
| public: | |
| explicit ThrowOnEmplace(int* counter, int val) : destructor_calls_(counter) { | |
| if (val < 0) { | |
| throw std::runtime_error("expected"); | |
| } | |
| // While destructor_calls tracks the logic, ptr_ ensures that a double | |
| // destruction actually results in a reliable crash. Performing a real heap | |
| // allocation and deallocation (new/delete) guarantees that AddressSanitizer | |
| // (ASAN) or the heap allocator will instantly catch the double-free if the | |
| // bug regresses, rather than relying solely on the integer check. | |
| ptr_ = new int(val); | |
| } | |
| ThrowOnEmplace(const ThrowOnEmplace&) = delete; | |
| ThrowOnEmplace& operator=(const ThrowOnEmplace&) = delete; | |
| ~ThrowOnEmplace() { | |
| if (destructor_calls_) { | |
| ++(*destructor_calls_); | |
| } | |
| delete ptr_; | |
| } | |
| private: | |
| int* destructor_calls_ = nullptr; | |
| int* ptr_ = nullptr; | |
| }; | |
| TEST(StatusOr, EmplaceThrowsExceptionSafety) { | |
| int destructor_calls = 0; | |
| { | |
| absl::StatusOr<ThrowOnEmplace> status_or(std::in_place, &destructor_calls, | |
| 1); | |
| EXPECT_TRUE(status_or.ok()); | |
| EXPECT_THROW(status_or.emplace(&destructor_calls, -1), std::runtime_error); | |
| EXPECT_FALSE(status_or.ok()); | |
| EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInternal); | |
| } | |
| // Verifies that the initial object is properly destroyed by Clear() (count is | |
| // 1), and that the exception thrown during replacement does not cause a | |
| // second destruction (double-free) during stack unwinding. | |
| EXPECT_EQ(destructor_calls, 1); | |
| } | |
| #endif // ABSL_HAVE_EXCEPTIONS | |
| TEST(StatusOr, TestCopyCtorStatusOk) { | |
| const int kI = 4; | |
| const absl::StatusOr<int> original(kI); | |
| const absl::StatusOr<int> copy(original); | |
| EXPECT_THAT(copy.status(), IsOk()); | |
| EXPECT_EQ(*original, *copy); | |
| } | |
| TEST(StatusOr, TestCopyCtorStatusNotOk) { | |
| absl::StatusOr<int> original(absl::CancelledError()); | |
| absl::StatusOr<int> copy(original); | |
| EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestCopyCtorNonAssignable) { | |
| const int kI = 4; | |
| CopyNoAssign value(kI); | |
| absl::StatusOr<CopyNoAssign> original(value); | |
| absl::StatusOr<CopyNoAssign> copy(original); | |
| EXPECT_THAT(copy.status(), IsOk()); | |
| EXPECT_EQ(original->foo, copy->foo); | |
| } | |
| TEST(StatusOr, TestCopyCtorStatusOKConverting) { | |
| const int kI = 4; | |
| absl::StatusOr<int> original(kI); | |
| absl::StatusOr<double> copy(original); | |
| EXPECT_THAT(copy.status(), IsOk()); | |
| EXPECT_DOUBLE_EQ(*original, *copy); | |
| } | |
| TEST(StatusOr, TestCopyCtorStatusNotOkConverting) { | |
| absl::StatusOr<int> original(absl::CancelledError()); | |
| absl::StatusOr<double> copy(original); | |
| EXPECT_EQ(copy.status(), original.status()); | |
| } | |
| TEST(StatusOr, TestAssignmentStatusOk) { | |
| // Copy assignmment | |
| { | |
| const auto p = std::make_shared<int>(17); | |
| absl::StatusOr<std::shared_ptr<int>> source(p); | |
| absl::StatusOr<std::shared_ptr<int>> target; | |
| target = source; | |
| ASSERT_TRUE(target.ok()); | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_EQ(p, *target); | |
| ASSERT_TRUE(source.ok()); | |
| EXPECT_THAT(source.status(), IsOk()); | |
| EXPECT_EQ(p, *source); | |
| } | |
| // Move assignment | |
| { | |
| const auto p = std::make_shared<int>(17); | |
| absl::StatusOr<std::shared_ptr<int>> source(p); | |
| absl::StatusOr<std::shared_ptr<int>> target; | |
| target = std::move(source); | |
| ASSERT_TRUE(target.ok()); | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_EQ(p, *target); | |
| ASSERT_TRUE(source.ok()); | |
| EXPECT_THAT(source.status(), IsOk()); | |
| EXPECT_EQ(nullptr, *source); | |
| } | |
| } | |
| TEST(StatusOr, TestAssignmentStatusNotOk) { | |
| // Copy assignment | |
| { | |
| const absl::Status expected = absl::CancelledError(); | |
| absl::StatusOr<int> source(expected); | |
| absl::StatusOr<int> target; | |
| target = source; | |
| EXPECT_FALSE(target.ok()); | |
| EXPECT_EQ(expected, target.status()); | |
| EXPECT_FALSE(source.ok()); | |
| EXPECT_EQ(expected, source.status()); | |
| } | |
| // Move assignment | |
| { | |
| const absl::Status expected = absl::CancelledError(); | |
| absl::StatusOr<int> source(expected); | |
| absl::StatusOr<int> target; | |
| target = std::move(source); | |
| EXPECT_FALSE(target.ok()); | |
| EXPECT_EQ(expected, target.status()); | |
| EXPECT_FALSE(source.ok()); | |
| EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); | |
| } | |
| } | |
| TEST(StatusOr, TestAssignmentStatusOKConverting) { | |
| // Copy assignment | |
| { | |
| const int kI = 4; | |
| absl::StatusOr<int> source(kI); | |
| absl::StatusOr<double> target; | |
| target = source; | |
| ASSERT_TRUE(target.ok()); | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_DOUBLE_EQ(kI, *target); | |
| ASSERT_TRUE(source.ok()); | |
| EXPECT_THAT(source.status(), IsOk()); | |
| EXPECT_DOUBLE_EQ(kI, *source); | |
| } | |
| // Move assignment | |
| { | |
| const auto p = new int(17); | |
| absl::StatusOr<std::unique_ptr<int>> source(absl::WrapUnique(p)); | |
| absl::StatusOr<std::shared_ptr<int>> target; | |
| target = std::move(source); | |
| ASSERT_TRUE(target.ok()); | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_EQ(p, target->get()); | |
| ASSERT_TRUE(source.ok()); | |
| EXPECT_THAT(source.status(), IsOk()); | |
| EXPECT_EQ(nullptr, source->get()); | |
| } | |
| } | |
| struct A { | |
| int x; | |
| }; | |
| struct ImplicitConstructibleFromA { | |
| int x; | |
| bool moved; | |
| ImplicitConstructibleFromA(const A& a) // NOLINT | |
| : x(a.x), moved(false) {} | |
| ImplicitConstructibleFromA(A&& a) // NOLINT | |
| : x(a.x), moved(true) {} | |
| }; | |
| TEST(StatusOr, ImplicitConvertingConstructor) { | |
| EXPECT_THAT( | |
| absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>( | |
| absl::StatusOr<A>(A{11})), | |
| IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11), | |
| Field(&ImplicitConstructibleFromA::moved, true)))); | |
| absl::StatusOr<A> a(A{12}); | |
| EXPECT_THAT( | |
| absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a), | |
| IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12), | |
| Field(&ImplicitConstructibleFromA::moved, false)))); | |
| } | |
| struct ExplicitConstructibleFromA { | |
| int x; | |
| bool moved; | |
| explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {} | |
| explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {} | |
| }; | |
| TEST(StatusOr, ExplicitConvertingConstructor) { | |
| EXPECT_FALSE( | |
| (std::is_convertible_v<const absl::StatusOr<A>&, | |
| absl::StatusOr<ExplicitConstructibleFromA>>)); | |
| EXPECT_FALSE( | |
| (std::is_convertible_v<absl::StatusOr<A>&&, | |
| absl::StatusOr<ExplicitConstructibleFromA>>)); | |
| EXPECT_THAT( | |
| absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})), | |
| IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11), | |
| Field(&ExplicitConstructibleFromA::moved, true)))); | |
| absl::StatusOr<A> a(A{12}); | |
| EXPECT_THAT( | |
| absl::StatusOr<ExplicitConstructibleFromA>(a), | |
| IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12), | |
| Field(&ExplicitConstructibleFromA::moved, false)))); | |
| } | |
| struct ImplicitConstructibleFromBool { | |
| ImplicitConstructibleFromBool(bool y) : x(y) {} // NOLINT | |
| bool x = false; | |
| }; | |
| struct ConvertibleToBool { | |
| explicit ConvertibleToBool(bool y) : x(y) {} | |
| operator bool() const { return x; } // NOLINT | |
| bool x = false; | |
| }; | |
| TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) { | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), | |
| IsOkAndHolds(false)); | |
| EXPECT_THAT( | |
| absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>( | |
| absl::StatusOr<bool>(false)), | |
| IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); | |
| EXPECT_FALSE( | |
| (std::is_convertible_v<absl::StatusOr<ConvertibleToBool>, | |
| absl::StatusOr<ImplicitConstructibleFromBool>>)); | |
| } | |
| TEST(StatusOr, BooleanConstructionWithImplicitCasts) { | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), | |
| IsOkAndHolds(false)); | |
| EXPECT_THAT( | |
| absl::StatusOr<ImplicitConstructibleFromBool>{ | |
| absl::StatusOr<bool>(false)}, | |
| IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); | |
| EXPECT_THAT( | |
| absl::StatusOr<ImplicitConstructibleFromBool>{ | |
| absl::StatusOr<bool>(absl::InvalidArgumentError(""))}, | |
| Not(IsOk())); | |
| EXPECT_THAT( | |
| absl::StatusOr<ImplicitConstructibleFromBool>{ | |
| absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})}, | |
| IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); | |
| EXPECT_THAT( | |
| absl::StatusOr<ImplicitConstructibleFromBool>{ | |
| absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))}, | |
| Not(IsOk())); | |
| } | |
| TEST(StatusOr, ConstImplicitCast) { | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( | |
| absl::StatusOr<const bool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( | |
| absl::StatusOr<const bool>(false)), | |
| IsOkAndHolds(false)); | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( | |
| absl::StatusOr<bool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( | |
| absl::StatusOr<bool>(false)), | |
| IsOkAndHolds(false)); | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>( | |
| absl::StatusOr<std::string>("foo")), | |
| IsOkAndHolds("foo")); | |
| EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>( | |
| absl::StatusOr<const std::string>("foo")), | |
| IsOkAndHolds("foo")); | |
| EXPECT_THAT( | |
| absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>( | |
| absl::StatusOr<std::shared_ptr<std::string>>( | |
| std::make_shared<std::string>("foo"))), | |
| IsOkAndHolds(Pointee(std::string("foo")))); | |
| } | |
| TEST(StatusOr, ConstExplicitConstruction) { | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)), | |
| IsOkAndHolds(false)); | |
| EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)), | |
| IsOkAndHolds(true)); | |
| EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)), | |
| IsOkAndHolds(false)); | |
| } | |
| struct ExplicitConstructibleFromInt { | |
| int x; | |
| explicit ExplicitConstructibleFromInt(int y) : x(y) {} | |
| }; | |
| TEST(StatusOr, ExplicitConstruction) { | |
| EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10), | |
| IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10))); | |
| } | |
| TEST(StatusOr, ImplicitConstruction) { | |
| // Check implicit casting works. | |
| auto status_or = | |
| absl::implicit_cast<absl::StatusOr<std::variant<int, std::string>>>(10); | |
| EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); | |
| } | |
| TEST(StatusOr, ImplicitConstructionFromInitliazerList) { | |
| // Note: dropping the explicit std::initializer_list<int> is not supported | |
| // by absl::StatusOr or std::optional. | |
| auto status_or = | |
| absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}}); | |
| EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); | |
| } | |
| TEST(StatusOr, UniquePtrImplicitConstruction) { | |
| auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>( | |
| std::make_unique<Derived>()); | |
| EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); | |
| } | |
| TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) { | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = | |
| absl::InvalidArgumentError("foo"); | |
| EXPECT_THAT(status_or, | |
| IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or; | |
| EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error; | |
| EXPECT_THAT(a_err, Not(IsOk())); | |
| const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref; // NOLINT | |
| EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); | |
| const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err; // NOLINT | |
| EXPECT_THAT(b_err, Not(IsOk())); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or); | |
| EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error); | |
| EXPECT_THAT(c_err, Not(IsOk())); | |
| } | |
| TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) { | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = | |
| absl::InvalidArgumentError("foo"); | |
| absl::StatusOr<absl::StatusOr<CopyDetector>> a; | |
| a = status_or; | |
| EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); | |
| a = status_error; | |
| EXPECT_THAT(a, Not(IsOk())); | |
| const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; | |
| a = cref; | |
| EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); | |
| const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; | |
| a = cref_err; | |
| EXPECT_THAT(a, Not(IsOk())); | |
| a = std::move(status_or); | |
| EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); | |
| a = std::move(status_error); | |
| EXPECT_THAT(a, Not(IsOk())); | |
| } | |
| struct Copyable { | |
| Copyable() = default; | |
| Copyable(const Copyable&) = default; | |
| Copyable& operator=(const Copyable&) = default; | |
| }; | |
| struct MoveOnly { | |
| MoveOnly() = default; | |
| MoveOnly(MoveOnly&&) {} | |
| MoveOnly& operator=(MoveOnly&&) { return *this; } | |
| }; | |
| struct NonMovable { | |
| NonMovable() = default; | |
| NonMovable(const NonMovable&) = delete; | |
| NonMovable(NonMovable&&) = delete; | |
| NonMovable& operator=(const NonMovable&) = delete; | |
| NonMovable& operator=(NonMovable&&) = delete; | |
| }; | |
| TEST(StatusOr, CopyAndMoveAbility) { | |
| EXPECT_TRUE(std::is_copy_constructible_v<Copyable>); | |
| EXPECT_TRUE(std::is_copy_assignable_v<Copyable>); | |
| EXPECT_TRUE(std::is_move_constructible_v<Copyable>); | |
| EXPECT_TRUE(std::is_move_assignable_v<Copyable>); | |
| EXPECT_FALSE(std::is_copy_constructible_v<MoveOnly>); | |
| EXPECT_FALSE(std::is_copy_assignable_v<MoveOnly>); | |
| EXPECT_TRUE(std::is_move_constructible_v<MoveOnly>); | |
| EXPECT_TRUE(std::is_move_assignable_v<MoveOnly>); | |
| EXPECT_FALSE(std::is_copy_constructible_v<NonMovable>); | |
| EXPECT_FALSE(std::is_copy_assignable_v<NonMovable>); | |
| EXPECT_FALSE(std::is_move_constructible_v<NonMovable>); | |
| EXPECT_FALSE(std::is_move_assignable_v<NonMovable>); | |
| } | |
| TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) { | |
| absl::StatusOr<std::any> status_or = CopyDetector(10); | |
| absl::StatusOr<std::any> status_error = absl::InvalidArgumentError("foo"); | |
| EXPECT_THAT( | |
| status_or, | |
| IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); | |
| absl::StatusOr<std::any> a = status_or; | |
| EXPECT_THAT( | |
| a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); | |
| absl::StatusOr<std::any> a_err = status_error; | |
| EXPECT_THAT(a_err, Not(IsOk())); | |
| const absl::StatusOr<std::any>& cref = status_or; | |
| // No lint for no-change copy. | |
| absl::StatusOr<std::any> b = cref; // NOLINT | |
| EXPECT_THAT( | |
| b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); | |
| const absl::StatusOr<std::any>& cref_err = status_error; | |
| // No lint for no-change copy. | |
| absl::StatusOr<std::any> b_err = cref_err; // NOLINT | |
| EXPECT_THAT(b_err, Not(IsOk())); | |
| absl::StatusOr<std::any> c = std::move(status_or); | |
| EXPECT_THAT( | |
| c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); | |
| absl::StatusOr<std::any> c_err = std::move(status_error); | |
| EXPECT_THAT(c_err, Not(IsOk())); | |
| } | |
| TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) { | |
| absl::StatusOr<std::any> status_or = CopyDetector(10); | |
| absl::StatusOr<std::any> status_error = absl::InvalidArgumentError("foo"); | |
| absl::StatusOr<std::any> a; | |
| a = status_or; | |
| EXPECT_THAT( | |
| a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); | |
| a = status_error; | |
| EXPECT_THAT(a, Not(IsOk())); | |
| const absl::StatusOr<std::any>& cref = status_or; | |
| a = cref; | |
| EXPECT_THAT( | |
| a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); | |
| const absl::StatusOr<std::any>& cref_err = status_error; | |
| a = cref_err; | |
| EXPECT_THAT(a, Not(IsOk())); | |
| a = std::move(status_or); | |
| EXPECT_THAT( | |
| a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); | |
| a = std::move(status_error); | |
| EXPECT_THAT(a, Not(IsOk())); | |
| } | |
| TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) { | |
| absl::StatusOr<CopyDetector> status_or(10); | |
| ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); | |
| absl::StatusOr<CopyDetector> a(status_or); | |
| EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); | |
| const absl::StatusOr<CopyDetector>& cref = status_or; | |
| absl::StatusOr<CopyDetector> b(cref); // NOLINT | |
| EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); | |
| absl::StatusOr<CopyDetector> c(std::move(status_or)); | |
| EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); | |
| } | |
| TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) { | |
| absl::StatusOr<CopyDetector> status_or(10); | |
| ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); | |
| absl::StatusOr<CopyDetector> a; | |
| a = status_or; | |
| EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); | |
| const absl::StatusOr<CopyDetector>& cref = status_or; | |
| absl::StatusOr<CopyDetector> b; | |
| b = cref; | |
| EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); | |
| absl::StatusOr<CopyDetector> c; | |
| c = std::move(status_or); | |
| EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); | |
| } | |
| TEST(StatusOr, AbslAnyAssignment) { | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<absl::StatusOr<std::any>, absl::StatusOr<int>>)); | |
| absl::StatusOr<std::any> status_or; | |
| status_or = absl::InvalidArgumentError("foo"); | |
| EXPECT_THAT(status_or, Not(IsOk())); | |
| } | |
| TEST(StatusOr, ImplicitAssignment) { | |
| absl::StatusOr<std::variant<int, std::string>> status_or; | |
| status_or = 10; | |
| EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); | |
| } | |
| TEST(StatusOr, SelfDirectInitAssignment) { | |
| absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; | |
| status_or = *status_or; | |
| EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); | |
| } | |
| TEST(StatusOr, ImplicitCastFromInitializerList) { | |
| absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; | |
| EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); | |
| } | |
| TEST(StatusOr, UniquePtrImplicitAssignment) { | |
| absl::StatusOr<std::unique_ptr<Base1>> status_or; | |
| status_or = std::make_unique<Derived>(); | |
| EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); | |
| } | |
| TEST(StatusOr, Pointer) { | |
| struct A {}; | |
| struct B : public A {}; | |
| struct C : private A {}; | |
| EXPECT_TRUE((std::is_constructible_v<absl::StatusOr<A*>, B*>)); | |
| EXPECT_TRUE((std::is_convertible_v<B*, absl::StatusOr<A*>>)); | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<A*>, C*>)); | |
| EXPECT_FALSE((std::is_convertible_v<C*, absl::StatusOr<A*>>)); | |
| } | |
| TEST(StatusOr, TestAssignmentStatusNotOkConverting) { | |
| // Copy assignment | |
| { | |
| const absl::Status expected = absl::CancelledError(); | |
| absl::StatusOr<int> source(expected); | |
| absl::StatusOr<double> target; | |
| target = source; | |
| EXPECT_FALSE(target.ok()); | |
| EXPECT_EQ(expected, target.status()); | |
| EXPECT_FALSE(source.ok()); | |
| EXPECT_EQ(expected, source.status()); | |
| } | |
| // Move assignment | |
| { | |
| const absl::Status expected = absl::CancelledError(); | |
| absl::StatusOr<int> source(expected); | |
| absl::StatusOr<double> target; | |
| target = std::move(source); | |
| EXPECT_FALSE(target.ok()); | |
| EXPECT_EQ(expected, target.status()); | |
| EXPECT_FALSE(source.ok()); | |
| EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); | |
| } | |
| } | |
| TEST(StatusOr, SelfAssignment) { | |
| // Copy-assignment, status OK | |
| { | |
| // A string long enough that it's likely to defeat any inline representation | |
| // optimization. | |
| const std::string long_str(128, 'a'); | |
| absl::StatusOr<std::string> so = long_str; | |
| so = *&so; | |
| ASSERT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(long_str, *so); | |
| } | |
| // Copy-assignment, error status | |
| { | |
| absl::StatusOr<int> so = absl::NotFoundError("taco"); | |
| so = *&so; | |
| EXPECT_FALSE(so.ok()); | |
| EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); | |
| EXPECT_EQ(so.status().message(), "taco"); | |
| } | |
| // Move-assignment with copyable type, status OK | |
| { | |
| absl::StatusOr<int> so = 17; | |
| // Fool the compiler, which otherwise complains. | |
| auto& same = so; | |
| so = std::move(same); | |
| ASSERT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(17, *so); | |
| } | |
| // Move-assignment with copyable type, error status | |
| { | |
| absl::StatusOr<int> so = absl::NotFoundError("taco"); | |
| // Fool the compiler, which otherwise complains. | |
| auto& same = so; | |
| so = std::move(same); | |
| EXPECT_FALSE(so.ok()); | |
| EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); | |
| EXPECT_EQ(so.status().message(), "taco"); | |
| } | |
| // Move-assignment with non-copyable type, status OK | |
| { | |
| const auto raw = new int(17); | |
| absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw); | |
| // Fool the compiler, which otherwise complains. | |
| auto& same = so; | |
| so = std::move(same); | |
| ASSERT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(raw, so->get()); | |
| } | |
| // Move-assignment with non-copyable type, error status | |
| { | |
| absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco"); | |
| // Fool the compiler, which otherwise complains. | |
| auto& same = so; | |
| so = std::move(same); | |
| EXPECT_FALSE(so.ok()); | |
| EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); | |
| EXPECT_EQ(so.status().message(), "taco"); | |
| } | |
| } | |
| // These types form the overload sets of the constructors and the assignment | |
| // operators of `MockValue`. They distinguish construction from assignment, | |
| // lvalue from rvalue. | |
| struct FromConstructibleAssignableLvalue {}; | |
| struct FromConstructibleAssignableRvalue {}; | |
| struct FromImplicitConstructibleOnly {}; | |
| struct FromAssignableOnly {}; | |
| // This class is for testing the forwarding value assignments of `StatusOr`. | |
| // `from_rvalue` indicates whether the constructor or the assignment taking | |
| // rvalue reference is called. `from_assignment` indicates whether any | |
| // assignment is called. | |
| struct MockValue { | |
| // Constructs `MockValue` from `FromConstructibleAssignableLvalue`. | |
| MockValue(const FromConstructibleAssignableLvalue&) // NOLINT | |
| : from_rvalue(false), assigned(false) {} | |
| // Constructs `MockValue` from `FromConstructibleAssignableRvalue`. | |
| MockValue(FromConstructibleAssignableRvalue&&) // NOLINT | |
| : from_rvalue(true), assigned(false) {} | |
| // Constructs `MockValue` from `FromImplicitConstructibleOnly`. | |
| // `MockValue` is not assignable from `FromImplicitConstructibleOnly`. | |
| MockValue(const FromImplicitConstructibleOnly&) // NOLINT | |
| : from_rvalue(false), assigned(false) {} | |
| // Assigns `FromConstructibleAssignableLvalue`. | |
| MockValue& operator=(const FromConstructibleAssignableLvalue&) { | |
| from_rvalue = false; | |
| assigned = true; | |
| return *this; | |
| } | |
| // Assigns `FromConstructibleAssignableRvalue` (rvalue only). | |
| MockValue& operator=(FromConstructibleAssignableRvalue&&) { | |
| from_rvalue = true; | |
| assigned = true; | |
| return *this; | |
| } | |
| // Assigns `FromAssignableOnly`, but not constructible from | |
| // `FromAssignableOnly`. | |
| MockValue& operator=(const FromAssignableOnly&) { | |
| from_rvalue = false; | |
| assigned = true; | |
| return *this; | |
| } | |
| bool from_rvalue; | |
| bool assigned; | |
| }; | |
| // operator=(U&&) | |
| TEST(StatusOr, PerfectForwardingAssignment) { | |
| // U == T | |
| constexpr int kValue1 = 10, kValue2 = 20; | |
| absl::StatusOr<CopyDetector> status_or; | |
| CopyDetector lvalue(kValue1); | |
| status_or = lvalue; | |
| EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true))); | |
| status_or = CopyDetector(kValue2); | |
| EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false))); | |
| // U != T | |
| EXPECT_TRUE((std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| const FromConstructibleAssignableLvalue&>)); | |
| EXPECT_TRUE((std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| FromConstructibleAssignableLvalue&&>)); | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| const FromConstructibleAssignableRvalue&>)); | |
| EXPECT_TRUE((std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| FromConstructibleAssignableRvalue&&>)); | |
| EXPECT_TRUE((std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| const FromImplicitConstructibleOnly&>)); | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<MockValue>&, | |
| const FromAssignableOnly&>)); | |
| absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{}); | |
| EXPECT_FALSE(from_lvalue->from_rvalue); | |
| EXPECT_FALSE(from_lvalue->assigned); | |
| from_lvalue = FromConstructibleAssignableLvalue{}; | |
| EXPECT_FALSE(from_lvalue->from_rvalue); | |
| EXPECT_TRUE(from_lvalue->assigned); | |
| absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{}); | |
| EXPECT_TRUE(from_rvalue->from_rvalue); | |
| EXPECT_FALSE(from_rvalue->assigned); | |
| from_rvalue = FromConstructibleAssignableRvalue{}; | |
| EXPECT_TRUE(from_rvalue->from_rvalue); | |
| EXPECT_TRUE(from_rvalue->assigned); | |
| absl::StatusOr<MockValue> from_implicit_constructible( | |
| FromImplicitConstructibleOnly{}); | |
| EXPECT_FALSE(from_implicit_constructible->from_rvalue); | |
| EXPECT_FALSE(from_implicit_constructible->assigned); | |
| // construct a temporary `StatusOr` object and invoke the `StatusOr` move | |
| // assignment operator. | |
| from_implicit_constructible = FromImplicitConstructibleOnly{}; | |
| EXPECT_FALSE(from_implicit_constructible->from_rvalue); | |
| EXPECT_FALSE(from_implicit_constructible->assigned); | |
| } | |
| TEST(StatusOr, TestStatus) { | |
| absl::StatusOr<int> good(4); | |
| EXPECT_TRUE(good.ok()); | |
| absl::StatusOr<int> bad(absl::CancelledError()); | |
| EXPECT_FALSE(bad.ok()); | |
| EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, OperatorStarRefQualifiers) { | |
| static_assert( | |
| std::is_same<const int&, | |
| decltype(*std::declval<const absl::StatusOr<int>&>())>(), | |
| "Unexpected ref-qualifiers"); | |
| static_assert( | |
| std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(), | |
| "Unexpected ref-qualifiers"); | |
| static_assert( | |
| std::is_same<const int&&, | |
| decltype(*std::declval<const absl::StatusOr<int>&&>())>(), | |
| "Unexpected ref-qualifiers"); | |
| static_assert( | |
| std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(), | |
| "Unexpected ref-qualifiers"); | |
| } | |
| TEST(StatusOr, OperatorStar) { | |
| const absl::StatusOr<std::string> const_lvalue("hello"); | |
| EXPECT_EQ("hello", *const_lvalue); | |
| absl::StatusOr<std::string> lvalue("hello"); | |
| EXPECT_EQ("hello", *lvalue); | |
| // Note: Recall that std::move() is equivalent to a static_cast to an rvalue | |
| // reference type. | |
| const absl::StatusOr<std::string> const_rvalue("hello"); | |
| EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT | |
| absl::StatusOr<std::string> rvalue("hello"); | |
| EXPECT_EQ("hello", *std::move(rvalue)); | |
| } | |
| TEST(StatusOr, OperatorArrowQualifiers) { | |
| static_assert( | |
| std::is_same< | |
| const int*, | |
| decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(), | |
| "Unexpected qualifiers"); | |
| static_assert( | |
| std::is_same< | |
| int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(), | |
| "Unexpected qualifiers"); | |
| static_assert( | |
| std::is_same< | |
| const int*, | |
| decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(), | |
| "Unexpected qualifiers"); | |
| static_assert( | |
| std::is_same< | |
| int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(), | |
| "Unexpected qualifiers"); | |
| } | |
| TEST(StatusOr, OperatorArrow) { | |
| const absl::StatusOr<std::string> const_lvalue("hello"); | |
| EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); | |
| absl::StatusOr<std::string> lvalue("hello"); | |
| EXPECT_EQ(std::string("hello"), lvalue->c_str()); | |
| } | |
| TEST(StatusOr, RValueStatus) { | |
| absl::StatusOr<int> so(absl::NotFoundError("taco")); | |
| const absl::Status s = std::move(so).status(); | |
| EXPECT_EQ(s.code(), absl::StatusCode::kNotFound); | |
| EXPECT_EQ(s.message(), "taco"); | |
| // Check that !ok() still implies !status().ok(), even after moving out of the | |
| // object. See the note on the rvalue ref-qualified status method. | |
| EXPECT_FALSE(so.ok()); // NOLINT | |
| EXPECT_FALSE(so.status().ok()); | |
| EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal); | |
| EXPECT_EQ(so.status().message(), "Status accessed after move."); | |
| } | |
| TEST(StatusOr, TestValue) { | |
| const int kI = 4; | |
| absl::StatusOr<int> thing(kI); | |
| EXPECT_EQ(kI, *thing); | |
| } | |
| TEST(StatusOr, TestValueConst) { | |
| const int kI = 4; | |
| const absl::StatusOr<int> thing(kI); | |
| EXPECT_EQ(kI, *thing); | |
| } | |
| TEST(StatusOr, TestPointerDefaultCtor) { | |
| absl::StatusOr<int*> thing; | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); | |
| } | |
| TEST(StatusOr, TestPointerStatusCtor) { | |
| absl::StatusOr<int*> thing(absl::CancelledError()); | |
| EXPECT_FALSE(thing.ok()); | |
| EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestPointerValueCtor) { | |
| const int kI = 4; | |
| // Construction from a non-null pointer | |
| { | |
| absl::StatusOr<const int*> so(&kI); | |
| EXPECT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(&kI, *so); | |
| } | |
| // Construction from a null pointer constant | |
| { | |
| absl::StatusOr<const int*> so(nullptr); | |
| EXPECT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(nullptr, *so); | |
| } | |
| // Construction from a non-literal null pointer | |
| { | |
| const int* const p = nullptr; | |
| absl::StatusOr<const int*> so(p); | |
| EXPECT_TRUE(so.ok()); | |
| EXPECT_THAT(so.status(), IsOk()); | |
| EXPECT_EQ(nullptr, *so); | |
| } | |
| } | |
| TEST(StatusOr, TestPointerCopyCtorStatusOk) { | |
| const int kI = 0; | |
| absl::StatusOr<const int*> original(&kI); | |
| absl::StatusOr<const int*> copy(original); | |
| EXPECT_THAT(copy.status(), IsOk()); | |
| EXPECT_EQ(*original, *copy); | |
| } | |
| TEST(StatusOr, TestPointerCopyCtorStatusNotOk) { | |
| absl::StatusOr<int*> original(absl::CancelledError()); | |
| absl::StatusOr<int*> copy(original); | |
| EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) { | |
| Derived derived; | |
| absl::StatusOr<Derived*> original(&derived); | |
| absl::StatusOr<Base2*> copy(original); | |
| EXPECT_THAT(copy.status(), IsOk()); | |
| EXPECT_EQ(static_cast<const Base2*>(*original), *copy); | |
| } | |
| TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) { | |
| absl::StatusOr<Derived*> original(absl::CancelledError()); | |
| absl::StatusOr<Base2*> copy(original); | |
| EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestPointerAssignmentStatusOk) { | |
| const int kI = 0; | |
| absl::StatusOr<const int*> source(&kI); | |
| absl::StatusOr<const int*> target; | |
| target = source; | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_EQ(*source, *target); | |
| } | |
| TEST(StatusOr, TestPointerAssignmentStatusNotOk) { | |
| absl::StatusOr<int*> source(absl::CancelledError()); | |
| absl::StatusOr<int*> target; | |
| target = source; | |
| EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestPointerAssignmentStatusOKConverting) { | |
| Derived derived; | |
| absl::StatusOr<Derived*> source(&derived); | |
| absl::StatusOr<Base2*> target; | |
| target = source; | |
| EXPECT_THAT(target.status(), IsOk()); | |
| EXPECT_EQ(static_cast<const Base2*>(*source), *target); | |
| } | |
| TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) { | |
| absl::StatusOr<Derived*> source(absl::CancelledError()); | |
| absl::StatusOr<Base2*> target; | |
| target = source; | |
| EXPECT_EQ(target.status(), source.status()); | |
| } | |
| TEST(StatusOr, TestPointerStatus) { | |
| const int kI = 0; | |
| absl::StatusOr<const int*> good(&kI); | |
| EXPECT_TRUE(good.ok()); | |
| absl::StatusOr<const int*> bad(absl::CancelledError()); | |
| EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); | |
| } | |
| TEST(StatusOr, TestPointerValue) { | |
| const int kI = 0; | |
| absl::StatusOr<const int*> thing(&kI); | |
| EXPECT_EQ(&kI, *thing); | |
| } | |
| TEST(StatusOr, TestPointerValueConst) { | |
| const int kI = 0; | |
| const absl::StatusOr<const int*> thing(&kI); | |
| EXPECT_EQ(&kI, *thing); | |
| } | |
| TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) { | |
| using EvilType = std::vector<std::unique_ptr<int>>; | |
| static_assert(std::is_copy_constructible_v<EvilType>); | |
| std::vector<::absl::StatusOr<EvilType>> v(5); | |
| v.reserve(v.capacity() + 10); | |
| v.resize(v.capacity() + 10); | |
| } | |
| TEST(StatusOr, ConstPayload) { | |
| // A reduced version of a problematic type found in the wild. All of the | |
| // operations below should compile. | |
| absl::StatusOr<const int> a; | |
| // Copy-construction | |
| absl::StatusOr<const int> b(a); | |
| // Copy-assignment | |
| EXPECT_FALSE(std::is_copy_assignable_v<absl::StatusOr<const int>>); | |
| // Move-construction | |
| absl::StatusOr<const int> c(std::move(a)); | |
| // Move-assignment | |
| EXPECT_FALSE(std::is_move_assignable_v<absl::StatusOr<const int>>); | |
| } | |
| TEST(StatusOr, MapToStatusOrUniquePtr) { | |
| // A reduced version of a problematic type found in the wild. All of the | |
| // operations below should compile. | |
| using MapType = std::map<std::string, absl::StatusOr<std::unique_ptr<int>>>; | |
| MapType a; | |
| // Move-construction | |
| MapType b(std::move(a)); | |
| // Move-assignment | |
| a = std::move(b); | |
| } | |
| TEST(StatusOr, ValueOrOk) { | |
| const absl::StatusOr<int> status_or = 0; | |
| EXPECT_EQ(status_or.value_or(-1), 0); | |
| } | |
| TEST(StatusOr, ValueOrDefault) { | |
| const absl::StatusOr<int> status_or = absl::CancelledError(); | |
| EXPECT_EQ(status_or.value_or(-1), -1); | |
| } | |
| TEST(StatusOr, MoveOnlyValueOrOk) { | |
| EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(std::make_unique<int>(0)) | |
| .value_or(std::make_unique<int>(-1)), | |
| Pointee(0)); | |
| } | |
| TEST(StatusOr, MoveOnlyValueOrDefault) { | |
| EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError()) | |
| .value_or(std::make_unique<int>(-1)), | |
| Pointee(-1)); | |
| } | |
| static absl::StatusOr<int> MakeStatus() { return 100; } | |
| TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); } | |
| TEST(StatusOr, EqualityOperator) { | |
| constexpr size_t kNumCases = 4; | |
| std::array<absl::StatusOr<int>, kNumCases> group1 = { | |
| absl::StatusOr<int>(1), absl::StatusOr<int>(2), | |
| absl::StatusOr<int>(absl::InvalidArgumentError("msg")), | |
| absl::StatusOr<int>(absl::InternalError("msg"))}; | |
| std::array<absl::StatusOr<int>, kNumCases> group2 = { | |
| absl::StatusOr<int>(1), absl::StatusOr<int>(2), | |
| absl::StatusOr<int>(absl::InvalidArgumentError("msg")), | |
| absl::StatusOr<int>(absl::InternalError("msg"))}; | |
| for (size_t i = 0; i < kNumCases; ++i) { | |
| for (size_t j = 0; j < kNumCases; ++j) { | |
| if (i == j) { | |
| EXPECT_TRUE(group1[i] == group2[j]); | |
| EXPECT_FALSE(group1[i] != group2[j]); | |
| } else { | |
| EXPECT_FALSE(group1[i] == group2[j]); | |
| EXPECT_TRUE(group1[i] != group2[j]); | |
| } | |
| } | |
| } | |
| } | |
| struct MyType { | |
| bool operator==(const MyType&) const { return true; } | |
| }; | |
| enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 }; | |
| // This class has conversion operator to `StatusOr<T>` based on value of | |
| // `conv_traits`. | |
| template <typename T, ConvTraits conv_traits = ConvTraits::kNone> | |
| struct StatusOrConversionBase {}; | |
| template <typename T> | |
| struct StatusOrConversionBase<T, ConvTraits::kImplicit> { | |
| operator absl::StatusOr<T>() const& { // NOLINT | |
| return absl::InvalidArgumentError("conversion to absl::StatusOr"); | |
| } | |
| operator absl::StatusOr<T>() && { // NOLINT | |
| return absl::InvalidArgumentError("conversion to absl::StatusOr"); | |
| } | |
| }; | |
| template <typename T> | |
| struct StatusOrConversionBase<T, ConvTraits::kExplicit> { | |
| explicit operator absl::StatusOr<T>() const& { | |
| return absl::InvalidArgumentError("conversion to absl::StatusOr"); | |
| } | |
| explicit operator absl::StatusOr<T>() && { | |
| return absl::InvalidArgumentError("conversion to absl::StatusOr"); | |
| } | |
| }; | |
| // This class has conversion operator to `T` based on the value of | |
| // `conv_traits`. | |
| template <typename T, ConvTraits conv_traits = ConvTraits::kNone> | |
| struct ConversionBase {}; | |
| template <typename T> | |
| struct ConversionBase<T, ConvTraits::kImplicit> { | |
| operator T() const& { return t; } // NOLINT | |
| operator T() && { return std::move(t); } // NOLINT | |
| T t; | |
| }; | |
| template <typename T> | |
| struct ConversionBase<T, ConvTraits::kExplicit> { | |
| explicit operator T() const& { return t; } | |
| explicit operator T() && { return std::move(t); } | |
| T t; | |
| }; | |
| // This class has conversion operator to `absl::Status` based on the value of | |
| // `conv_traits`. | |
| template <ConvTraits conv_traits = ConvTraits::kNone> | |
| struct StatusConversionBase {}; | |
| template <> | |
| struct StatusConversionBase<ConvTraits::kImplicit> { | |
| operator absl::Status() const& { // NOLINT | |
| return absl::InternalError("conversion to Status"); | |
| } | |
| operator absl::Status() && { // NOLINT | |
| return absl::InternalError("conversion to Status"); | |
| } | |
| }; | |
| template <> | |
| struct StatusConversionBase<ConvTraits::kExplicit> { | |
| explicit operator absl::Status() const& { // NOLINT | |
| return absl::InternalError("conversion to Status"); | |
| } | |
| explicit operator absl::Status() && { // NOLINT | |
| return absl::InternalError("conversion to Status"); | |
| } | |
| }; | |
| static constexpr int kConvToStatus = 1; | |
| static constexpr int kConvToStatusOr = 2; | |
| static constexpr int kConvToT = 4; | |
| static constexpr int kConvExplicit = 8; | |
| constexpr ConvTraits GetConvTraits(int bit, int config) { | |
| return (config & bit) == 0 | |
| ? ConvTraits::kNone | |
| : ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit | |
| : ConvTraits::kExplicit); | |
| } | |
| // This class conditionally has conversion operator to `absl::Status`, `T`, | |
| // `StatusOr<T>`, based on values of the template parameters. | |
| template <typename T, int config> | |
| struct CustomType | |
| : StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>, | |
| ConversionBase<T, GetConvTraits(kConvToT, config)>, | |
| StatusConversionBase<GetConvTraits(kConvToStatus, config)> {}; | |
| struct ConvertibleToAnyStatusOr { | |
| template <typename T> | |
| operator absl::StatusOr<T>() const { // NOLINT | |
| return absl::InvalidArgumentError("Conversion to absl::StatusOr"); | |
| } | |
| }; | |
| // Test the rank of overload resolution for `StatusOr<T>` constructor and | |
| // assignment, from highest to lowest: | |
| // 1. T/Status | |
| // 2. U that has conversion operator to absl::StatusOr<T> | |
| // 3. U that is convertible to Status | |
| // 4. U that is convertible to T | |
| TEST(StatusOr, ConstructionFromT) { | |
| // Construct absl::StatusOr<T> from T when T is convertible to | |
| // absl::StatusOr<T> | |
| { | |
| ConvertibleToAnyStatusOr v; | |
| absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v); | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| { | |
| ConvertibleToAnyStatusOr v; | |
| absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| // Construct absl::StatusOr<T> from T when T is explicitly convertible to | |
| // Status | |
| { | |
| CustomType<MyType, kConvToStatus | kConvExplicit> v; | |
| absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor( | |
| v); | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatus | kConvExplicit> v; | |
| absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor = | |
| v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| } | |
| // Construct absl::StatusOr<T> from U when U is explicitly convertible to T | |
| TEST(StatusOr, ConstructionFromTypeConvertibleToT) { | |
| { | |
| CustomType<MyType, kConvToT | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| } | |
| // Construct absl::StatusOr<T> from U when U has explicit conversion operator to | |
| // absl::StatusOr<T> | |
| TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) { | |
| { | |
| CustomType<MyType, kConvToStatusOr | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, | |
| kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit> | |
| v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatusOr> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatusOr> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatusOr | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| } | |
| TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) { | |
| // Construction fails because conversion to `Status` is explicit. | |
| { | |
| CustomType<MyType, kConvToStatus | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v; | |
| absl::StatusOr<MyType> statusor(v); | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor = v; | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| } | |
| TEST(StatusOr, AssignmentFromT) { | |
| // Assign to absl::StatusOr<T> from T when T is convertible to | |
| // absl::StatusOr<T> | |
| { | |
| ConvertibleToAnyStatusOr v; | |
| absl::StatusOr<ConvertibleToAnyStatusOr> statusor; | |
| statusor = v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| // Assign to absl::StatusOr<T> from T when T is convertible to Status | |
| { | |
| CustomType<MyType, kConvToStatus> v; | |
| absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor; | |
| statusor = v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| } | |
| TEST(StatusOr, AssignmentFromTypeConvertibleToT) { | |
| // Assign to absl::StatusOr<T> from U when U is convertible to T | |
| { | |
| CustomType<MyType, kConvToT> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_TRUE(statusor.ok()); | |
| } | |
| } | |
| TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) { | |
| // Assign to absl::StatusOr<T> from U when U has conversion operator to | |
| // absl::StatusOr<T> | |
| { | |
| CustomType<MyType, kConvToStatusOr> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatusOr> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToStatusOr | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); | |
| } | |
| } | |
| TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { | |
| // Assign to absl::StatusOr<T> from U when U is convertible to Status | |
| { | |
| CustomType<MyType, kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| { | |
| CustomType<MyType, kConvToT | kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor = v; | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| } | |
| TEST(StatusOr, StatusAssignmentFromStatusError) { | |
| absl::StatusOr<absl::Status> statusor; | |
| statusor.AssignStatus(absl::CancelledError()); | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), absl::CancelledError()); | |
| } | |
| #if GTEST_HAS_DEATH_TEST | |
| TEST(StatusOr, StatusAssignmentFromStatusOk) { | |
| EXPECT_DEBUG_DEATH( | |
| { | |
| absl::StatusOr<absl::Status> statusor; | |
| // This will DCHECK. | |
| statusor.AssignStatus(absl::OkStatus()); | |
| // In optimized mode, we are actually going to get error::INTERNAL for | |
| // status here, rather than crashing, so check that. | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status().code(), absl::StatusCode::kInternal); | |
| }, | |
| "An OK status is not a valid constructor argument to StatusOr<T>"); | |
| } | |
| #endif | |
| TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) { | |
| CustomType<MyType, kConvToStatus> v; | |
| absl::StatusOr<MyType> statusor; | |
| statusor.AssignStatus(v); | |
| EXPECT_FALSE(statusor.ok()); | |
| EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); | |
| } | |
| struct PrintTestStruct { | |
| friend std::ostream& operator<<(std::ostream& os, const PrintTestStruct&) { | |
| return os << "ostream"; | |
| } | |
| template <typename Sink> | |
| friend void AbslStringify(Sink& sink, const PrintTestStruct&) { | |
| sink.Append("stringify"); | |
| } | |
| }; | |
| TEST(StatusOr, OkPrinting) { | |
| absl::StatusOr<PrintTestStruct> print_me = PrintTestStruct{}; | |
| std::stringstream stream; | |
| stream << print_me; | |
| EXPECT_EQ(stream.str(), "ostream"); | |
| EXPECT_EQ(absl::StrCat(print_me), "stringify"); | |
| } | |
| TEST(StatusOr, ErrorPrinting) { | |
| absl::StatusOr<PrintTestStruct> print_me = absl::UnknownError("error"); | |
| std::stringstream stream; | |
| stream << print_me; | |
| const auto error_matcher = | |
| AllOf(HasSubstr("UNKNOWN"), HasSubstr("error"), | |
| AnyOf(AllOf(StartsWith("("), EndsWith(")")), | |
| AllOf(StartsWith("["), EndsWith("]")))); | |
| EXPECT_THAT(stream.str(), error_matcher); | |
| EXPECT_THAT(absl::StrCat(print_me), error_matcher); | |
| } | |
| #ifdef ABSL_INTERNAL_HAVE_BUILTIN_LINE_FILE | |
| #define GET_SOURCE_LOCATION(offset) __builtin_LINE() - offset | |
| #else | |
| #define GET_SOURCE_LOCATION(offset) 1 | |
| #endif | |
| template <typename T> | |
| void CheckSourceLocation( | |
| const absl::StatusOr<T>& status_or, std::vector<int> lines = {}, | |
| absl::SourceLocation loc = absl::SourceLocation::current()) { | |
| ASSERT_EQ(status_or.GetSourceLocations().size(), lines.size()) | |
| << "Size check failed at " << loc.line(); | |
| for (size_t i = 0; i < lines.size(); ++i) { | |
| EXPECT_EQ(absl::string_view(status_or.GetSourceLocations()[i].file_name()), | |
| absl::string_view(loc.file_name())) | |
| << "File name check failed at " << loc.line(); | |
| EXPECT_EQ(status_or.GetSourceLocations()[i].line(), lines[i]) | |
| << "Line check failed at " << loc.line(); | |
| } | |
| } | |
| TEST(StatusOr, AddSourceLocation) { | |
| constexpr int kMaxIter = 10; | |
| { | |
| // Status that ignores source location. | |
| absl::StatusOr<int> status_ignores_source_location[] = { | |
| 123, absl::Status(absl::StatusCode::kInternal, "")}; | |
| for (absl::StatusOr<int>& s : status_ignores_source_location) { | |
| for (int i = 0; i < kMaxIter; ++i) { | |
| s.AddSourceLocation(absl::SourceLocation::current()); | |
| s.AddSourceLocation(absl::SourceLocation()); | |
| } | |
| CheckSourceLocation(s); | |
| } | |
| } | |
| { | |
| // Default SourceLocation is not added. | |
| absl::StatusOr<int> status = absl::Status( | |
| absl::StatusCode::kInternal, "foo", absl::SourceLocation::current()); | |
| int line = GET_SOURCE_LOCATION(1); | |
| for (int i = 0; i < kMaxIter; ++i) { | |
| status.AddSourceLocation(absl::SourceLocation()); | |
| } | |
| CheckSourceLocation(status, {line}); | |
| } | |
| { | |
| // Default SourceLocation is not added. | |
| absl::StatusOr<int> status = absl::Status( | |
| absl::StatusCode::kInternal, "foo", absl::SourceLocation::current()); | |
| int line = GET_SOURCE_LOCATION(1); | |
| std::vector<int> lines = {line}; | |
| lines.reserve(1 + kMaxIter); | |
| for (int i = 0; i < kMaxIter; ++i) { | |
| status.AddSourceLocation(absl::SourceLocation::current()); | |
| lines.push_back(GET_SOURCE_LOCATION(1)); | |
| } | |
| CheckSourceLocation(status, lines); | |
| } | |
| } | |
| absl::StatusOr<int>&& IsRvalueStatus(absl::StatusOr<int>&& s) { | |
| return std::move(s); | |
| } | |
| TEST(StatusOr, WithSourceLocationMove) { | |
| absl::StatusOr<int> original = absl::Status( | |
| absl::StatusCode::kInternal, "message", absl::SourceLocation::current()); | |
| int line = GET_SOURCE_LOCATION(1); | |
| const absl::StatusOr<int> status_or = IsRvalueStatus( | |
| std::move(original).WithSourceLocation(absl::SourceLocation::current())); | |
| int line2 = GET_SOURCE_LOCATION(1); | |
| CheckSourceLocation(status_or, {line, line2}); | |
| EXPECT_FALSE(status_or.ok()); | |
| } | |
| TEST(StatusOr, WithSourceLocationReturn) { | |
| absl::SourceLocation loc1 = absl::SourceLocation::current(); | |
| int line1 = GET_SOURCE_LOCATION(1); | |
| absl::SourceLocation loc2 = absl::SourceLocation::current(); | |
| int line2 = GET_SOURCE_LOCATION(1); | |
| const auto return_error = [&loc1]() -> absl::StatusOr<int> { | |
| return absl::InvalidArgumentError("I am error", loc1); | |
| }; | |
| const auto return_error_with_source_location = | |
| [&return_error, &loc2]() -> absl::StatusOr<int> { | |
| return return_error().WithSourceLocation(loc2); | |
| }; | |
| absl::StatusOr<int> status_or = return_error_with_source_location(); | |
| CheckSourceLocation(status_or, {line1, line2}); | |
| EXPECT_FALSE(status_or.ok()); | |
| } | |
| TEST(StatusOr, SupportsReferenceTypes) { | |
| int i = 1; | |
| absl::StatusOr<int&> s = i; | |
| EXPECT_EQ(&i, &*s); | |
| *s = 10; | |
| EXPECT_EQ(i, 10); | |
| } | |
| TEST(StatusOr, ReferenceFromStatus) { | |
| int i = 10; | |
| absl::StatusOr<int&> s = i; | |
| s = absl::InternalError("foo"); | |
| EXPECT_EQ(s.status().message(), "foo"); | |
| absl::StatusOr<int&> s2 = absl::InternalError("foo2"); | |
| EXPECT_EQ(s2.status().message(), "foo2"); | |
| } | |
| TEST(StatusOr, SupportReferenceValueConstructor) { | |
| int i = 1; | |
| absl::StatusOr<int&> s = i; | |
| absl::StatusOr<const int&> cs = i; | |
| absl::StatusOr<const int&> cs2 = std::move(i); // `T&&` to `const T&` is ok. | |
| EXPECT_EQ(&i, &*s); | |
| EXPECT_EQ(&i, &*cs); | |
| Derived d; | |
| absl::StatusOr<const Base1&> b = d; | |
| EXPECT_EQ(&d, &*b); | |
| // We disallow constructions that cause temporaries. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>, double>)); | |
| EXPECT_FALSE( | |
| (std::is_constructible_v<absl::StatusOr<const int&>, const double&>)); | |
| EXPECT_FALSE( | |
| (std::is_constructible_v<absl::StatusOr<const absl::string_view&>, | |
| std::string>)); | |
| // We disallow constructions with wrong reference. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, int&&>)); | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, const int&>)); | |
| } | |
| TEST(StatusOr, SupportReferenceConvertingConstructor) { | |
| int i = 1; | |
| absl::StatusOr<int&> s = i; | |
| absl::StatusOr<const int&> cs = s; | |
| EXPECT_EQ(&i, &*s); | |
| EXPECT_EQ(&i, &*cs); | |
| // The other direction is not allowed. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, | |
| absl::StatusOr<const int&>>)); | |
| Derived d; | |
| absl::StatusOr<const Base1&> b = absl::StatusOr<const Derived&>(d); | |
| EXPECT_EQ(&d, &*b); | |
| // The other direction is not allowed. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const Derived&>, | |
| absl::StatusOr<const Base1&>>)); | |
| // We disallow conversions that cause temporaries. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>, | |
| absl::StatusOr<int>>)); | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>, | |
| absl::StatusOr<double>>)); | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const int&>, | |
| absl::StatusOr<const double&>>)); | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<const double&>, | |
| absl::StatusOr<const int&>>)); | |
| EXPECT_FALSE( | |
| (std::is_constructible_v<absl::StatusOr<const absl::string_view&>, | |
| absl::StatusOr<std::string>>)); | |
| // We disallow constructions with wrong reference. | |
| EXPECT_FALSE((std::is_constructible_v<absl::StatusOr<int&>, | |
| absl::StatusOr<const int&>>)); | |
| } | |
| TEST(StatusOr, SupportReferenceValueAssignment) { | |
| int i = 1; | |
| absl::StatusOr<int&> s = i; | |
| absl::StatusOr<const int&> cs; | |
| cs = i; | |
| absl::StatusOr<const int&> cs2; | |
| cs2 = std::move(i); // `T&&` to `const T&` is ok. | |
| EXPECT_EQ(&i, &*s); | |
| EXPECT_EQ(&i, &*cs); | |
| Derived d; | |
| absl::StatusOr<const Base1&> b; | |
| b = d; | |
| EXPECT_EQ(&d, &*b); | |
| // We disallow constructions that cause temporaries. | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>, double>)); | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<absl::StatusOr<const int&>, const double&>)); | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const absl::string_view&>, | |
| std::string>)); | |
| // We disallow constructions with wrong reference. | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<int&>, int&&>)); | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<int&>, const int&>)); | |
| } | |
| TEST(StatusOr, SupportReferenceConvertingAssignment) { | |
| int i = 1; | |
| absl::StatusOr<int&> s; | |
| s = i; | |
| absl::StatusOr<const int&> cs; | |
| cs = s; | |
| EXPECT_EQ(&i, &*s); | |
| EXPECT_EQ(&i, &*cs); | |
| // The other direction is not allowed. | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<absl::StatusOr<int&>, absl::StatusOr<const int&>>)); | |
| Derived d; | |
| absl::StatusOr<const Base1&> b; | |
| b = absl::StatusOr<const Derived&>(d); | |
| EXPECT_EQ(&d, &*b); | |
| // The other direction is not allowed. | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const Derived&>, | |
| absl::StatusOr<const Base1&>>)); | |
| // We disallow conversions that cause temporaries. | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>, | |
| absl::StatusOr<const double&>>)); | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const int&>, | |
| absl::StatusOr<double>>)); | |
| EXPECT_FALSE((std::is_assignable_v<absl::StatusOr<const absl::string_view&>, | |
| absl::StatusOr<std::string>>)); | |
| // We disallow constructions with wrong reference. | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<absl::StatusOr<int&>, absl::StatusOr<const int&>>)); | |
| } | |
| TEST(StatusOr, SupportReferenceToNonReferenceConversions) { | |
| int i = 17; | |
| absl::StatusOr<int&> si = i; | |
| absl::StatusOr<float> sf = si; | |
| EXPECT_THAT(sf, IsOkAndHolds(17.)); | |
| i = 20; | |
| sf = si; | |
| EXPECT_THAT(sf, IsOkAndHolds(20.)); | |
| EXPECT_THAT(absl::StatusOr<int64_t>(absl::StatusOr<int&>(i)), | |
| IsOkAndHolds(20)); | |
| EXPECT_THAT(absl::StatusOr<int64_t>(absl::StatusOr<const int&>(i)), | |
| IsOkAndHolds(20)); | |
| std::string str = "str"; | |
| absl::StatusOr<std::string> sos = absl::StatusOr<std::string&>(str); | |
| EXPECT_THAT(sos, IsOkAndHolds("str")); | |
| str = "str2"; | |
| EXPECT_THAT(sos, IsOkAndHolds("str")); | |
| sos = absl::StatusOr<std::string&>(str); | |
| EXPECT_THAT(sos, IsOkAndHolds("str2")); | |
| absl::StatusOr<absl::string_view> sosv = absl::StatusOr<std::string&>(str); | |
| EXPECT_THAT(sosv, IsOkAndHolds("str2")); | |
| str = "str3"; | |
| sosv = absl::StatusOr<std::string&>(str); | |
| EXPECT_THAT(sosv, IsOkAndHolds("str3")); | |
| absl::string_view view = "view"; | |
| // This way it is constructible, but not convertible because | |
| // string_view->string is explicit | |
| EXPECT_THAT( | |
| absl::StatusOr<std::string>(absl::StatusOr<absl::string_view&>(view)), | |
| IsOkAndHolds("view")); | |
| #if defined(ABSL_USES_STD_STRING_VIEW) | |
| // The assignment doesn't work with normal absl::string_view because | |
| // std::string doesn't know about it. | |
| sos = absl::StatusOr<absl::string_view&>(view); | |
| EXPECT_THAT(sos, IsOkAndHolds("view")); | |
| #endif | |
| EXPECT_FALSE((std::is_convertible_v<absl::StatusOr<absl::string_view&>, | |
| absl::StatusOr<std::string>>)); | |
| } | |
| TEST(StatusOr, ReferenceOperatorStarAndArrow) { | |
| std::string str = "Foo"; | |
| absl::StatusOr<std::string&> s = str; | |
| s->assign("Bar"); | |
| EXPECT_EQ(str, "Bar"); | |
| *s = "Baz"; | |
| EXPECT_EQ(str, "Baz"); | |
| const absl::StatusOr<std::string&> cs = str; | |
| // Even if the StatusOr is const, the reference it gives is non-const so we | |
| // can still assign. | |
| *cs = "Finally"; | |
| EXPECT_EQ(str, "Finally"); | |
| cs->clear(); | |
| EXPECT_EQ(cs.value(), str); | |
| EXPECT_EQ(str, ""); | |
| } | |
| TEST(StatusOr, ReferenceValueOr) { | |
| int i = 17; | |
| absl::StatusOr<int&> si = i; | |
| int other = 20; | |
| EXPECT_EQ(&i, &si.value_or(other)); | |
| si = absl::UnknownError(""); | |
| EXPECT_EQ(&other, &si.value_or(other)); | |
| absl::StatusOr<const int&> csi = i; | |
| EXPECT_EQ(&i, &csi.value_or(1)); | |
| const auto value_or_call = [](auto&& sor, auto&& v) | |
| -> decltype(std::forward<decltype(sor)>(sor).value_or( | |
| std::forward<decltype(v)>(v))) {}; | |
| using Probe = decltype(value_or_call); | |
| // Just to verify that Probe works as expected in the good cases. | |
| EXPECT_TRUE((std::is_invocable_v<Probe, absl::StatusOr<const int&>, int&&>)); | |
| // Causes temporary conversion. | |
| EXPECT_FALSE( | |
| (std::is_invocable_v<Probe, absl::StatusOr<const int&>, double&&>)); | |
| // Const invalid. | |
| EXPECT_FALSE((std::is_invocable_v<Probe, absl::StatusOr<int&>, const int&>)); | |
| } | |
| TEST(StatusOr, ReferenceAssignmentFromStatusOr) { | |
| std::vector<int> v = {1, 2, 3}; | |
| absl::StatusOr<int&> si = v[0]; | |
| absl::StatusOr<int&> si2 = v[1]; | |
| EXPECT_THAT(v, ElementsAre(1, 2, 3)); | |
| EXPECT_THAT(si, IsOkAndHolds(1)); | |
| EXPECT_THAT(si2, IsOkAndHolds(2)); | |
| // This rebinds the reference. | |
| si = si2; | |
| EXPECT_THAT(v, ElementsAre(1, 2, 3)); | |
| EXPECT_THAT(si, IsOkAndHolds(2)); | |
| EXPECT_THAT(si2, IsOkAndHolds(2)); | |
| EXPECT_EQ(&*si, &*si2); | |
| } | |
| TEST(StatusOr, ReferenceAssignFromReference) { | |
| std::vector<int> v = {1, 2, 3}; | |
| absl::StatusOr<int&> si = v[0]; | |
| EXPECT_THAT(v, ElementsAre(1, 2, 3)); | |
| EXPECT_THAT(si, IsOkAndHolds(1)); | |
| // This rebinds the reference. | |
| si = v[2]; | |
| EXPECT_THAT(v, ElementsAre(1, 2, 3)); | |
| EXPECT_THAT(si, IsOkAndHolds(3)); | |
| EXPECT_EQ(&*si, &v[2]); | |
| } | |
| TEST(StatusOr, ReferenceIsNotLifetimeBoundForStarValue) { | |
| int i = 0; | |
| // op*/value should not be LIFETIME_BOUND because the ref is not limited to | |
| // the lifetime of the StatusOr. | |
| int& r = *absl::StatusOr<int&>(i); | |
| EXPECT_EQ(&r, &i); | |
| int& r2 = absl::StatusOr<int&>(i).value(); | |
| EXPECT_EQ(&r2, &i); | |
| struct S { | |
| int i; | |
| }; | |
| S s; | |
| // op-> should also not be LIFETIME_BOUND for refs. | |
| int& r3 = absl::StatusOr<S&>(s)->i; | |
| EXPECT_EQ(&r3, &s.i); | |
| } | |
| template <typename Expected, typename T> | |
| void TestReferenceDeref() { | |
| static_assert(std::is_same_v<Expected, decltype(*std::declval<T>())>); | |
| static_assert(std::is_same_v<Expected, decltype(std::declval<T>().value())>); | |
| } | |
| TEST(StatusOr, ReferenceTypeIsMaintainedOnDeref) { | |
| TestReferenceDeref<int&, absl::StatusOr<int&>&>(); | |
| TestReferenceDeref<int&, absl::StatusOr<int&>&&>(); | |
| TestReferenceDeref<int&, const absl::StatusOr<int&>&>(); | |
| TestReferenceDeref<int&, const absl::StatusOr<int&>&&>(); | |
| TestReferenceDeref<const int&, absl::StatusOr<const int&>&>(); | |
| TestReferenceDeref<const int&, absl::StatusOr<const int&>&&>(); | |
| TestReferenceDeref<const int&, const absl::StatusOr<const int&>&>(); | |
| TestReferenceDeref<const int&, const absl::StatusOr<const int&>&&>(); | |
| struct Struct { | |
| int value; | |
| }; | |
| EXPECT_TRUE( | |
| (std::is_same_v< | |
| int&, decltype((std::declval<absl::StatusOr<Struct&>>()->value))>)); | |
| EXPECT_TRUE( | |
| (std::is_same_v< | |
| int&, | |
| decltype((std::declval<const absl::StatusOr<Struct&>>()->value))>)); | |
| EXPECT_TRUE( | |
| (std::is_same_v< | |
| const int&, | |
| decltype((std::declval<absl::StatusOr<const Struct&>>()->value))>)); | |
| } | |
| } // namespace |