// 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 #include #include #include #include #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/types/any.h" #include "absl/utility/utility.h" namespace { using ::testing::AllOf; using ::testing::AnyWith; using ::testing::ElementsAre; using ::testing::Field; using ::testing::Ne; using ::testing::Not; using ::testing::Pointee; using ::testing::VariantWith; #ifdef GTEST_HAS_STATUS_MATCHERS using ::testing::status::IsOk; using ::testing::status::IsOkAndHolds; #else // GTEST_HAS_STATUS_MATCHERS inline const ::absl::Status& GetStatus(const ::absl::Status& status) { return status; } template inline const ::absl::Status& GetStatus(const ::absl::StatusOr& status) { return status.status(); } // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a // reference to StatusOr. template class IsOkAndHoldsMatcherImpl : public ::testing::MatcherInterface { public: typedef typename std::remove_reference::type::value_type value_type; template explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) : inner_matcher_(::testing::SafeMatcherCast( std::forward(inner_matcher))) {} void DescribeTo(std::ostream* os) const override { *os << "is OK and has a value that "; inner_matcher_.DescribeTo(os); } void DescribeNegationTo(std::ostream* os) const override { *os << "isn't OK or has a value that "; inner_matcher_.DescribeNegationTo(os); } bool MatchAndExplain( StatusOrType actual_value, ::testing::MatchResultListener* result_listener) const override { if (!actual_value.ok()) { *result_listener << "which has status " << actual_value.status(); return false; } ::testing::StringMatchResultListener inner_listener; const bool matches = inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); const std::string inner_explanation = inner_listener.str(); if (!inner_explanation.empty()) { *result_listener << "which contains value " << ::testing::PrintToString(*actual_value) << ", " << inner_explanation; } return matches; } private: const ::testing::Matcher inner_matcher_; }; // Implements IsOkAndHolds(m) as a polymorphic matcher. template class IsOkAndHoldsMatcher { public: explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) : inner_matcher_(std::move(inner_matcher)) {} // Converts this polymorphic matcher to a monomorphic matcher of the // given type. StatusOrType can be either StatusOr or a // reference to StatusOr. template operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher( new IsOkAndHoldsMatcherImpl(inner_matcher_)); } private: const InnerMatcher inner_matcher_; }; // Monomorphic implementation of matcher IsOk() for a given type T. // T can be Status, StatusOr<>, or a reference to either of them. template class MonoIsOkMatcherImpl : public ::testing::MatcherInterface { public: void DescribeTo(std::ostream* os) const override { *os << "is OK"; } void DescribeNegationTo(std::ostream* os) const override { *os << "is not OK"; } bool MatchAndExplain(T actual_value, ::testing::MatchResultListener*) const override { return GetStatus(actual_value).ok(); } }; // Implements IsOk() as a polymorphic matcher. class IsOkMatcher { public: template operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher(new MonoIsOkMatcherImpl()); } }; // Macros for testing the results of functions that return absl::Status or // absl::StatusOr (for any type T). #define EXPECT_OK(expression) EXPECT_THAT(expression, IsOk()) // Returns a gMock matcher that matches a StatusOr<> whose status is // OK and whose value matches the inner matcher. template IsOkAndHoldsMatcher::type> IsOkAndHolds( InnerMatcher&& inner_matcher) { return IsOkAndHoldsMatcher::type>( std::forward(inner_matcher)); } // Returns a gMock matcher that matches a Status or StatusOr<> which is OK. inline IsOkMatcher IsOk() { return IsOkMatcher(); } #endif // GTEST_HAS_STATUS_MATCHERS 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 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() {} int pad; }; class Base2 { public: virtual ~Base2() {} int yetotherpad; }; class Derived : public Base1, public Base2 { public: virtual ~Derived() {} int evenmorepad; }; class CopyNoAssign { public: explicit CopyNoAssign(int value) : foo(value) {} CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} int foo; private: const CopyNoAssign& operator=(const CopyNoAssign&); }; absl::StatusOr> ReturnUniquePtr() { // Uses implicit constructor from T&& return absl::make_unique(0); } TEST(StatusOr, ElementType) { static_assert(std::is_same::value_type, int>(), ""); static_assert(std::is_same::value_type, char>(), ""); } TEST(StatusOr, TestMoveOnlyInitialization) { absl::StatusOr> 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> thing(ReturnUniquePtr()); ASSERT_TRUE(thing.ok()); std::unique_ptr 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 ptr(*ReturnUniquePtr()); EXPECT_EQ(0, *ptr); } TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { static_assert( std::is_same&&>().value())>(), "value() for const temporaries should return const T&&"); } TEST(StatusOr, TestMoveOnlyConversion) { absl::StatusOr> 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 works in vector. std::vector>> 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 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(status).status().message(), "Some error"); EXPECT_EQ(status.message(), "Some error"); EXPECT_EQ(absl::StatusOr(std::move(status)).status().message(), "Some error"); EXPECT_NE(status.message(), "Some error"); } // 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_); \ 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 thing; EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); const absl::StatusOr thing2; EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError("")); } TEST(StatusOrDeathTest, TestValueNotOk) { absl::StatusOr thing(absl::CancelledError()); EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); } TEST(StatusOrDeathTest, TestValueNotOkConst) { const absl::StatusOr thing(absl::UnknownError("")); EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); } TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { absl::StatusOr thing; EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); } TEST(StatusOrDeathTest, TestPointerValueNotOk) { absl::StatusOr thing(absl::CancelledError()); EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); } TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { const absl::StatusOr 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 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 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 status_or(kIntValue); EXPECT_EQ(kIntValue, status_or.value()); EXPECT_EQ(kIntValue, std::move(status_or).value()); } { absl::StatusOr 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 status_or(kError); EXPECT_DEATH_OR_THROW(status_or.value(), kError); } TEST(StatusOr, TestStatusCtor) { absl::StatusOr 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 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(absl::in_place, 10), IsOkAndHolds(Field(&Foo::x, 10))); } struct InPlaceHelper { InPlaceHelper(std::initializer_list xs, std::unique_ptr yy) : x(xs), y(std::move(yy)) {} const std::vector x; std::unique_ptr y; }; TEST(StatusOr, InPlaceInitListConstruction) { absl::StatusOr status_or(absl::in_place, {10, 11, 12}, absl::make_unique(13)); EXPECT_THAT(status_or, IsOkAndHolds(AllOf( Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)), Field(&InPlaceHelper::y, Pointee(13))))); } TEST(StatusOr, Emplace) { absl::StatusOr 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 status_or(absl::in_place, {10, 11, 12}, absl::make_unique(13)); status_or.emplace({1, 2, 3}, absl::make_unique(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}, absl::make_unique(4)); EXPECT_THAT(status_or, IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), Field(&InPlaceHelper::y, Pointee(4))))); } TEST(StatusOr, TestCopyCtorStatusOk) { const int kI = 4; const absl::StatusOr original(kI); const absl::StatusOr copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(*original, *copy); } TEST(StatusOr, TestCopyCtorStatusNotOk) { absl::StatusOr original(absl::CancelledError()); absl::StatusOr copy(original); EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestCopyCtorNonAssignable) { const int kI = 4; CopyNoAssign value(kI); absl::StatusOr original(value); absl::StatusOr copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(original->foo, copy->foo); } TEST(StatusOr, TestCopyCtorStatusOKConverting) { const int kI = 4; absl::StatusOr original(kI); absl::StatusOr copy(original); EXPECT_OK(copy.status()); EXPECT_DOUBLE_EQ(*original, *copy); } TEST(StatusOr, TestCopyCtorStatusNotOkConverting) { absl::StatusOr original(absl::CancelledError()); absl::StatusOr copy(original); EXPECT_EQ(copy.status(), original.status()); } TEST(StatusOr, TestAssignmentStatusOk) { // Copy assignmment { const auto p = std::make_shared(17); absl::StatusOr> source(p); absl::StatusOr> target; target = source; ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(p, *target); ASSERT_TRUE(source.ok()); EXPECT_OK(source.status()); EXPECT_EQ(p, *source); } // Move asssignment { const auto p = std::make_shared(17); absl::StatusOr> source(p); absl::StatusOr> target; target = std::move(source); ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(p, *target); ASSERT_TRUE(source.ok()); EXPECT_OK(source.status()); EXPECT_EQ(nullptr, *source); } } TEST(StatusOr, TestAssignmentStatusNotOk) { // Copy assignment { const absl::Status expected = absl::CancelledError(); absl::StatusOr source(expected); absl::StatusOr 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 source(expected); absl::StatusOr 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 source(kI); absl::StatusOr target; target = source; ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_DOUBLE_EQ(kI, *target); ASSERT_TRUE(source.ok()); EXPECT_OK(source.status()); EXPECT_DOUBLE_EQ(kI, *source); } // Move assignment { const auto p = new int(17); absl::StatusOr> source(absl::WrapUnique(p)); absl::StatusOr> target; target = std::move(source); ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(p, target->get()); ASSERT_TRUE(source.ok()); EXPECT_OK(source.status()); 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(A{11})), IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11), Field(&ImplicitConstructibleFromA::moved, true)))); absl::StatusOr a(A{12}); EXPECT_THAT( absl::implicit_cast>(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&, absl::StatusOr>::value)); EXPECT_FALSE( (std::is_convertible&&, absl::StatusOr>::value)); EXPECT_THAT( absl::StatusOr(absl::StatusOr(A{11})), IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11), Field(&ExplicitConstructibleFromA::moved, true)))); absl::StatusOr a(A{12}); EXPECT_THAT( absl::StatusOr(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(absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::StatusOr(absl::StatusOr(false)), IsOkAndHolds(false)); EXPECT_THAT( absl::implicit_cast>( absl::StatusOr(false)), IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); EXPECT_FALSE((std::is_convertible< absl::StatusOr, absl::StatusOr>::value)); } TEST(StatusOr, BooleanConstructionWithImplicitCasts) { EXPECT_THAT(absl::StatusOr(absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::StatusOr(absl::StatusOr(false)), IsOkAndHolds(false)); EXPECT_THAT( absl::StatusOr{ absl::StatusOr(false)}, IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); EXPECT_THAT( absl::StatusOr{ absl::StatusOr(absl::InvalidArgumentError(""))}, Not(IsOk())); EXPECT_THAT( absl::StatusOr{ absl::StatusOr(ConvertibleToBool{false})}, IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); EXPECT_THAT( absl::StatusOr{ absl::StatusOr(absl::InvalidArgumentError(""))}, Not(IsOk())); } TEST(StatusOr, ConstImplicitCast) { EXPECT_THAT(absl::implicit_cast>( absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::implicit_cast>( absl::StatusOr(false)), IsOkAndHolds(false)); EXPECT_THAT(absl::implicit_cast>( absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::implicit_cast>( absl::StatusOr(false)), IsOkAndHolds(false)); EXPECT_THAT(absl::implicit_cast>( absl::StatusOr("foo")), IsOkAndHolds("foo")); EXPECT_THAT(absl::implicit_cast>( absl::StatusOr("foo")), IsOkAndHolds("foo")); EXPECT_THAT( absl::implicit_cast>>( absl::StatusOr>( std::make_shared("foo"))), IsOkAndHolds(Pointee(std::string("foo")))); } TEST(StatusOr, ConstExplicitConstruction) { EXPECT_THAT(absl::StatusOr(absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::StatusOr(absl::StatusOr(false)), IsOkAndHolds(false)); EXPECT_THAT(absl::StatusOr(absl::StatusOr(true)), IsOkAndHolds(true)); EXPECT_THAT(absl::StatusOr(absl::StatusOr(false)), IsOkAndHolds(false)); } struct ExplicitConstructibleFromInt { int x; explicit ExplicitConstructibleFromInt(int y) : x(y) {} }; TEST(StatusOr, ExplicitConstruction) { EXPECT_THAT(absl::StatusOr(10), IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10))); } TEST(StatusOr, ImplicitConstruction) { // Check implicit casting works. auto status_or = absl::implicit_cast>>(10); EXPECT_THAT(status_or, IsOkAndHolds(VariantWith(10))); } TEST(StatusOr, ImplicitConstructionFromInitliazerList) { // Note: dropping the explicit std::initializer_list is not supported // by absl::StatusOr or absl::optional. auto status_or = absl::implicit_cast>>({{10, 20, 30}}); EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); } TEST(StatusOr, UniquePtrImplicitConstruction) { auto status_or = absl::implicit_cast>>( absl::make_unique()); EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); } TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) { absl::StatusOr> status_or = CopyDetector(10); absl::StatusOr> status_error = absl::InvalidArgumentError("foo"); EXPECT_THAT(status_or, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); absl::StatusOr> a = status_or; EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); absl::StatusOr> a_err = status_error; EXPECT_THAT(a_err, Not(IsOk())); const absl::StatusOr>& cref = status_or; absl::StatusOr> b = cref; // NOLINT EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); const absl::StatusOr>& cref_err = status_error; absl::StatusOr> b_err = cref_err; // NOLINT EXPECT_THAT(b_err, Not(IsOk())); absl::StatusOr> c = std::move(status_or); EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); absl::StatusOr> c_err = std::move(status_error); EXPECT_THAT(c_err, Not(IsOk())); } TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) { absl::StatusOr> status_or = CopyDetector(10); absl::StatusOr> status_error = absl::InvalidArgumentError("foo"); absl::StatusOr> a; a = status_or; EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); a = status_error; EXPECT_THAT(a, Not(IsOk())); const absl::StatusOr>& cref = status_or; a = cref; EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); const absl::StatusOr>& 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() {} Copyable(const Copyable&) {} Copyable& operator=(const Copyable&) { return *this; } }; struct MoveOnly { MoveOnly() {} MoveOnly(MoveOnly&&) {} MoveOnly& operator=(MoveOnly&&) { return *this; } }; struct NonMovable { NonMovable() {} 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::value); EXPECT_TRUE(std::is_copy_assignable::value); EXPECT_TRUE(std::is_move_constructible::value); EXPECT_TRUE(std::is_move_assignable::value); EXPECT_FALSE(std::is_copy_constructible::value); EXPECT_FALSE(std::is_copy_assignable::value); EXPECT_TRUE(std::is_move_constructible::value); EXPECT_TRUE(std::is_move_assignable::value); EXPECT_FALSE(std::is_copy_constructible::value); EXPECT_FALSE(std::is_copy_assignable::value); EXPECT_FALSE(std::is_move_constructible::value); EXPECT_FALSE(std::is_move_assignable::value); } TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) { absl::StatusOr status_or = CopyDetector(10); absl::StatusOr status_error = absl::InvalidArgumentError("foo"); EXPECT_THAT( status_or, IsOkAndHolds(AnyWith(CopyDetectorHas(10, true, false)))); absl::StatusOr a = status_or; EXPECT_THAT( a, IsOkAndHolds(AnyWith(CopyDetectorHas(10, false, true)))); absl::StatusOr a_err = status_error; EXPECT_THAT(a_err, Not(IsOk())); const absl::StatusOr& cref = status_or; // No lint for no-change copy. absl::StatusOr b = cref; // NOLINT EXPECT_THAT( b, IsOkAndHolds(AnyWith(CopyDetectorHas(10, false, true)))); const absl::StatusOr& cref_err = status_error; // No lint for no-change copy. absl::StatusOr b_err = cref_err; // NOLINT EXPECT_THAT(b_err, Not(IsOk())); absl::StatusOr c = std::move(status_or); EXPECT_THAT( c, IsOkAndHolds(AnyWith(CopyDetectorHas(10, true, false)))); absl::StatusOr c_err = std::move(status_error); EXPECT_THAT(c_err, Not(IsOk())); } TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) { absl::StatusOr status_or = CopyDetector(10); absl::StatusOr status_error = absl::InvalidArgumentError("foo"); absl::StatusOr a; a = status_or; EXPECT_THAT( a, IsOkAndHolds(AnyWith(CopyDetectorHas(10, false, true)))); a = status_error; EXPECT_THAT(a, Not(IsOk())); const absl::StatusOr& cref = status_or; a = cref; EXPECT_THAT( a, IsOkAndHolds(AnyWith(CopyDetectorHas(10, false, true)))); const absl::StatusOr& cref_err = status_error; a = cref_err; EXPECT_THAT(a, Not(IsOk())); a = std::move(status_or); EXPECT_THAT( a, IsOkAndHolds(AnyWith(CopyDetectorHas(10, true, false)))); a = std::move(status_error); EXPECT_THAT(a, Not(IsOk())); } TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) { absl::StatusOr status_or(10); ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); absl::StatusOr a(status_or); EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); const absl::StatusOr& cref = status_or; absl::StatusOr b(cref); // NOLINT EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); absl::StatusOr c(std::move(status_or)); EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); } TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) { absl::StatusOr status_or(10); ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); absl::StatusOr a; a = status_or; EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); const absl::StatusOr& cref = status_or; absl::StatusOr b; b = cref; EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); absl::StatusOr c; c = std::move(status_or); EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); } TEST(StatusOr, AbslAnyAssignment) { EXPECT_FALSE((std::is_assignable, absl::StatusOr>::value)); absl::StatusOr status_or; status_or = absl::InvalidArgumentError("foo"); EXPECT_THAT(status_or, Not(IsOk())); } TEST(StatusOr, ImplicitAssignment) { absl::StatusOr> status_or; status_or = 10; EXPECT_THAT(status_or, IsOkAndHolds(VariantWith(10))); } TEST(StatusOr, SelfDirectInitAssignment) { absl::StatusOr> status_or = {{10, 20, 30}}; status_or = *status_or; EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); } TEST(StatusOr, ImplicitCastFromInitializerList) { absl::StatusOr> status_or = {{10, 20, 30}}; EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); } TEST(StatusOr, UniquePtrImplicitAssignment) { absl::StatusOr> status_or; status_or = absl::make_unique(); 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, B*>::value)); EXPECT_TRUE((std::is_convertible>::value)); EXPECT_FALSE((std::is_constructible, C*>::value)); EXPECT_FALSE((std::is_convertible>::value)); } TEST(StatusOr, TestAssignmentStatusNotOkConverting) { // Copy assignment { const absl::Status expected = absl::CancelledError(); absl::StatusOr source(expected); absl::StatusOr 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 source(expected); absl::StatusOr 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 so = long_str; so = *&so; ASSERT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(long_str, *so); } // Copy-assignment, error status { absl::StatusOr 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 so = 17; // Fool the compiler, which otherwise complains. auto& same = so; so = std::move(same); ASSERT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(17, *so); } // Move-assignment with copyable type, error status { absl::StatusOr 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> so = absl::WrapUnique(raw); // Fool the compiler, which otherwise complains. auto& same = so; so = std::move(same); ASSERT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(raw, so->get()); } // Move-assignment with non-copyable type, error status { absl::StatusOr> 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 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&, const FromConstructibleAssignableLvalue&>::value)); EXPECT_TRUE((std::is_assignable&, FromConstructibleAssignableLvalue&&>::value)); EXPECT_FALSE( (std::is_assignable&, const FromConstructibleAssignableRvalue&>::value)); EXPECT_TRUE((std::is_assignable&, FromConstructibleAssignableRvalue&&>::value)); EXPECT_TRUE( (std::is_assignable&, const FromImplicitConstructibleOnly&>::value)); EXPECT_FALSE((std::is_assignable&, const FromAssignableOnly&>::value)); absl::StatusOr 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 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 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 good(4); EXPECT_TRUE(good.ok()); absl::StatusOr bad(absl::CancelledError()); EXPECT_FALSE(bad.ok()); EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, OperatorStarRefQualifiers) { static_assert( std::is_same&>())>(), "Unexpected ref-qualifiers"); static_assert( std::is_same&>())>(), "Unexpected ref-qualifiers"); static_assert( std::is_same&&>())>(), "Unexpected ref-qualifiers"); static_assert( std::is_same&&>())>(), "Unexpected ref-qualifiers"); } TEST(StatusOr, OperatorStar) { const absl::StatusOr const_lvalue("hello"); EXPECT_EQ("hello", *const_lvalue); absl::StatusOr 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 const_rvalue("hello"); EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT absl::StatusOr rvalue("hello"); EXPECT_EQ("hello", *std::move(rvalue)); } TEST(StatusOr, OperatorArrowQualifiers) { static_assert( std::is_same< const int*, decltype(std::declval&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same< int*, decltype(std::declval&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same< const int*, decltype(std::declval&&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same< int*, decltype(std::declval&&>().operator->())>(), "Unexpected qualifiers"); } TEST(StatusOr, OperatorArrow) { const absl::StatusOr const_lvalue("hello"); EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); absl::StatusOr lvalue("hello"); EXPECT_EQ(std::string("hello"), lvalue->c_str()); } TEST(StatusOr, RValueStatus) { absl::StatusOr 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 thing(kI); EXPECT_EQ(kI, *thing); } TEST(StatusOr, TestValueConst) { const int kI = 4; const absl::StatusOr thing(kI); EXPECT_EQ(kI, *thing); } TEST(StatusOr, TestPointerDefaultCtor) { absl::StatusOr thing; EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); } TEST(StatusOr, TestPointerStatusCtor) { absl::StatusOr 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 so(&kI); EXPECT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(&kI, *so); } // Construction from a null pointer constant { absl::StatusOr so(nullptr); EXPECT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(nullptr, *so); } // Construction from a non-literal null pointer { const int* const p = nullptr; absl::StatusOr so(p); EXPECT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(nullptr, *so); } } TEST(StatusOr, TestPointerCopyCtorStatusOk) { const int kI = 0; absl::StatusOr original(&kI); absl::StatusOr copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(*original, *copy); } TEST(StatusOr, TestPointerCopyCtorStatusNotOk) { absl::StatusOr original(absl::CancelledError()); absl::StatusOr copy(original); EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) { Derived derived; absl::StatusOr original(&derived); absl::StatusOr copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(static_cast(*original), *copy); } TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) { absl::StatusOr original(absl::CancelledError()); absl::StatusOr copy(original); EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerAssignmentStatusOk) { const int kI = 0; absl::StatusOr source(&kI); absl::StatusOr target; target = source; EXPECT_OK(target.status()); EXPECT_EQ(*source, *target); } TEST(StatusOr, TestPointerAssignmentStatusNotOk) { absl::StatusOr source(absl::CancelledError()); absl::StatusOr target; target = source; EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerAssignmentStatusOKConverting) { Derived derived; absl::StatusOr source(&derived); absl::StatusOr target; target = source; EXPECT_OK(target.status()); EXPECT_EQ(static_cast(*source), *target); } TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) { absl::StatusOr source(absl::CancelledError()); absl::StatusOr target; target = source; EXPECT_EQ(target.status(), source.status()); } TEST(StatusOr, TestPointerStatus) { const int kI = 0; absl::StatusOr good(&kI); EXPECT_TRUE(good.ok()); absl::StatusOr bad(absl::CancelledError()); EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerValue) { const int kI = 0; absl::StatusOr thing(&kI); EXPECT_EQ(&kI, *thing); } TEST(StatusOr, TestPointerValueConst) { const int kI = 0; const absl::StatusOr thing(&kI); EXPECT_EQ(&kI, *thing); } TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) { using EvilType = std::vector>; static_assert(std::is_copy_constructible::value, ""); std::vector<::absl::StatusOr> 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 a; // Copy-construction absl::StatusOr b(a); // Copy-assignment EXPECT_FALSE(std::is_copy_assignable>::value); // Move-construction absl::StatusOr c(std::move(a)); // Move-assignment EXPECT_FALSE(std::is_move_assignable>::value); } 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>>; MapType a; // Move-construction MapType b(std::move(a)); // Move-assignment a = std::move(b); } TEST(StatusOr, ValueOrOk) { const absl::StatusOr status_or = 0; EXPECT_EQ(status_or.value_or(-1), 0); } TEST(StatusOr, ValueOrDefault) { const absl::StatusOr status_or = absl::CancelledError(); EXPECT_EQ(status_or.value_or(-1), -1); } TEST(StatusOr, MoveOnlyValueOrOk) { EXPECT_THAT(absl::StatusOr>(absl::make_unique(0)) .value_or(absl::make_unique(-1)), Pointee(0)); } TEST(StatusOr, MoveOnlyValueOrDefault) { EXPECT_THAT(absl::StatusOr>(absl::CancelledError()) .value_or(absl::make_unique(-1)), Pointee(-1)); } static absl::StatusOr MakeStatus() { return 100; } TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); } TEST(StatusOr, EqualityOperator) { constexpr int kNumCases = 4; std::array, kNumCases> group1 = { absl::StatusOr(1), absl::StatusOr(2), absl::StatusOr(absl::InvalidArgumentError("msg")), absl::StatusOr(absl::InternalError("msg"))}; std::array, kNumCases> group2 = { absl::StatusOr(1), absl::StatusOr(2), absl::StatusOr(absl::InvalidArgumentError("msg")), absl::StatusOr(absl::InternalError("msg"))}; for (int i = 0; i < kNumCases; ++i) { for (int 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` based on value of // `conv_traits`. template struct StatusOrConversionBase {}; template struct StatusOrConversionBase { operator absl::StatusOr() const& { // NOLINT return absl::InvalidArgumentError("conversion to absl::StatusOr"); } operator absl::StatusOr() && { // NOLINT return absl::InvalidArgumentError("conversion to absl::StatusOr"); } }; template struct StatusOrConversionBase { explicit operator absl::StatusOr() const& { return absl::InvalidArgumentError("conversion to absl::StatusOr"); } explicit operator absl::StatusOr() && { return absl::InvalidArgumentError("conversion to absl::StatusOr"); } }; // This class has conversion operator to `T` based on the value of // `conv_traits`. template struct ConversionBase {}; template struct ConversionBase { operator T() const& { return t; } // NOLINT operator T() && { return std::move(t); } // NOLINT T t; }; template struct ConversionBase { 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 struct StatusConversionBase {}; template <> struct StatusConversionBase { operator absl::Status() const& { // NOLINT return absl::InternalError("conversion to Status"); } operator absl::Status() && { // NOLINT return absl::InternalError("conversion to Status"); } }; template <> struct StatusConversionBase { 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`, based on values of the template parameters. template struct CustomType : StatusOrConversionBase, ConversionBase, StatusConversionBase {}; struct ConvertibleToAnyStatusOr { template operator absl::StatusOr() const { // NOLINT return absl::InvalidArgumentError("Conversion to absl::StatusOr"); } }; // Test the rank of overload resolution for `StatusOr` constructor and // assignment, from highest to lowest: // 1. T/Status // 2. U that has conversion operator to absl::StatusOr // 3. U that is convertible to Status // 4. U that is convertible to T TEST(StatusOr, ConstructionFromT) { // Construct absl::StatusOr from T when T is convertible to // absl::StatusOr { ConvertibleToAnyStatusOr v; absl::StatusOr statusor(v); EXPECT_TRUE(statusor.ok()); } { ConvertibleToAnyStatusOr v; absl::StatusOr statusor = v; EXPECT_TRUE(statusor.ok()); } // Construct absl::StatusOr from T when T is explicitly convertible to // Status { CustomType v; absl::StatusOr> statusor( v); EXPECT_TRUE(statusor.ok()); } { CustomType v; absl::StatusOr> statusor = v; EXPECT_TRUE(statusor.ok()); } } // Construct absl::StatusOr from U when U is explicitly convertible to T TEST(StatusOr, ConstructionFromTypeConvertibleToT) { { CustomType v; absl::StatusOr statusor(v); EXPECT_TRUE(statusor.ok()); } { CustomType v; absl::StatusOr statusor = v; EXPECT_TRUE(statusor.ok()); } } // Construct absl::StatusOr from U when U has explicit conversion operator to // absl::StatusOr TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) { { CustomType v; absl::StatusOr statusor(v); EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor(v); EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor(v); EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor(v); EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } } TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) { // Construction fails because conversion to `Status` is explicit. { CustomType v; absl::StatusOr statusor(v); EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; absl::StatusOr statusor(v); EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; absl::StatusOr statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; absl::StatusOr statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } } TEST(StatusOr, AssignmentFromT) { // Assign to absl::StatusOr from T when T is convertible to // absl::StatusOr { ConvertibleToAnyStatusOr v; absl::StatusOr statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } // Assign to absl::StatusOr from T when T is convertible to Status { CustomType v; absl::StatusOr> statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } } TEST(StatusOr, AssignmentFromTypeConvertibleToT) { // Assign to absl::StatusOr from U when U is convertible to T { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } } TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) { // Assign to absl::StatusOr from U when U has conversion operator to // absl::StatusOr { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_EQ(statusor, v.operator absl::StatusOr()); } } TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { // Assign to absl::StatusOr from U when U is convertible to Status { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; absl::StatusOr statusor; statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } } } // namespace