| #include <stdplus/handle/managed.hpp> |
| |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| namespace stdplus |
| { |
| namespace |
| { |
| |
| static std::vector<int> dropped; |
| static int stored = 0; |
| |
| struct SimpleDrop |
| { |
| void operator()(int&& i) noexcept |
| { |
| dropped.push_back(std::move(i)); |
| } |
| }; |
| |
| void drop(int&& i) |
| { |
| SimpleDrop()(std::move(i)); |
| } |
| |
| struct StoreDrop |
| { |
| void operator()(int&& i, std::string&, int& si) |
| { |
| dropped.push_back(std::move(i)); |
| // Make sure we can update the stored data |
| stored = si++; |
| } |
| }; |
| |
| using SimpleHandleOld = Managed<int>::Handle<drop>; |
| using SimpleHandle = Managed<int>::HandleF<SimpleDrop>; |
| using StoreHandle = Managed<int, std::string, int>::HandleF<StoreDrop>; |
| |
| static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>); |
| static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>); |
| static_assert(std::is_nothrow_destructible_v<SimpleHandle>); |
| static_assert(noexcept(std::declval<SimpleHandle>().reset())); |
| // http://cplusplus.github.io/LWG/lwg-active.html#2116 |
| // static_assert(std::is_nothrow_move_constructible_v<StoreHandle>); |
| static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>); |
| static_assert(!std::is_nothrow_destructible_v<StoreHandle>); |
| static_assert(!noexcept(std::declval<StoreHandle>().reset())); |
| |
| class ManagedHandleTest : public ::testing::Test |
| { |
| protected: |
| void SetUp() |
| { |
| dropped.clear(); |
| } |
| |
| void TearDown() |
| { |
| EXPECT_TRUE(dropped.empty()); |
| } |
| }; |
| |
| TEST_F(ManagedHandleTest, EmptyNoStorage) |
| { |
| SimpleHandleOld h(std::nullopt); |
| EXPECT_FALSE(h); |
| EXPECT_THROW(h.value(), std::bad_optional_access); |
| EXPECT_THROW((void)h.release(), std::bad_optional_access); |
| h.reset(); |
| EXPECT_FALSE(h.has_value()); |
| EXPECT_THROW(h.value(), std::bad_optional_access); |
| EXPECT_THROW((void)h.release(), std::bad_optional_access); |
| EXPECT_EQ(std::nullopt, h.maybe_value()); |
| EXPECT_EQ(std::nullopt, h.maybe_release()); |
| } |
| |
| TEST_F(ManagedHandleTest, EmptyWithStorage) |
| { |
| StoreHandle h(std::nullopt, "str", 5); |
| EXPECT_FALSE(h); |
| EXPECT_THROW(h.value(), std::bad_optional_access); |
| h.reset(std::nullopt); |
| EXPECT_FALSE(h); |
| EXPECT_THROW(h.value(), std::bad_optional_access); |
| |
| StoreHandle h2(std::nullopt, std::make_tuple<std::string, int>("str", 5)); |
| EXPECT_FALSE(h2); |
| EXPECT_THROW(h2.value(), std::bad_optional_access); |
| } |
| |
| TEST_F(ManagedHandleTest, SimplePopulated) |
| { |
| constexpr int expected = 3; |
| { |
| int val = expected; |
| SimpleHandle h(std::move(val)); |
| EXPECT_TRUE(h.has_value()); |
| EXPECT_EQ(expected, *h); |
| EXPECT_EQ(expected, h.value()); |
| EXPECT_EQ(expected, h.maybe_value()); |
| EXPECT_TRUE(dropped.empty()); |
| } |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, OptionalPopulated) |
| { |
| constexpr int expected = 3; |
| { |
| std::optional<int> maybeVal{expected}; |
| SimpleHandle h(std::move(maybeVal)); |
| EXPECT_TRUE(h); |
| EXPECT_EQ(expected, *h); |
| EXPECT_EQ(expected, h.value()); |
| EXPECT_TRUE(dropped.empty()); |
| } |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, SimplePopulatedWithStorage) |
| { |
| constexpr int expected = 3; |
| { |
| StoreHandle h(int{expected}, std::make_tuple(std::string{"str"}, 5)); |
| EXPECT_TRUE(h); |
| EXPECT_EQ(expected, *h); |
| EXPECT_EQ(expected, h.value()); |
| EXPECT_TRUE(dropped.empty()); |
| } |
| EXPECT_EQ(5, stored); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, ResetPopulatedWithStorage) |
| { |
| constexpr int expected = 3; |
| const std::string s{"str"}; |
| StoreHandle h(int{expected}, s, 5); |
| EXPECT_TRUE(dropped.empty()); |
| h.reset(std::nullopt); |
| EXPECT_FALSE(h); |
| EXPECT_THROW(h.value(), std::bad_optional_access); |
| EXPECT_EQ(5, stored); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, ResetNewPopulated) |
| { |
| constexpr int expected = 3, expected2 = 10; |
| { |
| SimpleHandle h(int{expected}); |
| EXPECT_TRUE(dropped.empty()); |
| h.reset(int{expected2}); |
| EXPECT_TRUE(h); |
| EXPECT_EQ(expected2, *h); |
| EXPECT_EQ(expected2, h.value()); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| EXPECT_EQ(std::vector{expected2}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, ResetNewPopulatedWithStorage) |
| { |
| constexpr int expected = 3, expected2 = 10; |
| { |
| StoreHandle h(int{expected}, "str", 5); |
| EXPECT_TRUE(dropped.empty()); |
| h.reset(int{expected2}); |
| EXPECT_TRUE(h); |
| EXPECT_EQ(expected2, *h); |
| EXPECT_EQ(expected2, h.value()); |
| EXPECT_EQ(5, stored); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| EXPECT_EQ(6, stored); |
| EXPECT_EQ(std::vector{expected2}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, Release) |
| { |
| constexpr int expected = 3; |
| int val = expected; |
| SimpleHandle h(std::move(val)); |
| EXPECT_EQ(expected, h.release()); |
| EXPECT_FALSE(h); |
| } |
| |
| TEST_F(ManagedHandleTest, MaybeRelease) |
| { |
| constexpr int expected = 3; |
| int val = expected; |
| SimpleHandle h(std::move(val)); |
| EXPECT_EQ(expected, h.maybe_release()); |
| EXPECT_FALSE(h); |
| } |
| |
| TEST_F(ManagedHandleTest, MoveConstructWithStorage) |
| { |
| constexpr int expected = 3; |
| StoreHandle h1(int{expected}, "str", 5); |
| { |
| StoreHandle h2(std::move(h1)); |
| EXPECT_TRUE(dropped.empty()); |
| EXPECT_FALSE(h1); |
| // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move) |
| EXPECT_THROW(h1.value(), std::bad_optional_access); |
| EXPECT_TRUE(h2); |
| EXPECT_EQ(expected, *h2); |
| EXPECT_EQ(expected, h2.value()); |
| } |
| EXPECT_EQ(5, stored); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| TEST_F(ManagedHandleTest, MoveAssignWithStorage) |
| { |
| constexpr int expected = 3, expected2 = 10; |
| { |
| StoreHandle h1(int{expected}, "str", 5); |
| StoreHandle h2(int{expected2}, "str", 10); |
| EXPECT_TRUE(dropped.empty()); |
| |
| h2 = std::move(h1); |
| EXPECT_EQ(10, stored); |
| EXPECT_EQ(std::vector{expected2}, dropped); |
| dropped.clear(); |
| EXPECT_FALSE(h1); |
| // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move) |
| EXPECT_THROW(h1.value(), std::bad_optional_access); |
| EXPECT_TRUE(h2); |
| EXPECT_EQ(expected, *h2); |
| EXPECT_EQ(expected, h2.value()); |
| } |
| EXPECT_EQ(5, stored); |
| EXPECT_EQ(std::vector{expected}, dropped); |
| dropped.clear(); |
| } |
| |
| } // namespace |
| } // namespace stdplus |