// Copyright 2022 The Pigweed 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. // These tests are a modified version of the tests for absl::StatusOr: // inclusive-language: disable // https://github.com/abseil/abseil-cpp/blob/master/absl/status/statusor_test.cc // inclusive-language: enable #include #include #include #include #include #include #include #include #include #include #include #include "gtest/gtest.h" #include "pw_result/result.h" namespace { #define EXPECT_OK(expression) EXPECT_EQ(::pw::OkStatus(), expression) #define ASSERT_OK(expression) ASSERT_EQ(::pw::OkStatus(), expression) 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; }; // Define custom macros instead of the CopyDetectorHas matcher. #define EXPECT_COPY_DETECTOR_HAS( \ value, expected_x, expected_moved, expected_copied) \ EXPECT_EQ(value.x, expected_x); \ EXPECT_EQ(value.moved, expected_moved); \ EXPECT_EQ(value.copied, expected_copied) #define EXPECT_OK_AND_COPY_DETECTOR_HAS( \ statusor_expr, expected_x, expected_moved, expected_copied) \ do { \ auto&& temp_status_or = statusor_expr; \ ASSERT_EQ(::pw::OkStatus(), temp_status_or.status()); \ EXPECT_COPY_DETECTOR_HAS( \ temp_status_or.value(), expected_x, expected_moved, expected_copied); \ } while (0) #define EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS( \ statusor_expr, expected_x, expected_moved, expected_copied) \ do { \ auto&& temp_status_or = statusor_expr; \ ASSERT_EQ(::pw::OkStatus(), temp_status_or.status()); \ const auto& temp_any_value = \ std::any_cast(temp_status_or.value()); \ EXPECT_COPY_DETECTOR_HAS( \ temp_any_value, expected_x, expected_moved, expected_copied); \ } while (0) class Base1 { public: virtual ~Base1() {} int pad; }; class Base2 { public: virtual ~Base2() {} int yetotherpad; }; class Derived : public Base1, public Base2 { public: ~Derived() override {} int evenmorepad; }; class CopyNoAssign { public: explicit CopyNoAssign(int value) : foo(value) {} CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {} const CopyNoAssign& operator=(const CopyNoAssign&) = delete; int foo; }; pw::Result> ReturnUniquePtr() { // Uses implicit constructor from T&& return std::make_unique(0); } TEST(Result, ElementType) { static_assert(std::is_same::value_type, int>()); static_assert(std::is_same::value_type, char>()); } TEST(Result, TestMoveOnlyInitialization) { pw::Result> 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(Result, TestMoveOnlyValueExtraction) { pw::Result> 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(Result, TestMoveOnlyInitializationFromTemporaryByValueOrDie) { std::unique_ptr ptr(*ReturnUniquePtr()); EXPECT_EQ(0, *ptr); } TEST(Result, TestValueOrDieOverloadForConstTemporary) { static_assert( std::is_same&&>().value())>(), "value() for const temporaries should return const T&&"); } TEST(Result, TestMoveOnlyConversion) { pw::Result> 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(Result, TestMoveOnlyVector) { // Check that pw::Result 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(pw::Status::Unknown(), another_vec[1].status()); } TEST(Result, TestDefaultCtor) { pw::Result thing; EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), pw::Status::Unknown().code()); } TEST(Result, StatusCtorForwards) { pw::Status status = pw::Status::Internal(); EXPECT_EQ(pw::Result(status).status(), pw::Status::Internal()); EXPECT_EQ(pw::Result(std::move(status)).status(), pw::Status::Internal()); } #define EXPECT_DEATH_OR_THROW(statement, status) \ EXPECT_DEATH_IF_SUPPORTED(statement, ".*"); TEST(ResultDeathTest, TestDefaultCtorValue) { pw::Result thing; EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Unknown()); const pw::Result thing2; EXPECT_DEATH_OR_THROW(thing2.value(), pw::Status::Unknown()); } TEST(ResultDeathTest, TestValueNotOk) { pw::Result thing(pw::Status::Cancelled()); EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Cancelled()); } TEST(ResultDeathTest, TestValueNotOkConst) { const pw::Result thing(pw::Status::Unknown()); EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Unknown()); } TEST(ResultDeathTest, TestPointerDefaultCtorValue) { pw::Result thing; EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Unknown()); } TEST(ResultDeathTest, TestPointerValueNotOk) { pw::Result thing(pw::Status::Cancelled()); EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Cancelled()); } TEST(ResultDeathTest, TestPointerValueNotOkConst) { const pw::Result thing(pw::Status::Cancelled()); EXPECT_DEATH_OR_THROW(thing.value(), pw::Status::Cancelled()); } #if GTEST_HAS_DEATH_TEST TEST(ResultDeathTest, TestStatusCtorStatusOk) { EXPECT_DEBUG_DEATH( { // This will DCHECK pw::Result thing(pw::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(), pw::Status::Internal().code()); }, ".*"); } TEST(ResultDeathTest, TestPointerStatusCtorStatusOk) { EXPECT_DEBUG_DEATH( { pw::Result thing(pw::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(), pw::Status::Internal().code()); }, ".*"); } #endif TEST(Result, ValueAccessor) { const int kIntValue = 110; { pw::Result status_or(kIntValue); EXPECT_EQ(kIntValue, status_or.value()); EXPECT_EQ(kIntValue, std::move(status_or).value()); } { pw::Result status_or(kIntValue); EXPECT_OK_AND_COPY_DETECTOR_HAS(status_or, kIntValue, false, false); CopyDetector copy_detector = status_or.value(); EXPECT_COPY_DETECTOR_HAS(copy_detector, kIntValue, false, true); copy_detector = std::move(status_or).value(); EXPECT_COPY_DETECTOR_HAS(copy_detector, kIntValue, true, false); } } TEST(Result, BadValueAccess) { const pw::Status kError = pw::Status::Cancelled(); pw::Result status_or(kError); EXPECT_DEATH_OR_THROW(status_or.value(), kError); } TEST(Result, TestStatusCtor) { pw::Result thing(pw::Status::Cancelled()); EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestValueCtor) { const int kI = 4; const pw::Result thing(kI); EXPECT_TRUE(thing.ok()); EXPECT_EQ(kI, *thing); } struct Foo { const int x; explicit Foo(int y) : x(y) {} }; TEST(Result, InPlaceConstruction) { pw::Result status_or(std::in_place, 10); ASSERT_TRUE(status_or.ok()); EXPECT_EQ(status_or->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(Result, InPlaceInitListConstruction) { pw::Result status_or( std::in_place, {10, 11, 12}, std::make_unique(13)); ASSERT_TRUE(status_or.ok()); ASSERT_EQ(status_or->x.size(), 3u); EXPECT_EQ(status_or->x[0], 10); EXPECT_EQ(status_or->x[1], 11); EXPECT_EQ(status_or->x[2], 12); EXPECT_EQ(*(status_or->y), 13); } TEST(Result, Emplace) { pw::Result status_or_foo(10); status_or_foo.emplace(20); ASSERT_TRUE(status_or_foo.ok()); EXPECT_EQ(status_or_foo->x, 20); status_or_foo = pw::Status::InvalidArgument(); EXPECT_FALSE(status_or_foo.ok()); EXPECT_EQ(status_or_foo.status().code(), pw::Status::InvalidArgument().code()); status_or_foo.emplace(20); ASSERT_TRUE(status_or_foo.ok()); EXPECT_EQ(status_or_foo->x, 20); } TEST(Result, EmplaceInitializerList) { pw::Result status_or( std::in_place, {10, 11, 12}, std::make_unique(13)); status_or.emplace({1, 2, 3}, std::make_unique(4)); ASSERT_TRUE(status_or.ok()); ASSERT_EQ(status_or->x.size(), 3u); EXPECT_EQ(status_or->x[0], 1); EXPECT_EQ(status_or->x[1], 2); EXPECT_EQ(status_or->x[2], 3); EXPECT_EQ(*(status_or->y), 4); status_or = pw::Status::InvalidArgument(); EXPECT_FALSE(status_or.ok()); EXPECT_EQ(status_or.status().code(), pw::Status::InvalidArgument().code()); status_or.emplace({1, 2, 3}, std::make_unique(4)); ASSERT_TRUE(status_or.ok()); ASSERT_EQ(status_or->x.size(), 3u); EXPECT_EQ(status_or->x[0], 1); EXPECT_EQ(status_or->x[1], 2); EXPECT_EQ(status_or->x[2], 3); EXPECT_EQ(*(status_or->y), 4); } TEST(Result, TestCopyCtorStatusOk) { const int kI = 4; const pw::Result original(kI); const pw::Result copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(*original, *copy); } TEST(Result, TestCopyCtorStatusNotOk) { pw::Result original(pw::Status::Cancelled()); pw::Result copy(original); EXPECT_EQ(copy.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestCopyCtorNonAssignable) { const int kI = 4; CopyNoAssign value(kI); pw::Result original(value); pw::Result copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(original->foo, copy->foo); } TEST(Result, TestCopyCtorStatusOKConverting) { const int kI = 4; pw::Result original(kI); pw::Result copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(*original, *copy); } TEST(Result, TestCopyCtorStatusNotOkConverting) { pw::Result original(pw::Status::Cancelled()); pw::Result copy(original); EXPECT_EQ(copy.status(), original.status()); } TEST(Result, TestAssignmentStatusOk) { // Copy assignmment { const auto p = std::make_shared(17); pw::Result> source(p); pw::Result> 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); pw::Result> source(p); pw::Result> target; target = std::move(source); ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(p, *target); ASSERT_TRUE(source.ok()); // NOLINT(bugprone-use-after-move) EXPECT_OK(source.status()); EXPECT_EQ(nullptr, *source); } } TEST(Result, TestAssignmentStatusNotOk) { // Copy assignment { const pw::Status expected = pw::Status::Cancelled(); pw::Result source(expected); pw::Result target; target = source; EXPECT_FALSE(target.ok()); EXPECT_EQ(expected, target.status()); EXPECT_FALSE(source.ok()); EXPECT_EQ(expected, source.status()); } // Move assignment { const pw::Status expected = pw::Status::Cancelled(); pw::Result source(expected); pw::Result target; target = std::move(source); EXPECT_FALSE(target.ok()); EXPECT_EQ(expected, target.status()); EXPECT_FALSE(source.ok()); // NOLINT(bugprone-use-after-move) // absl::Status sets itself to INTERNAL when moved, but pw::Status does not. // EXPECT_EQ(source.status().code(), pw::Status::Internal().code()); } } TEST(Result, TestAssignmentStatusOKConverting) { // Copy assignment { const int kI = 4; pw::Result source(kI); pw::Result target; target = source; ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(kI, *target); ASSERT_TRUE(source.ok()); EXPECT_OK(source.status()); EXPECT_EQ(kI, *source); } // Move assignment { const auto p = new int(17); pw::Result> source(p); pw::Result> target; target = std::move(source); ASSERT_TRUE(target.ok()); EXPECT_OK(target.status()); EXPECT_EQ(p, target->get()); ASSERT_TRUE(source.ok()); // NOLINT(bugprone-use-after-move) EXPECT_OK(source.status()); EXPECT_EQ(nullptr, source->get()); } } // implicit_cast template struct type_identity { using type = T; }; template constexpr To implicit_cast(typename type_identity::type to) { return to; } 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(Result, ImplicitConvertingConstructor) { auto status_or = implicit_cast>( pw::Result(A{11})); ASSERT_OK(status_or.status()); EXPECT_EQ(status_or->x, 11); EXPECT_TRUE(status_or->moved); pw::Result a(A{12}); auto status_or_2 = implicit_cast>(a); ASSERT_OK(status_or_2.status()); EXPECT_EQ(status_or_2->x, 12); EXPECT_FALSE(status_or_2->moved); } 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(Result, ExplicitConvertingConstructor) { EXPECT_FALSE( (std::is_convertible&, pw::Result>::value)); EXPECT_FALSE( (std::is_convertible&&, pw::Result>::value)); auto a1 = pw::Result(pw::Result(A{11})); ASSERT_OK(a1.status()); EXPECT_EQ(a1->x, 11); EXPECT_TRUE(a1->moved); pw::Result a(A{12}); auto a2 = pw::Result(a); ASSERT_OK(a2.status()); EXPECT_EQ(a2->x, 12); EXPECT_FALSE(a2->moved); } 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(Result, ImplicitBooleanConstructionWithImplicitCasts) { auto a = pw::Result(pw::Result(true)); ASSERT_OK(a.status()); EXPECT_TRUE(*a); auto b = pw::Result(pw::Result(false)); ASSERT_OK(b.status()); EXPECT_FALSE(*b); auto c = pw::Result(pw::Result(false)); ASSERT_OK(c.status()); EXPECT_EQ(c->x, false); EXPECT_FALSE( (std::is_convertible, pw::Result>::value)); } TEST(Result, BooleanConstructionWithImplicitCasts) { auto a = pw::Result(pw::Result(true)); ASSERT_OK(a.status()); EXPECT_TRUE(*a); auto b = pw::Result(pw::Result(false)); ASSERT_OK(b.status()); EXPECT_FALSE(*b); auto c = pw::Result{pw::Result(false)}; ASSERT_OK(c.status()); EXPECT_FALSE(c->x); auto d = pw::Result{ pw::Result(pw::Status::InvalidArgument())}; EXPECT_FALSE(d.ok()); auto e = pw::Result{ pw::Result(ConvertibleToBool{false})}; ASSERT_OK(e.status()); EXPECT_FALSE(e->x); auto f = pw::Result{ pw::Result(pw::Status::InvalidArgument())}; EXPECT_FALSE(f.ok()); } TEST(Result, ConstImplicitCast) { auto a = implicit_cast>(pw::Result(true)); ASSERT_OK(a.status()); EXPECT_TRUE(*a); auto b = implicit_cast>(pw::Result(false)); ASSERT_OK(b.status()); EXPECT_FALSE(*b); auto c = implicit_cast>(pw::Result(true)); ASSERT_OK(c.status()); EXPECT_TRUE(*c); auto d = implicit_cast>(pw::Result(false)); ASSERT_OK(d.status()); EXPECT_FALSE(*d); auto e = implicit_cast>( pw::Result("foo")); ASSERT_OK(e.status()); EXPECT_EQ(*e, "foo"); auto f = implicit_cast>( pw::Result("foo")); ASSERT_OK(f.status()); EXPECT_EQ(*f, "foo"); auto g = implicit_cast>>( pw::Result>( std::make_shared("foo"))); ASSERT_OK(g.status()); EXPECT_EQ(*(*g), "foo"); } TEST(Result, ConstExplicitConstruction) { auto a = pw::Result(pw::Result(true)); ASSERT_OK(a.status()); EXPECT_TRUE(*a); auto b = pw::Result(pw::Result(false)); ASSERT_OK(b.status()); EXPECT_FALSE(*b); auto c = pw::Result(pw::Result(true)); ASSERT_OK(c.status()); EXPECT_TRUE(*c); auto d = pw::Result(pw::Result(false)); ASSERT_OK(d.status()); EXPECT_FALSE(*d); } struct ExplicitConstructibleFromInt { int x; explicit ExplicitConstructibleFromInt(int y) : x(y) {} }; TEST(Result, ExplicitConstruction) { auto a = pw::Result(10); ASSERT_OK(a.status()); EXPECT_EQ(a->x, 10); } TEST(Result, ImplicitConstruction) { // Check implicit casting works. auto status_or = implicit_cast>>(10); ASSERT_OK(status_or.status()); EXPECT_EQ(std::get(*status_or), 10); } TEST(Result, ImplicitConstructionFromInitliazerList) { // Note: dropping the explicit std::initializer_list is not supported // by pw::Result or std::optional. auto status_or = implicit_cast>>({{10, 20, 30}}); ASSERT_OK(status_or.status()); ASSERT_EQ(status_or->size(), 3u); EXPECT_EQ((*status_or)[0], 10); EXPECT_EQ((*status_or)[1], 20); EXPECT_EQ((*status_or)[2], 30); } TEST(Result, UniquePtrImplicitConstruction) { auto status_or = implicit_cast>>( std::make_unique()); ASSERT_OK(status_or.status()); EXPECT_NE(status_or->get(), nullptr); } TEST(Result, NestedResultCopyAndMoveConstructorTests) { pw::Result> status_or = CopyDetector(10); pw::Result> status_error = pw::Status::InvalidArgument(); ASSERT_OK(status_or.status()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*status_or, 10, true, false); pw::Result> a = status_or; EXPECT_OK_AND_COPY_DETECTOR_HAS(*a, 10, false, true); pw::Result> a_err = status_error; EXPECT_FALSE(a_err.ok()); const pw::Result>& cref = status_or; pw::Result> b = cref; // NOLINT ASSERT_OK(b.status()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*b, 10, false, true); const pw::Result>& cref_err = status_error; pw::Result> b_err = cref_err; // NOLINT EXPECT_FALSE(b_err.ok()); pw::Result> c = std::move(status_or); ASSERT_OK(c.status()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*c, 10, true, false); pw::Result> c_err = std::move(status_error); EXPECT_FALSE(c_err.ok()); } TEST(Result, NestedResultCopyAndMoveAssignment) { pw::Result> status_or = CopyDetector(10); pw::Result> status_error = pw::Status::InvalidArgument(); pw::Result> a; a = status_or; ASSERT_TRUE(a.ok()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*a, 10, false, true); a = status_error; EXPECT_FALSE(a.ok()); const pw::Result>& cref = status_or; a = cref; ASSERT_TRUE(a.ok()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*a, 10, false, true); const pw::Result>& cref_err = status_error; a = cref_err; EXPECT_FALSE(a.ok()); a = std::move(status_or); ASSERT_TRUE(a.ok()); EXPECT_OK_AND_COPY_DETECTOR_HAS(*a, 10, true, false); a = std::move(status_error); EXPECT_FALSE(a.ok()); } 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(Result, 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(Result, ResultAnyCopyAndMoveConstructorTests) { pw::Result status_or = CopyDetector(10); pw::Result status_error = pw::Status::InvalidArgument(); EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(status_or, 10, true, false); pw::Result a = status_or; EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(a, 10, false, true); pw::Result a_err = status_error; EXPECT_FALSE(a_err.ok()); const pw::Result& cref = status_or; // No lint for no-change copy. pw::Result b = cref; // NOLINT EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(b, 10, false, true); const pw::Result& cref_err = status_error; // No lint for no-change copy. pw::Result b_err = cref_err; // NOLINT EXPECT_FALSE(b_err.ok()); pw::Result c = std::move(status_or); EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(c, 10, true, false); pw::Result c_err = std::move(status_error); EXPECT_FALSE(c_err.ok()); } TEST(Result, ResultAnyCopyAndMoveAssignment) { pw::Result status_or = CopyDetector(10); pw::Result status_error = pw::Status::InvalidArgument(); pw::Result a; a = status_or; EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(a, 10, false, true); a = status_error; EXPECT_FALSE(a.ok()); const pw::Result& cref = status_or; a = cref; EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(a, 10, false, true); const pw::Result& cref_err = status_error; a = cref_err; EXPECT_FALSE(a.ok()); a = std::move(status_or); EXPECT_OK_AND_ANY_WITH_COPY_DETECTOR_HAS(a, 10, true, false); a = std::move(status_error); EXPECT_FALSE(a.ok()); } TEST(Result, ResultCopyAndMoveTestsConstructor) { pw::Result status_or(10); EXPECT_OK_AND_COPY_DETECTOR_HAS(status_or, 10, false, false); pw::Result a(status_or); EXPECT_OK_AND_COPY_DETECTOR_HAS(a, 10, false, true); const pw::Result& cref = status_or; pw::Result b(cref); // NOLINT EXPECT_OK_AND_COPY_DETECTOR_HAS(b, 10, false, true); pw::Result c(std::move(status_or)); EXPECT_OK_AND_COPY_DETECTOR_HAS(c, 10, true, false); } TEST(Result, ResultCopyAndMoveTestsAssignment) { pw::Result status_or(10); EXPECT_OK_AND_COPY_DETECTOR_HAS(status_or, 10, false, false); pw::Result a; a = status_or; EXPECT_OK_AND_COPY_DETECTOR_HAS(a, 10, false, true); const pw::Result& cref = status_or; pw::Result b; b = cref; EXPECT_OK_AND_COPY_DETECTOR_HAS(b, 10, false, true); pw::Result c; c = std::move(status_or); EXPECT_OK_AND_COPY_DETECTOR_HAS(c, 10, true, false); } TEST(Result, StdAnyAssignment) { EXPECT_FALSE( (std::is_assignable, pw::Result>::value)); pw::Result status_or; status_or = pw::Status::InvalidArgument(); EXPECT_FALSE(status_or.ok()); } TEST(Result, ImplicitAssignment) { pw::Result> status_or; status_or = 10; ASSERT_OK(status_or.status()); EXPECT_EQ(std::get(*status_or), 10); } TEST(Result, SelfDirectInitAssignment) { pw::Result> status_or = {{10, 20, 30}}; status_or = *status_or; ASSERT_OK(status_or.status()); ASSERT_EQ(status_or->size(), 3u); EXPECT_EQ((*status_or)[0], 10); EXPECT_EQ((*status_or)[1], 20); EXPECT_EQ((*status_or)[2], 30); } TEST(Result, ImplicitCastFromInitializerList) { pw::Result> status_or = {{10, 20, 30}}; ASSERT_OK(status_or.status()); ASSERT_EQ(status_or->size(), 3u); EXPECT_EQ((*status_or)[0], 10); EXPECT_EQ((*status_or)[1], 20); EXPECT_EQ((*status_or)[2], 30); } TEST(Result, UniquePtrImplicitAssignment) { pw::Result> status_or; status_or = std::make_unique(); ASSERT_OK(status_or.status()); EXPECT_NE(status_or->get(), nullptr); } TEST(Result, Pointer) { struct Base {}; struct B : public Base {}; struct C : private Base {}; 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(Result, TestAssignmentStatusNotOkConverting) { // Copy assignment { const pw::Status expected = pw::Status::Cancelled(); pw::Result source(expected); pw::Result target; target = source; EXPECT_FALSE(target.ok()); EXPECT_EQ(expected, target.status()); EXPECT_FALSE(source.ok()); EXPECT_EQ(expected, source.status()); } // Move assignment { const pw::Status expected = pw::Status::Cancelled(); pw::Result source(expected); pw::Result target; target = std::move(source); EXPECT_FALSE(target.ok()); EXPECT_EQ(expected, target.status()); EXPECT_FALSE(source.ok()); // NOLINT(bugprone-use-after-move) // absl::Status sets itself to INTERNAL when moved, but pw::Status does not. // EXPECT_EQ(source.status().code(), pw::Status::Internal().code()); } } TEST(Result, 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'); pw::Result so = long_str; so = *&so; ASSERT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(long_str, *so); } // Copy-assignment, error status { pw::Result so = pw::Status::NotFound(); so = *&so; EXPECT_FALSE(so.ok()); EXPECT_EQ(so.status().code(), pw::Status::NotFound().code()); } // Move-assignment with copyable type, status OK { pw::Result 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 { pw::Result so = pw::Status::NotFound(); // Fool the compiler, which otherwise complains. auto& same = so; so = std::move(same); EXPECT_FALSE(so.ok()); EXPECT_EQ(so.status().code(), pw::Status::NotFound().code()); } // Move-assignment with non-copyable type, status OK { const auto raw = new int(17); pw::Result> so = std::unique_ptr(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 { pw::Result> so = pw::Status::NotFound(); // Fool the compiler, which otherwise complains. auto& same = so; so = std::move(same); EXPECT_FALSE(so.ok()); EXPECT_EQ(so.status().code(), pw::Status::NotFound().code()); } } // 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 `Result`. // `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(Result, PerfectForwardingAssignment) { // U == T constexpr int kValue1 = 10, kValue2 = 20; pw::Result status_or; CopyDetector lvalue(kValue1); status_or = lvalue; EXPECT_OK_AND_COPY_DETECTOR_HAS(status_or, kValue1, false, true); status_or = CopyDetector(kValue2); EXPECT_OK_AND_COPY_DETECTOR_HAS(status_or, 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)); pw::Result 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); pw::Result 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); pw::Result from_implicit_constructible( FromImplicitConstructibleOnly{}); EXPECT_FALSE(from_implicit_constructible->from_rvalue); EXPECT_FALSE(from_implicit_constructible->assigned); // construct a temporary `Result` object and invoke the `Result` move // assignment operator. from_implicit_constructible = FromImplicitConstructibleOnly{}; EXPECT_FALSE(from_implicit_constructible->from_rvalue); EXPECT_FALSE(from_implicit_constructible->assigned); } TEST(Result, TestStatus) { pw::Result good(4); EXPECT_TRUE(good.ok()); pw::Result bad(pw::Status::Cancelled()); EXPECT_FALSE(bad.ok()); EXPECT_EQ(bad.status().code(), pw::Status::Cancelled().code()); } TEST(Result, 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(Result, OperatorStar) { const pw::Result const_lvalue("hello"); EXPECT_EQ("hello", *const_lvalue); pw::Result lvalue("hello"); EXPECT_EQ("hello", *lvalue); // Note: Recall that std::move() is equivalent to a static_cast to an rvalue // reference type. const pw::Result const_rvalue("hello"); EXPECT_EQ("hello", *std::move(const_rvalue)); // NOLINT pw::Result rvalue("hello"); EXPECT_EQ("hello", *std::move(rvalue)); } TEST(Result, OperatorArrowQualifiers) { static_assert( std::is_same< const int*, decltype(std::declval&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same< const int*, decltype(std::declval&&>().operator->())>(), "Unexpected qualifiers"); static_assert( std::is_same&&>().operator->())>(), "Unexpected qualifiers"); } TEST(Result, OperatorArrow) { const pw::Result const_lvalue("hello"); EXPECT_EQ(std::string("hello"), const_lvalue->c_str()); pw::Result lvalue("hello"); EXPECT_EQ(std::string("hello"), lvalue->c_str()); } TEST(Result, RValueStatus) { pw::Result so(pw::Status::NotFound()); const pw::Status s = std::move(so).status(); EXPECT_EQ(s.code(), pw::Status::NotFound().code()); // 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()); // absl::Status sets itself to INTERNAL when moved, but pw::Status does not. // EXPECT_EQ(so.status().code(), pw::Status::Internal().code()); } TEST(Result, TestValue) { const int kI = 4; pw::Result thing(kI); EXPECT_EQ(kI, *thing); } TEST(Result, TestValueConst) { const int kI = 4; const pw::Result thing(kI); EXPECT_EQ(kI, *thing); } TEST(Result, TestPointerDefaultCtor) { pw::Result thing; EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), pw::Status::Unknown().code()); } TEST(Result, TestPointerStatusCtor) { pw::Result thing(pw::Status::Cancelled()); EXPECT_FALSE(thing.ok()); EXPECT_EQ(thing.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestPointerValueCtor) { const int kI = 4; // Construction from a non-null pointer { pw::Result so(&kI); EXPECT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(&kI, *so); } // Construction from a null pointer constant { pw::Result 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; pw::Result so(p); EXPECT_TRUE(so.ok()); EXPECT_OK(so.status()); EXPECT_EQ(nullptr, *so); } } TEST(Result, TestPointerCopyCtorStatusOk) { const int kI = 0; pw::Result original(&kI); pw::Result copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(*original, *copy); } TEST(Result, TestPointerCopyCtorStatusNotOk) { pw::Result original(pw::Status::Cancelled()); pw::Result copy(original); EXPECT_EQ(copy.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestPointerCopyCtorStatusOKConverting) { Derived derived; pw::Result original(&derived); pw::Result copy(original); EXPECT_OK(copy.status()); EXPECT_EQ(static_cast(*original), *copy); } TEST(Result, TestPointerCopyCtorStatusNotOkConverting) { pw::Result original(pw::Status::Cancelled()); pw::Result copy(original); EXPECT_EQ(copy.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestPointerAssignmentStatusOk) { const int kI = 0; pw::Result source(&kI); pw::Result target; target = source; EXPECT_OK(target.status()); EXPECT_EQ(*source, *target); } TEST(Result, TestPointerAssignmentStatusNotOk) { pw::Result source(pw::Status::Cancelled()); pw::Result target; target = source; EXPECT_EQ(target.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestPointerAssignmentStatusOKConverting) { Derived derived; pw::Result source(&derived); pw::Result target; target = source; EXPECT_OK(target.status()); EXPECT_EQ(static_cast(*source), *target); } TEST(Result, TestPointerAssignmentStatusNotOkConverting) { pw::Result source(pw::Status::Cancelled()); pw::Result target; target = source; EXPECT_EQ(target.status(), source.status()); } TEST(Result, TestPointerStatus) { const int kI = 0; pw::Result good(&kI); EXPECT_TRUE(good.ok()); pw::Result bad(pw::Status::Cancelled()); EXPECT_EQ(bad.status().code(), pw::Status::Cancelled().code()); } TEST(Result, TestPointerValue) { const int kI = 0; pw::Result thing(&kI); EXPECT_EQ(&kI, *thing); } TEST(Result, TestPointerValueConst) { const int kI = 0; const pw::Result thing(&kI); EXPECT_EQ(&kI, *thing); } TEST(Result, ResultVectorOfUniquePointerCanReserveAndResize) { using EvilType = std::vector>; static_assert(std::is_copy_constructible::value); std::vector<::pw::Result> v(5); v.reserve(v.capacity() + 10); v.resize(v.capacity() + 10); } TEST(Result, ConstPayload) { // A reduced version of a problematic type found in the wild. All of the // operations below should compile. pw::Result a; // Copy-construction pw::Result b(a); // Copy-assignment EXPECT_FALSE(std::is_copy_assignable>::value); // Move-construction pw::Result c(std::move(a)); // Move-assignment EXPECT_FALSE(std::is_move_assignable>::value); } TEST(Result, MapToResultUniquePtr) { // 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(Result, ValueOrOk) { const pw::Result status_or = 0; EXPECT_EQ(status_or.value_or(-1), 0); } TEST(Result, ValueOrDefault) { const pw::Result status_or = pw::Status::Cancelled(); EXPECT_EQ(status_or.value_or(-1), -1); } TEST(Result, MoveOnlyValueOrOk) { pw::Result> status_or = std::make_unique(0); ASSERT_TRUE(status_or.ok()); auto value = std::move(status_or).value_or(std::make_unique(-1)); EXPECT_EQ(*value, 0); } TEST(Result, MoveOnlyValueOrDefault) { pw::Result> status_or(pw::Status::Cancelled()); ASSERT_FALSE(status_or.ok()); auto value = std::move(status_or).value_or(std::make_unique(-1)); EXPECT_EQ(*value, -1); } static pw::Result MakeStatus() { return 100; } TEST(Result, TestIgnoreError) { MakeStatus().IgnoreError(); } TEST(Result, EqualityOperator) { constexpr int kNumCases = 4; std::array, kNumCases> group1 = { pw::Result(1), pw::Result(2), pw::Result(pw::Status::InvalidArgument()), pw::Result(pw::Status::Internal())}; std::array, kNumCases> group2 = { pw::Result(1), pw::Result(2), pw::Result(pw::Status::InvalidArgument()), pw::Result(pw::Status::Internal())}; 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 `Result` based on value of // `conv_traits`. template struct ResultConversionBase {}; template struct ResultConversionBase { operator pw::Result() const& { // NOLINT return pw::Status::InvalidArgument(); } operator pw::Result() && { // NOLINT return pw::Status::InvalidArgument(); } }; template struct ResultConversionBase { explicit operator pw::Result() const& { return pw::Status::InvalidArgument(); } explicit operator pw::Result() && { return pw::Status::InvalidArgument(); } }; // 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 `pw::Status` based on the value of // `conv_traits`. template struct StatusConversionBase {}; template <> struct StatusConversionBase { operator pw::Status() const& { // NOLINT return pw::Status::Internal(); } operator pw::Status() && { // NOLINT return pw::Status::Internal(); } }; template <> struct StatusConversionBase { explicit operator pw::Status() const& { // NOLINT return pw::Status::Internal(); } explicit operator pw::Status() && { // NOLINT return pw::Status::Internal(); } }; static constexpr int kConvToStatus = 1; static constexpr int kConvToResult = 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 `pw::Status`, `T`, // `Result`, based on values of the template parameters. template struct CustomType : ResultConversionBase, ConversionBase, StatusConversionBase {}; struct ConvertibleToAnyResult { template operator pw::Result() const { // NOLINT return pw::Status::InvalidArgument(); } }; // Test the rank of overload resolution for `Result` constructor and // assignment, from highest to lowest: // 1. T/Status // 2. U that has conversion operator to pw::Result // 3. U that is convertible to Status // 4. U that is convertible to T TEST(Result, ConstructionFromT) { // Construct pw::Result from T when T is convertible to // pw::Result { ConvertibleToAnyResult v; pw::Result statusor(v); EXPECT_TRUE(statusor.ok()); } { ConvertibleToAnyResult v; pw::Result statusor = v; EXPECT_TRUE(statusor.ok()); } // Construct pw::Result from T when T is explicitly convertible to // Status { CustomType v; pw::Result> statusor(v); EXPECT_TRUE(statusor.ok()); } { CustomType v; pw::Result> statusor = v; EXPECT_TRUE(statusor.ok()); } } // Construct pw::Result from U when U is explicitly convertible to T TEST(Result, ConstructionFromTypeConvertibleToT) { { CustomType v; pw::Result statusor(v); EXPECT_TRUE(statusor.ok()); } { CustomType v; pw::Result statusor = v; EXPECT_TRUE(statusor.ok()); } } // Construct pw::Result from U when U has explicit conversion operator to // pw::Result TEST(Result, ConstructionFromTypeWithConversionOperatorToResultT) { { CustomType v; pw::Result statusor(v); EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor(v); EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor(v); EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor(v); EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } } TEST(Result, ConstructionFromTypeConvertibleToStatus) { // Construction fails because conversion to `Status` is explicit. { CustomType v; pw::Result statusor(v); EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; pw::Result statusor(v); EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; pw::Result statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; pw::Result statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } } TEST(Result, AssignmentFromT) { // Assign to pw::Result from T when T is convertible to // pw::Result { ConvertibleToAnyResult v; pw::Result statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } // Assign to pw::Result from T when T is convertible to Status { CustomType v; pw::Result> statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } } TEST(Result, AssignmentFromTypeConvertibleToT) { // Assign to pw::Result from U when U is convertible to T { CustomType v; pw::Result statusor; statusor = v; EXPECT_TRUE(statusor.ok()); } } TEST(Result, AssignmentFromTypeWithConversionOperatortoResultT) { // Assign to pw::Result from U when U has conversion operator to // pw::Result { CustomType v; pw::Result statusor; statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor; statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor; statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } { CustomType v; pw::Result statusor; statusor = v; EXPECT_EQ(statusor, v.operator pw::Result()); } } TEST(Result, AssignmentFromTypeConvertibleToStatus) { // Assign to pw::Result from U when U is convertible to Status { CustomType v; pw::Result statusor; statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } { CustomType v; pw::Result statusor; statusor = v; EXPECT_FALSE(statusor.ok()); EXPECT_EQ(statusor.status(), static_cast(v)); } } } // namespace