From b56cbdd23834a65682c0b46f367f8679e83bc894 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 18 Sep 2020 15:55:15 -0700 Subject: Abseil LTS 20200923 What's New: * `absl::StatusOr` has been released. See our [blog post](https://abseil.io/blog/2020-091021-status) for more information. * Abseil Flags reflection interfaces have been released. * Abseil Flags memory usage has been significantly optimized. * Abseil now supports a "hardened" build mode. This build mode enables runtime checks that guard against programming errors that may lead to security vulnerabilities. Notable Fixes: * Sanitizer dynamic annotations like `AnnotateRWLockCreate` that are also defined by the compiler sanitizer implementation are no longer also defined by Abseil. * Sanitizer macros are now prefixed with `ABSL_` to avoid naming collisions. * Sanitizer usage is now automatically detected and no longer requires macros like `ADDRESS_SANITIZER` to be defined on the command line. Breaking Changes: * Abseil no longer contains a `dynamic_annotations` library. Users using a supported build system (Bazel or CMake) are unaffected by this, but users manually specifying link libraries may get an error about a missing linker input. Baseline: 7680a5f8efe32de4753baadbd63e74e59d95bac1 Cherry picks: None --- absl/status/statusor_test.cc | 1800 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1800 insertions(+) create mode 100644 absl/status/statusor_test.cc (limited to 'absl/status/statusor_test.cc') diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc new file mode 100644 index 00000000..5e4b2687 --- /dev/null +++ b/absl/status/statusor_test.cc @@ -0,0 +1,1800 @@ +// 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); +} + +// 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 -- cgit v1.2.3