diff options
Diffstat (limited to 'third_party/abseil_cpp/absl/status/statusor_test.cc')
-rw-r--r-- | third_party/abseil_cpp/absl/status/statusor_test.cc | 2080 |
1 files changed, 1569 insertions, 511 deletions
diff --git a/third_party/abseil_cpp/absl/status/statusor_test.cc b/third_party/abseil_cpp/absl/status/statusor_test.cc index fc849515ca91..c2e8fb7e359e 100644 --- a/third_party/abseil_cpp/absl/status/statusor_test.cc +++ b/third_party/abseil_cpp/absl/status/statusor_test.cc @@ -1,753 +1,1811 @@ -/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. - -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 - - http://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. -==============================================================================*/ - -// Unit tests for StatusOr +// 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 <array> +#include <initializer_list> #include <memory> #include <type_traits> +#include <utility> -#include "absl/base/internal/exception_testing.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN +#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 <typename T> +inline const ::absl::Status& GetStatus(const ::absl::StatusOr<T>& status) { + return status.status(); +} + +// Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a +// reference to StatusOr<T>. +template <typename StatusOrType> +class IsOkAndHoldsMatcherImpl + : public ::testing::MatcherInterface<StatusOrType> { + public: + typedef + typename std::remove_reference<StatusOrType>::type::value_type value_type; + + template <typename InnerMatcher> + explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) + : inner_matcher_(::testing::SafeMatcherCast<const value_type&>( + std::forward<InnerMatcher>(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<const value_type&> inner_matcher_; +}; + +// Implements IsOkAndHolds(m) as a polymorphic matcher. +template <typename InnerMatcher> +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<T> or a + // reference to StatusOr<T>. + template <typename StatusOrType> + operator ::testing::Matcher<StatusOrType>() const { // NOLINT + return ::testing::Matcher<StatusOrType>( + new IsOkAndHoldsMatcherImpl<const StatusOrType&>(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 <typename T> +class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> { + 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 <typename T> + operator ::testing::Matcher<T>() const { // NOLINT + return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>()); + } +}; + +// Macros for testing the results of functions that return absl::Status or +// absl::StatusOr<T> (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 <typename InnerMatcher> +IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds( + InnerMatcher&& inner_matcher) { + return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( + std::forward<InnerMatcher>(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<const CopyDetector&> CopyDetectorHas(int a, bool b, bool c) { + return AllOf(Field(&CopyDetector::x, a), Field(&CopyDetector::moved, b), + Field(&CopyDetector::copied, c)); +} + class Base1 { public: virtual ~Base1() {} - int pad_; + int pad; }; class Base2 { public: virtual ~Base2() {} - int yetotherpad_; + int yetotherpad; }; class Derived : public Base1, public Base2 { public: - ~Derived() override {} - int evenmorepad_; + virtual ~Derived() {} + int evenmorepad; }; class CopyNoAssign { public: - explicit CopyNoAssign(int value) : foo_(value) {} - CopyNoAssign(const CopyNoAssign& other) : foo_(other.foo_) {} - int foo_; + explicit CopyNoAssign(int value) : foo(value) {} + CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} + int foo; private: const CopyNoAssign& operator=(const CopyNoAssign&); }; -class NoDefaultConstructor { - public: - explicit NoDefaultConstructor(int foo); -}; - -static_assert(!std::is_default_constructible<NoDefaultConstructor>(), - "Should not be default-constructible."); - -StatusOr<std::unique_ptr<int>> ReturnUniquePtr() { +absl::StatusOr<std::unique_ptr<int>> ReturnUniquePtr() { // Uses implicit constructor from T&& - return std::unique_ptr<int>(new int(0)); + return absl::make_unique<int>(0); } TEST(StatusOr, ElementType) { - static_assert(std::is_same<StatusOr<int>::element_type, int>(), ""); - static_assert(std::is_same<StatusOr<char>::element_type, char>(), ""); -} - -TEST(StatusOr, NullPointerStatusOr) { - // As a very special case, null-plain-pointer StatusOr used to be an - // error. Test that it no longer is. - StatusOr<int*> null_status(nullptr); - EXPECT_TRUE(null_status.ok()); - EXPECT_EQ(null_status.ValueOrDie(), nullptr); -} - -TEST(StatusOr, TestNoDefaultConstructorInitialization) { - // Explicitly initialize it with an error code. - StatusOr<NoDefaultConstructor> statusor(CancelledError("")); - EXPECT_FALSE(statusor.ok()); - EXPECT_EQ(statusor.status().code(), absl::StatusCode::kCancelled); - - // Default construction of StatusOr initializes it with an UNKNOWN error code. - StatusOr<NoDefaultConstructor> statusor2; - EXPECT_FALSE(statusor2.ok()); - EXPECT_EQ(statusor2.status().code(), absl::StatusCode::kUnknown); + static_assert(std::is_same<absl::StatusOr<int>::value_type, int>(), ""); + static_assert(std::is_same<absl::StatusOr<char>::value_type, char>(), ""); } TEST(StatusOr, TestMoveOnlyInitialization) { - StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); ASSERT_TRUE(thing.ok()); - EXPECT_EQ(0, *thing.ValueOrDie()); - int* previous = thing.ValueOrDie().get(); + EXPECT_EQ(0, **thing); + int* previous = thing->get(); thing = ReturnUniquePtr(); EXPECT_TRUE(thing.ok()); - EXPECT_EQ(0, *thing.ValueOrDie()); - EXPECT_NE(previous, thing.ValueOrDie().get()); -} - -TEST(StatusOr, TestMoveOnlyStatusCtr) { - StatusOr<std::unique_ptr<int>> thing(CancelledError("")); - ASSERT_FALSE(thing.ok()); + EXPECT_EQ(0, **thing); + EXPECT_NE(previous, thing->get()); } TEST(StatusOr, TestMoveOnlyValueExtraction) { - StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); + absl::StatusOr<std::unique_ptr<int>> thing(ReturnUniquePtr()); ASSERT_TRUE(thing.ok()); - std::unique_ptr<int> ptr = thing.ConsumeValueOrDie(); + std::unique_ptr<int> ptr = *std::move(thing); EXPECT_EQ(0, *ptr); thing = std::move(ptr); - ptr = std::move(thing.ValueOrDie()); + ptr = std::move(*thing); EXPECT_EQ(0, *ptr); } +TEST(StatusOr, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { + std::unique_ptr<int> ptr(*ReturnUniquePtr()); + EXPECT_EQ(0, *ptr); +} + +TEST(StatusOr, TestValueOrDieOverloadForConstTemporary) { + static_assert( + std::is_same<const int&&, + decltype( + std::declval<const absl::StatusOr<int>&&>().value())>(), + "value() for const temporaries should return const T&&"); +} + TEST(StatusOr, TestMoveOnlyConversion) { - StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr()); + absl::StatusOr<std::unique_ptr<const int>> const_thing(ReturnUniquePtr()); EXPECT_TRUE(const_thing.ok()); - EXPECT_EQ(0, *const_thing.ValueOrDie()); + EXPECT_EQ(0, **const_thing); // Test rvalue converting assignment - const int* const_previous = const_thing.ValueOrDie().get(); + const int* const_previous = const_thing->get(); const_thing = ReturnUniquePtr(); EXPECT_TRUE(const_thing.ok()); - EXPECT_EQ(0, *const_thing.ValueOrDie()); - EXPECT_NE(const_previous, const_thing.ValueOrDie().get()); + EXPECT_EQ(0, **const_thing); + EXPECT_NE(const_previous, const_thing->get()); } TEST(StatusOr, TestMoveOnlyVector) { - // Sanity check that StatusOr<MoveOnly> works in vector. - std::vector<StatusOr<std::unique_ptr<int>>> vec; + // Sanity check that absl::StatusOr<MoveOnly> works in vector. + std::vector<absl::StatusOr<std::unique_ptr<int>>> vec; vec.push_back(ReturnUniquePtr()); vec.resize(2); auto another_vec = std::move(vec); - EXPECT_EQ(0, *another_vec[0].ValueOrDie()); - EXPECT_EQ(absl::StatusCode::kUnknown, another_vec[1].status().code()); -} - -TEST(StatusOr, TestMoveWithValuesAndErrors) { - StatusOr<std::string> status_or(std::string(1000, '0')); - StatusOr<std::string> value1(std::string(1000, '1')); - StatusOr<std::string> value2(std::string(1000, '2')); - StatusOr<std::string> error1(UnknownError("error1")); - StatusOr<std::string> error2(UnknownError("error2")); - - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '0'), status_or.ValueOrDie()); - - // Overwrite the value in status_or with another value. - status_or = std::move(value1); - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '1'), status_or.ValueOrDie()); - - // Overwrite the value in status_or with an error. - status_or = std::move(error1); - ASSERT_FALSE(status_or.ok()); - EXPECT_EQ("error1", status_or.status().message()); - - // Overwrite the error in status_or with another error. - status_or = std::move(error2); - ASSERT_FALSE(status_or.ok()); - EXPECT_EQ("error2", status_or.status().message()); - - // Overwrite the error with a value. - status_or = std::move(value2); - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '2'), status_or.ValueOrDie()); -} - -TEST(StatusOr, TestCopyWithValuesAndErrors) { - StatusOr<std::string> status_or(std::string(1000, '0')); - StatusOr<std::string> value1(std::string(1000, '1')); - StatusOr<std::string> value2(std::string(1000, '2')); - StatusOr<std::string> error1(UnknownError("error1")); - StatusOr<std::string> error2(UnknownError("error2")); - - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '0'), status_or.ValueOrDie()); - - // Overwrite the value in status_or with another value. - status_or = value1; - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '1'), status_or.ValueOrDie()); - - // Overwrite the value in status_or with an error. - status_or = error1; - ASSERT_FALSE(status_or.ok()); - EXPECT_EQ("error1", status_or.status().message()); - - // Overwrite the error in status_or with another error. - status_or = error2; - ASSERT_FALSE(status_or.ok()); - EXPECT_EQ("error2", status_or.status().message()); - - // Overwrite the error with a value. - status_or = value2; - ASSERT_TRUE(status_or.ok()); - EXPECT_EQ(std::string(1000, '2'), status_or.ValueOrDie()); - - // Verify original values unchanged. - EXPECT_EQ(std::string(1000, '1'), value1.ValueOrDie()); - EXPECT_EQ("error1", error1.status().message()); - EXPECT_EQ("error2", error2.status().message()); - EXPECT_EQ(std::string(1000, '2'), value2.ValueOrDie()); + EXPECT_EQ(0, **another_vec[0]); + EXPECT_EQ(absl::UnknownError(""), another_vec[1].status()); } TEST(StatusOr, TestDefaultCtor) { - StatusOr<int> thing; + absl::StatusOr<int> thing; EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); } +TEST(StatusOr, StatusCtorForwards) { + absl::Status status(absl::StatusCode::kInternal, "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(status).status().message(), "Some error"); + EXPECT_EQ(status.message(), "Some error"); + + EXPECT_EQ(absl::StatusOr<int>(std::move(status)).status().message(), + "Some error"); + EXPECT_NE(status.message(), "Some error"); +} + +// 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) { - StatusOr<int> thing; - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, ""); + absl::StatusOr<int> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); + const absl::StatusOr<int> thing2; + EXPECT_DEATH_OR_THROW(thing2.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestValueNotOk) { + absl::StatusOr<int> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} - const StatusOr<int> thing2; - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, ""); +TEST(StatusOrDeathTest, TestValueNotOkConst) { + const absl::StatusOr<int> thing(absl::UnknownError("")); + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { + absl::StatusOr<int*> thing; + EXPECT_DEATH_OR_THROW(thing.value(), absl::UnknownError("")); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOk) { + absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { + const absl::StatusOr<int*> thing(absl::CancelledError()); + EXPECT_DEATH_OR_THROW(thing.value(), absl::CancelledError()); +} + +#if GTEST_HAS_DEATH_TEST +TEST(StatusOrDeathTest, TestStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + // This will DCHECK + absl::StatusOr<int> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} + +TEST(StatusOrDeathTest, TestPointerStatusCtorStatusOk) { + EXPECT_DEBUG_DEATH( + { + absl::StatusOr<int*> thing(absl::OkStatus()); + // In optimized mode, we are actually going to get error::INTERNAL for + // status here, rather than crashing, so check that. + EXPECT_FALSE(thing.ok()); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kInternal); + }, + "An OK status is not a valid constructor argument"); +} +#endif + +TEST(StatusOr, ValueAccessor) { + const int kIntValue = 110; + { + absl::StatusOr<int> status_or(kIntValue); + EXPECT_EQ(kIntValue, status_or.value()); + EXPECT_EQ(kIntValue, std::move(status_or).value()); + } + { + absl::StatusOr<CopyDetector> status_or(kIntValue); + EXPECT_THAT(status_or, + IsOkAndHolds(CopyDetectorHas(kIntValue, false, false))); + CopyDetector copy_detector = status_or.value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, false, true)); + copy_detector = std::move(status_or).value(); + EXPECT_THAT(copy_detector, CopyDetectorHas(kIntValue, true, false)); + } +} + +TEST(StatusOr, BadValueAccess) { + const absl::Status kError = absl::CancelledError("message"); + absl::StatusOr<int> status_or(kError); + EXPECT_DEATH_OR_THROW(status_or.value(), kError); } TEST(StatusOr, TestStatusCtor) { - StatusOr<int> thing(Status(absl::StatusCode::kCancelled, "")); + absl::StatusOr<int> thing(absl::CancelledError()); EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); } + + TEST(StatusOr, TestValueCtor) { const int kI = 4; - const StatusOr<int> thing(kI); + const absl::StatusOr<int> thing(kI); EXPECT_TRUE(thing.ok()); - EXPECT_EQ(kI, thing.ValueOrDie()); + EXPECT_EQ(kI, *thing); +} + +struct Foo { + const int x; + explicit Foo(int y) : x(y) {} +}; + +TEST(StatusOr, InPlaceConstruction) { + EXPECT_THAT(absl::StatusOr<Foo>(absl::in_place, 10), + IsOkAndHolds(Field(&Foo::x, 10))); +} + +struct InPlaceHelper { + InPlaceHelper(std::initializer_list<int> xs, std::unique_ptr<int> yy) + : x(xs), y(std::move(yy)) {} + const std::vector<int> x; + std::unique_ptr<int> y; +}; + +TEST(StatusOr, InPlaceInitListConstruction) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + EXPECT_THAT(status_or, IsOkAndHolds(AllOf( + Field(&InPlaceHelper::x, ElementsAre(10, 11, 12)), + Field(&InPlaceHelper::y, Pointee(13))))); +} + +TEST(StatusOr, Emplace) { + absl::StatusOr<Foo> status_or_foo(10); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); + status_or_foo = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or_foo.ok()); + EXPECT_EQ(status_or_foo.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or_foo.status().message(), "msg"); + status_or_foo.emplace(20); + EXPECT_THAT(status_or_foo, IsOkAndHolds(Field(&Foo::x, 20))); +} + +TEST(StatusOr, EmplaceInitializerList) { + absl::StatusOr<InPlaceHelper> status_or(absl::in_place, {10, 11, 12}, + absl::make_unique<int>(13)); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(4)); + EXPECT_THAT(status_or, + IsOkAndHolds(AllOf(Field(&InPlaceHelper::x, ElementsAre(1, 2, 3)), + Field(&InPlaceHelper::y, Pointee(4))))); + status_or = absl::InvalidArgumentError("msg"); + EXPECT_FALSE(status_or.ok()); + EXPECT_EQ(status_or.status().code(), absl::StatusCode::kInvalidArgument); + EXPECT_EQ(status_or.status().message(), "msg"); + status_or.emplace({1, 2, 3}, absl::make_unique<int>(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 StatusOr<int> original(kI); - const StatusOr<int> copy(original); - EXPECT_EQ(copy.status(), original.status()); - EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie()); + const absl::StatusOr<int> original(kI); + const absl::StatusOr<int> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); } TEST(StatusOr, TestCopyCtorStatusNotOk) { - StatusOr<int> original(Status(absl::StatusCode::kCancelled, "")); - StatusOr<int> copy(original); - EXPECT_EQ(copy.status(), original.status()); + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<int> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestCopyCtorNonAssignable) { const int kI = 4; CopyNoAssign value(kI); - StatusOr<CopyNoAssign> original(value); - StatusOr<CopyNoAssign> copy(original); - EXPECT_EQ(copy.status(), original.status()); - EXPECT_EQ(original.ValueOrDie().foo_, copy.ValueOrDie().foo_); + absl::StatusOr<CopyNoAssign> original(value); + absl::StatusOr<CopyNoAssign> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(original->foo, copy->foo); } TEST(StatusOr, TestCopyCtorStatusOKConverting) { const int kI = 4; - StatusOr<int> original(kI); - StatusOr<double> copy(original); - EXPECT_EQ(copy.status(), original.status()); - EXPECT_DOUBLE_EQ(original.ValueOrDie(), copy.ValueOrDie()); + absl::StatusOr<int> original(kI); + absl::StatusOr<double> copy(original); + EXPECT_OK(copy.status()); + EXPECT_DOUBLE_EQ(*original, *copy); } TEST(StatusOr, TestCopyCtorStatusNotOkConverting) { - StatusOr<int> original(Status(absl::StatusCode::kCancelled, "")); - StatusOr<double> copy(original); + absl::StatusOr<int> original(absl::CancelledError()); + absl::StatusOr<double> copy(original); EXPECT_EQ(copy.status(), original.status()); } TEST(StatusOr, TestAssignmentStatusOk) { - const int kI = 4; - StatusOr<int> source(kI); - StatusOr<int> target; - target = source; - EXPECT_EQ(target.status(), source.status()); - EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie()); + // Copy assignmment + { + const auto p = std::make_shared<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_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<int>(17); + absl::StatusOr<std::shared_ptr<int>> source(p); + + absl::StatusOr<std::shared_ptr<int>> target; + target = std::move(source); + + ASSERT_TRUE(target.ok()); + EXPECT_OK(target.status()); + EXPECT_EQ(p, *target); + + ASSERT_TRUE(source.ok()); + EXPECT_OK(source.status()); + EXPECT_EQ(nullptr, *source); + } } TEST(StatusOr, TestAssignmentStatusNotOk) { - StatusOr<int> source(Status(absl::StatusCode::kCancelled, "")); - StatusOr<int> target; - target = source; - EXPECT_EQ(target.status(), source.status()); + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<int> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, TestAssignmentStatusOKConverting) { + // Copy assignment + { + const int kI = 4; + absl::StatusOr<int> source(kI); + + absl::StatusOr<double> target; + target = source; + + ASSERT_TRUE(target.ok()); + EXPECT_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<std::unique_ptr<int>> source(absl::WrapUnique(p)); + + absl::StatusOr<std::shared_ptr<int>> 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<ImplicitConstructibleFromA>>( + absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 11), + Field(&ImplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromA>>(a), + IsOkAndHolds(AllOf(Field(&ImplicitConstructibleFromA::x, 12), + Field(&ImplicitConstructibleFromA::moved, false)))); +} + +struct ExplicitConstructibleFromA { + int x; + bool moved; + explicit ExplicitConstructibleFromA(const A& a) : x(a.x), moved(false) {} + explicit ExplicitConstructibleFromA(A&& a) : x(a.x), moved(true) {} +}; + +TEST(StatusOr, ExplicitConvertingConstructor) { + EXPECT_FALSE( + (std::is_convertible<const absl::StatusOr<A>&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_FALSE( + (std::is_convertible<absl::StatusOr<A>&&, + absl::StatusOr<ExplicitConstructibleFromA>>::value)); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(absl::StatusOr<A>(A{11})), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 11), + Field(&ExplicitConstructibleFromA::moved, true)))); + absl::StatusOr<A> a(A{12}); + EXPECT_THAT( + absl::StatusOr<ExplicitConstructibleFromA>(a), + IsOkAndHolds(AllOf(Field(&ExplicitConstructibleFromA::x, 12), + Field(&ExplicitConstructibleFromA::moved, false)))); +} + +struct ImplicitConstructibleFromBool { + ImplicitConstructibleFromBool(bool y) : x(y) {} // NOLINT + bool x = false; +}; + +struct ConvertibleToBool { + explicit ConvertibleToBool(bool y) : x(y) {} + operator bool() const { return x; } // NOLINT + bool x = false; +}; + +TEST(StatusOr, ImplicitBooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<ImplicitConstructibleFromBool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_FALSE((std::is_convertible< + absl::StatusOr<ConvertibleToBool>, + absl::StatusOr<ImplicitConstructibleFromBool>>::value)); +} + +TEST(StatusOr, BooleanConstructionWithImplicitCasts) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<ConvertibleToBool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(false)}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<bool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); + + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(ConvertibleToBool{false})}, + IsOkAndHolds(Field(&ImplicitConstructibleFromBool::x, false))); + EXPECT_THAT( + absl::StatusOr<ImplicitConstructibleFromBool>{ + absl::StatusOr<ConvertibleToBool>(absl::InvalidArgumentError(""))}, + Not(IsOk())); +} + +TEST(StatusOr, ConstImplicitCast) { + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<bool>>( + absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const bool>>( + absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<const std::string>>( + absl::StatusOr<std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT(absl::implicit_cast<absl::StatusOr<std::string>>( + absl::StatusOr<const std::string>("foo")), + IsOkAndHolds("foo")); + EXPECT_THAT( + absl::implicit_cast<absl::StatusOr<std::shared_ptr<const std::string>>>( + absl::StatusOr<std::shared_ptr<std::string>>( + std::make_shared<std::string>("foo"))), + IsOkAndHolds(Pointee(std::string("foo")))); +} + +TEST(StatusOr, ConstExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<bool>(absl::StatusOr<const bool>(false)), + IsOkAndHolds(false)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(true)), + IsOkAndHolds(true)); + EXPECT_THAT(absl::StatusOr<const bool>(absl::StatusOr<bool>(false)), + IsOkAndHolds(false)); +} + +struct ExplicitConstructibleFromInt { + int x; + explicit ExplicitConstructibleFromInt(int y) : x(y) {} +}; + +TEST(StatusOr, ExplicitConstruction) { + EXPECT_THAT(absl::StatusOr<ExplicitConstructibleFromInt>(10), + IsOkAndHolds(Field(&ExplicitConstructibleFromInt::x, 10))); +} + +TEST(StatusOr, ImplicitConstruction) { + // Check implicit casting works. + auto status_or = + absl::implicit_cast<absl::StatusOr<absl::variant<int, std::string>>>(10); + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, ImplicitConstructionFromInitliazerList) { + // Note: dropping the explicit std::initializer_list<int> is not supported + // by absl::StatusOr or absl::optional. + auto status_or = + absl::implicit_cast<absl::StatusOr<std::vector<int>>>({{10, 20, 30}}); + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitConstruction) { + auto status_or = absl::implicit_cast<absl::StatusOr<std::unique_ptr<Base1>>>( + absl::make_unique<Derived>()); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveConstructorTests) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, + IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + absl::StatusOr<absl::StatusOr<CopyDetector>> b = cref; // NOLINT + EXPECT_THAT(b, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + absl::StatusOr<absl::StatusOr<CopyDetector>> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::StatusOr<CopyDetector>> c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::StatusOr<CopyDetector>> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, NestedStatusOrCopyAndMoveAssignment) { + absl::StatusOr<absl::StatusOr<CopyDetector>> status_or = CopyDetector(10); + absl::StatusOr<absl::StatusOr<CopyDetector>> status_error = + absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::StatusOr<CopyDetector>> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref = status_or; + a = cref; + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::StatusOr<CopyDetector>>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT(a, IsOkAndHolds(IsOkAndHolds(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +struct Copyable { + Copyable() {} + 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<Copyable>::value); + EXPECT_TRUE(std::is_copy_assignable<Copyable>::value); + EXPECT_TRUE(std::is_move_constructible<Copyable>::value); + EXPECT_TRUE(std::is_move_assignable<Copyable>::value); + EXPECT_FALSE(std::is_copy_constructible<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_assignable<MoveOnly>::value); + EXPECT_TRUE(std::is_move_constructible<MoveOnly>::value); + EXPECT_TRUE(std::is_move_assignable<MoveOnly>::value); + EXPECT_FALSE(std::is_copy_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_copy_assignable<NonMovable>::value); + EXPECT_FALSE(std::is_move_constructible<NonMovable>::value); + EXPECT_FALSE(std::is_move_assignable<NonMovable>::value); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveConstructorTests) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + EXPECT_THAT( + status_or, + IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + absl::StatusOr<absl::any> a_err = status_error; + EXPECT_THAT(a_err, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + // No lint for no-change copy. + absl::StatusOr<absl::any> b = cref; // NOLINT + EXPECT_THAT( + b, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + // No lint for no-change copy. + absl::StatusOr<absl::any> b_err = cref_err; // NOLINT + EXPECT_THAT(b_err, Not(IsOk())); + + absl::StatusOr<absl::any> c = std::move(status_or); + EXPECT_THAT( + c, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + absl::StatusOr<absl::any> c_err = std::move(status_error); + EXPECT_THAT(c_err, Not(IsOk())); +} + +TEST(StatusOr, StatusOrAnyCopyAndMoveAssignment) { + absl::StatusOr<absl::any> status_or = CopyDetector(10); + absl::StatusOr<absl::any> status_error = absl::InvalidArgumentError("foo"); + absl::StatusOr<absl::any> a; + a = status_or; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + a = status_error; + EXPECT_THAT(a, Not(IsOk())); + + const absl::StatusOr<absl::any>& cref = status_or; + a = cref; + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, false, true)))); + const absl::StatusOr<absl::any>& cref_err = status_error; + a = cref_err; + EXPECT_THAT(a, Not(IsOk())); + a = std::move(status_or); + EXPECT_THAT( + a, IsOkAndHolds(AnyWith<CopyDetector>(CopyDetectorHas(10, true, false)))); + a = std::move(status_error); + EXPECT_THAT(a, Not(IsOk())); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsConstructor) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a(status_or); + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b(cref); // NOLINT + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c(std::move(status_or)); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, StatusOrCopyAndMoveTestsAssignment) { + absl::StatusOr<CopyDetector> status_or(10); + ASSERT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(10, false, false))); + absl::StatusOr<CopyDetector> a; + a = status_or; + EXPECT_THAT(a, IsOkAndHolds(CopyDetectorHas(10, false, true))); + const absl::StatusOr<CopyDetector>& cref = status_or; + absl::StatusOr<CopyDetector> b; + b = cref; + EXPECT_THAT(b, IsOkAndHolds(CopyDetectorHas(10, false, true))); + absl::StatusOr<CopyDetector> c; + c = std::move(status_or); + EXPECT_THAT(c, IsOkAndHolds(CopyDetectorHas(10, true, false))); +} + +TEST(StatusOr, AbslAnyAssignment) { + EXPECT_FALSE((std::is_assignable<absl::StatusOr<absl::any>, + absl::StatusOr<int>>::value)); + absl::StatusOr<absl::any> status_or; + status_or = absl::InvalidArgumentError("foo"); + EXPECT_THAT(status_or, Not(IsOk())); +} + +TEST(StatusOr, ImplicitAssignment) { + absl::StatusOr<absl::variant<int, std::string>> status_or; + status_or = 10; + EXPECT_THAT(status_or, IsOkAndHolds(VariantWith<int>(10))); +} + +TEST(StatusOr, SelfDirectInitAssignment) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + status_or = *status_or; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, ImplicitCastFromInitializerList) { + absl::StatusOr<std::vector<int>> status_or = {{10, 20, 30}}; + EXPECT_THAT(status_or, IsOkAndHolds(ElementsAre(10, 20, 30))); +} + +TEST(StatusOr, UniquePtrImplicitAssignment) { + absl::StatusOr<std::unique_ptr<Base1>> status_or; + status_or = absl::make_unique<Derived>(); + EXPECT_THAT(status_or, IsOkAndHolds(Ne(nullptr))); +} + +TEST(StatusOr, Pointer) { + struct A {}; + struct B : public A {}; + struct C : private A {}; + + EXPECT_TRUE((std::is_constructible<absl::StatusOr<A*>, B*>::value)); + EXPECT_TRUE((std::is_convertible<B*, absl::StatusOr<A*>>::value)); + EXPECT_FALSE((std::is_constructible<absl::StatusOr<A*>, C*>::value)); + EXPECT_FALSE((std::is_convertible<C*, absl::StatusOr<A*>>::value)); +} + +TEST(StatusOr, TestAssignmentStatusNotOkConverting) { + // Copy assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = source; + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(expected, source.status()); + } + + // Move assignment + { + const absl::Status expected = absl::CancelledError(); + absl::StatusOr<int> source(expected); + + absl::StatusOr<double> target; + target = std::move(source); + + EXPECT_FALSE(target.ok()); + EXPECT_EQ(expected, target.status()); + + EXPECT_FALSE(source.ok()); + EXPECT_EQ(source.status().code(), absl::StatusCode::kInternal); + } +} + +TEST(StatusOr, SelfAssignment) { + // Copy-assignment, status OK + { + // A string long enough that it's likely to defeat any inline representation + // optimization. + const std::string long_str(128, 'a'); + + absl::StatusOr<std::string> so = long_str; + so = *&so; + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(long_str, *so); + } + + // Copy-assignment, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + so = *&so; + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with copyable type, status OK + { + absl::StatusOr<int> so = 17; + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(17, *so); + } + + // Move-assignment with copyable type, error status + { + absl::StatusOr<int> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } + + // Move-assignment with non-copyable type, status OK + { + const auto raw = new int(17); + absl::StatusOr<std::unique_ptr<int>> so = absl::WrapUnique(raw); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + ASSERT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(raw, so->get()); + } + + // Move-assignment with non-copyable type, error status + { + absl::StatusOr<std::unique_ptr<int>> so = absl::NotFoundError("taco"); + + // Fool the compiler, which otherwise complains. + auto& same = so; + so = std::move(same); + + EXPECT_FALSE(so.ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kNotFound); + EXPECT_EQ(so.status().message(), "taco"); + } +} + +// These types form the overload sets of the constructors and the assignment +// operators of `MockValue`. They distinguish construction from assignment, +// lvalue from rvalue. +struct FromConstructibleAssignableLvalue {}; +struct FromConstructibleAssignableRvalue {}; +struct FromImplicitConstructibleOnly {}; +struct FromAssignableOnly {}; + +// This class is for testing the forwarding value assignments of `StatusOr`. +// `from_rvalue` indicates whether the constructor or the assignment taking +// rvalue reference is called. `from_assignment` indicates whether any +// assignment is called. +struct MockValue { + // Constructs `MockValue` from `FromConstructibleAssignableLvalue`. + MockValue(const FromConstructibleAssignableLvalue&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Constructs `MockValue` from `FromConstructibleAssignableRvalue`. + MockValue(FromConstructibleAssignableRvalue&&) // NOLINT + : from_rvalue(true), assigned(false) {} + // Constructs `MockValue` from `FromImplicitConstructibleOnly`. + // `MockValue` is not assignable from `FromImplicitConstructibleOnly`. + MockValue(const FromImplicitConstructibleOnly&) // NOLINT + : from_rvalue(false), assigned(false) {} + // Assigns `FromConstructibleAssignableLvalue`. + MockValue& operator=(const FromConstructibleAssignableLvalue&) { + from_rvalue = false; + assigned = true; + return *this; + } + // Assigns `FromConstructibleAssignableRvalue` (rvalue only). + MockValue& operator=(FromConstructibleAssignableRvalue&&) { + from_rvalue = true; + assigned = true; + return *this; + } + // Assigns `FromAssignableOnly`, but not constructible from + // `FromAssignableOnly`. + MockValue& operator=(const FromAssignableOnly&) { + from_rvalue = false; + assigned = true; + return *this; + } + bool from_rvalue; + bool assigned; +}; + +// operator=(U&&) +TEST(StatusOr, PerfectForwardingAssignment) { + // U == T + constexpr int kValue1 = 10, kValue2 = 20; + absl::StatusOr<CopyDetector> status_or; + CopyDetector lvalue(kValue1); + status_or = lvalue; + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue1, false, true))); + status_or = CopyDetector(kValue2); + EXPECT_THAT(status_or, IsOkAndHolds(CopyDetectorHas(kValue2, true, false))); + + // U != T + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableLvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableLvalue&&>::value)); + EXPECT_FALSE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromConstructibleAssignableRvalue&>::value)); + EXPECT_TRUE((std::is_assignable<absl::StatusOr<MockValue>&, + FromConstructibleAssignableRvalue&&>::value)); + EXPECT_TRUE( + (std::is_assignable<absl::StatusOr<MockValue>&, + const FromImplicitConstructibleOnly&>::value)); + EXPECT_FALSE((std::is_assignable<absl::StatusOr<MockValue>&, + const FromAssignableOnly&>::value)); + + absl::StatusOr<MockValue> from_lvalue(FromConstructibleAssignableLvalue{}); + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_FALSE(from_lvalue->assigned); + from_lvalue = FromConstructibleAssignableLvalue{}; + EXPECT_FALSE(from_lvalue->from_rvalue); + EXPECT_TRUE(from_lvalue->assigned); + + absl::StatusOr<MockValue> from_rvalue(FromConstructibleAssignableRvalue{}); + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_FALSE(from_rvalue->assigned); + from_rvalue = FromConstructibleAssignableRvalue{}; + EXPECT_TRUE(from_rvalue->from_rvalue); + EXPECT_TRUE(from_rvalue->assigned); + + absl::StatusOr<MockValue> from_implicit_constructible( + FromImplicitConstructibleOnly{}); + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); + // construct a temporary `StatusOr` object and invoke the `StatusOr` move + // assignment operator. + from_implicit_constructible = FromImplicitConstructibleOnly{}; + EXPECT_FALSE(from_implicit_constructible->from_rvalue); + EXPECT_FALSE(from_implicit_constructible->assigned); } TEST(StatusOr, TestStatus) { - StatusOr<int> good(4); + absl::StatusOr<int> good(4); EXPECT_TRUE(good.ok()); - StatusOr<int> bad(Status(absl::StatusCode::kCancelled, "")); + absl::StatusOr<int> bad(absl::CancelledError()); EXPECT_FALSE(bad.ok()); - EXPECT_EQ(bad.status(), Status(absl::StatusCode::kCancelled, "")); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, OperatorStarRefQualifiers) { + static_assert( + std::is_same<const int&, + decltype(*std::declval<const absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&, decltype(*std::declval<absl::StatusOr<int>&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<const int&&, + decltype(*std::declval<const absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); + static_assert( + std::is_same<int&&, decltype(*std::declval<absl::StatusOr<int>&&>())>(), + "Unexpected ref-qualifiers"); +} + +TEST(StatusOr, OperatorStar) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ("hello", *const_lvalue); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ("hello", *lvalue); + + // Note: Recall that std::move() is equivalent to a static_cast to an rvalue + // reference type. + const absl::StatusOr<std::string> const_rvalue("hello"); + EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT + + absl::StatusOr<std::string> rvalue("hello"); + EXPECT_EQ("hello", *std::move(rvalue)); +} + +TEST(StatusOr, OperatorArrowQualifiers) { + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + const int*, + decltype(std::declval<const absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); + static_assert( + std::is_same< + int*, decltype(std::declval<absl::StatusOr<int>&&>().operator->())>(), + "Unexpected qualifiers"); +} + +TEST(StatusOr, OperatorArrow) { + const absl::StatusOr<std::string> const_lvalue("hello"); + EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); + + absl::StatusOr<std::string> lvalue("hello"); + EXPECT_EQ(std::string("hello"), lvalue->c_str()); +} + +TEST(StatusOr, RValueStatus) { + absl::StatusOr<int> so(absl::NotFoundError("taco")); + const absl::Status s = std::move(so).status(); + + EXPECT_EQ(s.code(), absl::StatusCode::kNotFound); + EXPECT_EQ(s.message(), "taco"); + + // Check that !ok() still implies !status().ok(), even after moving out of the + // object. See the note on the rvalue ref-qualified status method. + EXPECT_FALSE(so.ok()); // NOLINT + EXPECT_FALSE(so.status().ok()); + EXPECT_EQ(so.status().code(), absl::StatusCode::kInternal); + EXPECT_EQ(so.status().message(), "Status accessed after move."); } TEST(StatusOr, TestValue) { const int kI = 4; - StatusOr<int> thing(kI); - EXPECT_EQ(kI, thing.ValueOrDie()); + absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); } TEST(StatusOr, TestValueConst) { const int kI = 4; - const StatusOr<int> thing(kI); - EXPECT_EQ(kI, thing.ValueOrDie()); -} - -TEST(StatusOrDeathTest, TestValueNotOk) { - StatusOr<int> thing(Status(absl::StatusCode::kCancelled, "cancelled")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled"); -} - -TEST(StatusOrDeathTest, TestValueNotOkConst) { - const StatusOr<int> thing(Status(absl::StatusCode::kUnknown, "")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, ""); + const absl::StatusOr<int> thing(kI); + EXPECT_EQ(kI, *thing); } TEST(StatusOr, TestPointerDefaultCtor) { - StatusOr<int*> thing; + absl::StatusOr<int*> thing; EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), absl::StatusCode::kUnknown); } -TEST(StatusOrDeathTest, TestPointerDefaultCtorValue) { - StatusOr<int*> thing; - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, ""); -} + TEST(StatusOr, TestPointerStatusCtor) { - StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "")); + absl::StatusOr<int*> thing(absl::CancelledError()); EXPECT_FALSE(thing.ok()); - EXPECT_EQ(thing.status(), Status(absl::StatusCode::kCancelled, "")); + EXPECT_EQ(thing.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerValueCtor) { const int kI = 4; - StatusOr<const int*> thing(&kI); - EXPECT_TRUE(thing.ok()); - EXPECT_EQ(&kI, thing.ValueOrDie()); + + // Construction from a non-null pointer + { + absl::StatusOr<const int*> so(&kI); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(&kI, *so); + } + + // Construction from a null pointer constant + { + absl::StatusOr<const int*> 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<const int*> so(p); + EXPECT_TRUE(so.ok()); + EXPECT_OK(so.status()); + EXPECT_EQ(nullptr, *so); + } } TEST(StatusOr, TestPointerCopyCtorStatusOk) { const int kI = 0; - StatusOr<const int*> original(&kI); - StatusOr<const int*> copy(original); - EXPECT_EQ(copy.status(), original.status()); - EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie()); + absl::StatusOr<const int*> original(&kI); + absl::StatusOr<const int*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(*original, *copy); } TEST(StatusOr, TestPointerCopyCtorStatusNotOk) { - StatusOr<int*> original(Status(absl::StatusCode::kCancelled, "")); - StatusOr<int*> copy(original); - EXPECT_EQ(copy.status(), original.status()); + absl::StatusOr<int*> original(absl::CancelledError()); + absl::StatusOr<int*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) { Derived derived; - StatusOr<Derived*> original(&derived); - StatusOr<Base2*> copy(original); - EXPECT_EQ(copy.status(), original.status()); - EXPECT_EQ(static_cast<const Base2*>(original.ValueOrDie()), - copy.ValueOrDie()); + absl::StatusOr<Derived*> original(&derived); + absl::StatusOr<Base2*> copy(original); + EXPECT_OK(copy.status()); + EXPECT_EQ(static_cast<const Base2*>(*original), *copy); } TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) { - StatusOr<Derived*> original(Status(absl::StatusCode::kCancelled, "")); - StatusOr<Base2*> copy(original); - EXPECT_EQ(copy.status(), original.status()); + absl::StatusOr<Derived*> original(absl::CancelledError()); + absl::StatusOr<Base2*> copy(original); + EXPECT_EQ(copy.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerAssignmentStatusOk) { const int kI = 0; - StatusOr<const int*> source(&kI); - StatusOr<const int*> target; + absl::StatusOr<const int*> source(&kI); + absl::StatusOr<const int*> target; target = source; - EXPECT_EQ(target.status(), source.status()); - EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie()); + EXPECT_OK(target.status()); + EXPECT_EQ(*source, *target); } TEST(StatusOr, TestPointerAssignmentStatusNotOk) { - StatusOr<int*> source(Status(absl::StatusCode::kCancelled, "")); - StatusOr<int*> target; + absl::StatusOr<int*> source(absl::CancelledError()); + absl::StatusOr<int*> target; + target = source; + EXPECT_EQ(target.status().code(), absl::StatusCode::kCancelled); +} + +TEST(StatusOr, TestPointerAssignmentStatusOKConverting) { + Derived derived; + absl::StatusOr<Derived*> source(&derived); + absl::StatusOr<Base2*> target; + target = source; + EXPECT_OK(target.status()); + EXPECT_EQ(static_cast<const Base2*>(*source), *target); +} + +TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) { + absl::StatusOr<Derived*> source(absl::CancelledError()); + absl::StatusOr<Base2*> target; target = source; EXPECT_EQ(target.status(), source.status()); } TEST(StatusOr, TestPointerStatus) { const int kI = 0; - StatusOr<const int*> good(&kI); + absl::StatusOr<const int*> good(&kI); EXPECT_TRUE(good.ok()); - StatusOr<const int*> bad(Status(absl::StatusCode::kCancelled, "")); - EXPECT_EQ(bad.status(), Status(absl::StatusCode::kCancelled, "")); + absl::StatusOr<const int*> bad(absl::CancelledError()); + EXPECT_EQ(bad.status().code(), absl::StatusCode::kCancelled); } TEST(StatusOr, TestPointerValue) { const int kI = 0; - StatusOr<const int*> thing(&kI); - EXPECT_EQ(&kI, thing.ValueOrDie()); + absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); } TEST(StatusOr, TestPointerValueConst) { const int kI = 0; - const StatusOr<const int*> thing(&kI); - EXPECT_EQ(&kI, thing.ValueOrDie()); + const absl::StatusOr<const int*> thing(&kI); + EXPECT_EQ(&kI, *thing); } -TEST(StatusOr, TestArrowOperator) { - StatusOr<std::unique_ptr<int>> uptr = ReturnUniquePtr(); - EXPECT_EQ(*uptr->get(), 0); +TEST(StatusOr, StatusOrVectorOfUniquePointerCanReserveAndResize) { + using EvilType = std::vector<std::unique_ptr<int>>; + static_assert(std::is_copy_constructible<EvilType>::value, ""); + std::vector<::absl::StatusOr<EvilType>> v(5); + v.reserve(v.capacity() + 10); + v.resize(v.capacity() + 10); } -TEST(StatusOr, TestArrowOperatorNotOk) { - StatusOr<Base1> error(Status(absl::StatusCode::kCancelled, "cancelled")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(error->pad_++, absl::Status, "cancelled"); -} +TEST(StatusOr, ConstPayload) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + absl::StatusOr<const int> a; -TEST(StatusOr, TestStarOperator) { - StatusOr<std::unique_ptr<int>> uptr = ReturnUniquePtr(); - EXPECT_EQ(**uptr, 0); -} + // Copy-construction + absl::StatusOr<const int> b(a); -TEST(StatusOr, TestStarOperatorDeath) { - StatusOr<Base1> error(Status(absl::StatusCode::kCancelled, "cancelled")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(*error, absl::Status, "cancelled"); -} + // Copy-assignment + EXPECT_FALSE(std::is_copy_assignable<absl::StatusOr<const int>>::value); -// NOTE(tucker): StatusOr does not support this kind -// of resize op. -// TEST(StatusOr, StatusOrVectorOfUniquePointerCanResize) { -// using EvilType = std::vector<std::unique_ptr<int>>; -// static_assert(std::is_copy_constructible<EvilType>::value, ""); -// std::vector<StatusOr<EvilType>> v(5); -// v.reserve(v.capacity() + 10); -// } + // Move-construction + absl::StatusOr<const int> c(std::move(a)); -TEST(StatusOrDeathTest, TestPointerValueNotOk) { - StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled"); + // Move-assignment + EXPECT_FALSE(std::is_move_assignable<absl::StatusOr<const int>>::value); } -TEST(StatusOrDeathTest, TestPointerValueNotOkConst) { - const StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled")); - ABSL_BASE_INTERNAL_EXPECT_FAIL(thing.ValueOrDie(), absl::Status, "cancelled"); -} +TEST(StatusOr, MapToStatusOrUniquePtr) { + // A reduced version of a problematic type found in the wild. All of the + // operations below should compile. + using MapType = std::map<std::string, absl::StatusOr<std::unique_ptr<int>>>; -static void AssertOkAndAssignBody(absl::StatusOr<int> consume) { - ASSERT_OK_AND_ASSIGN(int value, consume); - EXPECT_EQ(value, 1); -} + MapType a; -TEST(StatusOr, TestAssertOkAndAssign) { - const int kI = 1; - AssertOkAndAssignBody(kI); -} + // Move-construction + MapType b(std::move(a)); -TEST(StatusOrDeathTest, TestAssertOkAndAssignNotOk) { - // Can't actually test this, as calling ASSERT_TRUE fails the test. + // Move-assignment + a = std::move(b); } -static absl::Status AssignOrReturnBody(absl::StatusOr<int*> maybe) { - ASSIGN_OR_RETURN(int *iptr, maybe); - EXPECT_EQ(*iptr, 1); - *iptr = 4; - return OkStatus(); +TEST(StatusOr, ValueOrOk) { + const absl::StatusOr<int> status_or = 0; + EXPECT_EQ(status_or.value_or(-1), 0); } -TEST(StatusOr, TestAssignOrReturn) { - int i = 1; - EXPECT_TRUE(AssignOrReturnBody(&i).ok()); - EXPECT_EQ(i, 4); +TEST(StatusOr, ValueOrDefault) { + const absl::StatusOr<int> status_or = absl::CancelledError(); + EXPECT_EQ(status_or.value_or(-1), -1); } -TEST(StatusOr, TestAssignOrReturnNotOk) { - const StatusOr<int*> thing(Status(absl::StatusCode::kCancelled, "cancelled")); - const Status result = AssignOrReturnBody(thing); - EXPECT_FALSE(result.ok()); - EXPECT_EQ(result, thing.status()); +TEST(StatusOr, MoveOnlyValueOrOk) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::make_unique<int>(0)) + .value_or(absl::make_unique<int>(-1)), + Pointee(0)); } -static absl::Status ReturnIfErrorBody(absl::Status status, int* iptr) { - RETURN_IF_ERROR(status); - EXPECT_EQ(*iptr, 1); - *iptr = 4; - return OkStatus(); +TEST(StatusOr, MoveOnlyValueOrDefault) { + EXPECT_THAT(absl::StatusOr<std::unique_ptr<int>>(absl::CancelledError()) + .value_or(absl::make_unique<int>(-1)), + Pointee(-1)); } -TEST(StatusOr, TestReturnIfError) { - int i = 1; - EXPECT_TRUE(ReturnIfErrorBody(OkStatus(), &i).ok()); - EXPECT_EQ(i, 4); -} +static absl::StatusOr<int> MakeStatus() { return 100; } -TEST(StatusOr, TestReturnIfErrorNotOk) { - int i = 1; - Status thing(absl::StatusCode::kCancelled, ""); - EXPECT_FALSE(ReturnIfErrorBody(thing, &i).ok()); - EXPECT_EQ(i, 1); -} +TEST(StatusOr, TestIgnoreError) { MakeStatus().IgnoreError(); } -/* -static StatusOr<int> MakeStatus() { return 100; } +TEST(StatusOr, EqualityOperator) { + constexpr int kNumCases = 4; + std::array<absl::StatusOr<int>, kNumCases> group1 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + std::array<absl::StatusOr<int>, kNumCases> group2 = { + absl::StatusOr<int>(1), absl::StatusOr<int>(2), + absl::StatusOr<int>(absl::InvalidArgumentError("msg")), + absl::StatusOr<int>(absl::InternalError("msg"))}; + for (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]); + } + } + } +} -// A factory to help us benchmark the various factory styles. All of -// the factory methods are marked as non-inlineable so as to more -// accurately simulate calling a factory for which you do not have -// visibility of implementation. Similarly, the value_ variable is -// marked volatile to prevent the compiler from getting too clever -// about detecting that the same value is used in all loop iterations. -template <typename T> -class BenchmarkFactory { - public: - // Construct a new factory. Allocate an object which will always - // be the result of the factory methods. - BenchmarkFactory() : value_(new T) {} +struct MyType { + bool operator==(const MyType&) const { return true; } +}; - // Destroy this factory, including the result value. - ~BenchmarkFactory() { delete value_; } +enum class ConvTraits { kNone = 0, kImplicit = 1, kExplicit = 2 }; - // A trivial factory that just returns the value. There is no status - // object that could be returned to encapsulate an error - T* TrivialFactory() ABSL_ATTRIBUTE_NOINLINE { return value_; } +// This class has conversion operator to `StatusOr<T>` based on value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct StatusOrConversionBase {}; - // A more sophisticated factory, which returns a status to indicate - // the result of the operation. The factory result is populated into - // the user provided pointer result. - Status ArgumentFactory(T** result) ABSL_ATTRIBUTE_NOINLINE { - *result = value_; - return Status::OK(); +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kImplicit> { + operator absl::StatusOr<T>() const& { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); } - - Status ArgumentFactoryFail(T** result) ABSL_ATTRIBUTE_NOINLINE { - *result = nullptr; - return CancelledError(""); + operator absl::StatusOr<T>() && { // NOLINT + return absl::InvalidArgumentError("conversion to absl::StatusOr"); } +}; - Status ArgumentFactoryFailShortMsg(T** result) ABSL_ATTRIBUTE_NOINLINE { - *result = nullptr; - return InternalError(""); +template <typename T> +struct StatusOrConversionBase<T, ConvTraits::kExplicit> { + explicit operator absl::StatusOr<T>() const& { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); } - - Status ArgumentFactoryFailLongMsg(T** result) ABSL_ATTRIBUTE_NOINLINE { - *result = nullptr; - return InternalError(, - "a big string of message junk that will never be read"); + explicit operator absl::StatusOr<T>() && { + return absl::InvalidArgumentError("conversion to absl::StatusOr"); } +}; - // A factory that returns a StatusOr<T*>. If the factory operation - // is OK, then the StatusOr<T*> will hold a T*. Otherwise, it will - // hold a status explaining the error. - StatusOr<T*> StatusOrFactory() ABSL_ATTRIBUTE_NOINLINE { - return static_cast<T*>(value_); - } +// This class has conversion operator to `T` based on the value of +// `conv_traits`. +template <typename T, ConvTraits conv_traits = ConvTraits::kNone> +struct ConversionBase {}; - StatusOr<T*> StatusOrFactoryFail() ABSL_ATTRIBUTE_NOINLINE { - return CancelledError(""); - } +template <typename T> +struct ConversionBase<T, ConvTraits::kImplicit> { + operator T() const& { return t; } // NOLINT + operator T() && { return std::move(t); } // NOLINT + T t; +}; - StatusOr<T*> StatusOrFactoryFailShortMsg() ABSL_ATTRIBUTE_NOINLINE { - return InternalError("i"); - } +template <typename T> +struct ConversionBase<T, ConvTraits::kExplicit> { + explicit operator T() const& { return t; } + explicit operator T() && { return std::move(t); } + T t; +}; - StatusOr<T*> StatusOrFactoryFailLongMsg() ABSL_ATTRIBUTE_NOINLINE { - return InternalError( - "a big string of message junk that will never be read"); - } +// This class has conversion operator to `absl::Status` based on the value of +// `conv_traits`. +template <ConvTraits conv_traits = ConvTraits::kNone> +struct StatusConversionBase {}; - private: - T* volatile value_; - ABSL_DISALLOW_COPY_AND_ASSIGN(BenchmarkFactory); +template <> +struct StatusConversionBase<ConvTraits::kImplicit> { + operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } }; -// A simple type we use with the factory. -class BenchmarkType { - public: - BenchmarkType() {} - virtual ~BenchmarkType() {} - virtual void DoWork() ABSL_ATTRIBUTE_NOINLINE {} +template <> +struct StatusConversionBase<ConvTraits::kExplicit> { + explicit operator absl::Status() const& { // NOLINT + return absl::InternalError("conversion to Status"); + } + explicit operator absl::Status() && { // NOLINT + return absl::InternalError("conversion to Status"); + } +}; - private: - ABSL_DISALLOW_COPY_AND_ASSIGN(BenchmarkType); +static constexpr int kConvToStatus = 1; +static constexpr int kConvToStatusOr = 2; +static constexpr int kConvToT = 4; +static constexpr int kConvExplicit = 8; + +constexpr ConvTraits GetConvTraits(int bit, int config) { + return (config & bit) == 0 + ? ConvTraits::kNone + : ((config & kConvExplicit) == 0 ? ConvTraits::kImplicit + : ConvTraits::kExplicit); +} + +// This class conditionally has conversion operator to `absl::Status`, `T`, +// `StatusOr<T>`, based on values of the template parameters. +template <typename T, int config> +struct CustomType + : StatusOrConversionBase<T, GetConvTraits(kConvToStatusOr, config)>, + ConversionBase<T, GetConvTraits(kConvToT, config)>, + StatusConversionBase<GetConvTraits(kConvToStatus, config)> {}; + +struct ConvertibleToAnyStatusOr { + template <typename T> + operator absl::StatusOr<T>() const { // NOLINT + return absl::InvalidArgumentError("Conversion to absl::StatusOr"); + } }; -// Calibrate the amount of time spent just calling DoWork, since each of our -// tests will do this, we can subtract this out of benchmark results. -void BM_CalibrateWorkLoop(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - BenchmarkType* result = factory.TrivialFactory(); - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - if (result != nullptr) { - result->DoWork(); - } +// Test the rank of overload resolution for `StatusOr<T>` constructor and +// assignment, from highest to lowest: +// 1. T/Status +// 2. U that has conversion operator to absl::StatusOr<T> +// 3. U that is convertible to Status +// 4. U that is convertible to T +TEST(StatusOr, ConstructionFromT) { + // Construct absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor(v); + EXPECT_TRUE(statusor.ok()); + } + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Construct absl::StatusOr<T> from T when T is explicitly convertible to + // Status + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor( + v); + EXPECT_TRUE(statusor.ok()); + } + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<CustomType<MyType, kConvToStatus | kConvExplicit>> statusor = + v; + EXPECT_TRUE(statusor.ok()); } } -BENCHMARK(BM_CalibrateWorkLoop); -// Measure the time taken to call into the factory, return the value, -// determine that it is OK, and invoke a trivial function. -void BM_TrivialFactory(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - BenchmarkType* result = factory.TrivialFactory(); - if (result != nullptr) { - result->DoWork(); - } +// Construct absl::StatusOr<T> from U when U is explicitly convertible to T +TEST(StatusOr, ConstructionFromTypeConvertibleToT) { + { + CustomType<MyType, kConvToT | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_TRUE(statusor.ok()); } -} -BENCHMARK(BM_TrivialFactory); - -// Measure the time taken to call into the factory, providing an -// out-param for the result, evaluating the status result and the -// result pointer, and invoking the trivial function. -void BM_ArgumentFactory(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - BenchmarkType* result = nullptr; - Status status = factory.ArgumentFactory(&result); - if (status.ok() && result != nullptr) { - result->DoWork(); - } + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_TRUE(statusor.ok()); } } -BENCHMARK(BM_ArgumentFactory); -// Measure the time to use the StatusOr<T*> factory, evaluate the result, -// and invoke the trivial function. -void BM_StatusOrFactory(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - StatusOr<BenchmarkType*> result = factory.StatusOrFactory(); - if (result.ok()) { - result.ValueOrDie()->DoWork(); - } +// Construct absl::StatusOr<T> from U when U has explicit conversion operator to +// absl::StatusOr<T> +TEST(StatusOr, ConstructionFromTypeWithConversionOperatorToStatusOrT) { + { + CustomType<MyType, kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); } -} -BENCHMARK(BM_StatusOrFactory); - -// Measure the time taken to call into the factory, providing an -// out-param for the result, evaluating the status result and the -// result pointer, and invoking the trivial function. -void BM_ArgumentFactoryFail(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - BenchmarkType* result = nullptr; - Status status = factory.ArgumentFactoryFail(&result); - if (status.ok() && result != nullptr) { - result->DoWork(); - } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, + kConvToT | kConvToStatusOr | kConvToStatus | kConvExplicit> + v; + absl::StatusOr<MyType> statusor(v); + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); } } -BENCHMARK(BM_ArgumentFactoryFail); -// Measure the time to use the StatusOr<T*> factory, evaluate the result, -// and invoke the trivial function. -void BM_StatusOrFactoryFail(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFail(); - if (result.ok()) { - result.ValueOrDie()->DoWork(); - } +TEST(StatusOr, ConstructionFromTypeConvertibleToStatus) { + // Construction fails because conversion to `Status` is explicit. + { + CustomType<MyType, kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus | kConvExplicit> v; + absl::StatusOr<MyType> statusor(v); + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); } } -BENCHMARK(BM_StatusOrFactoryFail); - -// Measure the time taken to call into the factory, providing an -// out-param for the result, evaluating the status result and the -// result pointer, and invoking the trivial function. -void BM_ArgumentFactoryFailShortMsg(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - BenchmarkType* result = nullptr; - Status status = factory.ArgumentFactoryFailShortMsg(&result); - if (status.ok() && result != nullptr) { - result->DoWork(); - } + +TEST(StatusOr, AssignmentFromT) { + // Assign to absl::StatusOr<T> from T when T is convertible to + // absl::StatusOr<T> + { + ConvertibleToAnyStatusOr v; + absl::StatusOr<ConvertibleToAnyStatusOr> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); + } + // Assign to absl::StatusOr<T> from T when T is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<CustomType<MyType, kConvToStatus>> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); } } -BENCHMARK(BM_ArgumentFactoryFailShortMsg); -// Measure the time to use the StatusOr<T*> factory, evaluate the result, -// and invoke the trivial function. -void BM_StatusOrFactoryFailShortMsg(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailShortMsg(); - if (result.ok()) { - result.ValueOrDie()->DoWork(); - } +TEST(StatusOr, AssignmentFromTypeConvertibleToT) { + // Assign to absl::StatusOr<T> from U when U is convertible to T + { + CustomType<MyType, kConvToT> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_TRUE(statusor.ok()); } } -BENCHMARK(BM_StatusOrFactoryFailShortMsg); - -// Measure the time taken to call into the factory, providing an -// out-param for the result, evaluating the status result and the -// result pointer, and invoking the trivial function. -void BM_ArgumentFactoryFailLongMsg(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - BenchmarkType* result = nullptr; - Status status = factory.ArgumentFactoryFailLongMsg(&result); - if (status.ok() && result != nullptr) { - result->DoWork(); - } + +TEST(StatusOr, AssignmentFromTypeWithConversionOperatortoStatusOrT) { + // Assign to absl::StatusOr<T> from U when U has conversion operator to + // absl::StatusOr<T> + { + CustomType<MyType, kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); + } + { + CustomType<MyType, kConvToT | kConvToStatusOr | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_EQ(statusor, v.operator absl::StatusOr<MyType>()); } } -BENCHMARK(BM_ArgumentFactoryFailLongMsg); -// Measure the time to use the StatusOr<T*> factory, evaluate the result, -// and invoke the trivial function. -void BM_StatusOrFactoryFailLongMsg(int iters) { - tensorflow::testing::StopTiming(); - BenchmarkFactory<BenchmarkType> factory; - tensorflow::testing::StartTiming(); - for (int i = 0; i != iters; ++i) { - StatusOr<BenchmarkType*> result = factory.StatusOrFactoryFailLongMsg(); - if (result.ok()) { - result.ValueOrDie()->DoWork(); - } +TEST(StatusOr, AssignmentFromTypeConvertibleToStatus) { + // Assign to absl::StatusOr<T> from U when U is convertible to Status + { + CustomType<MyType, kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); + } + { + CustomType<MyType, kConvToT | kConvToStatus> v; + absl::StatusOr<MyType> statusor; + statusor = v; + EXPECT_FALSE(statusor.ok()); + EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); } } -BENCHMARK(BM_StatusOrFactoryFailLongMsg); -*/ } // namespace - -ABSL_NAMESPACE_END -} // namespace absl |