From e847ef8bbd5accd36a3287c686894418721c6798 Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Fri, 2 Nov 2018 17:29:15 -0700 Subject: handle/copyable: Implement copyable handle This is a generic handle type that holds a resource and uses RAII to call a user defined function when the resource is copied or destroyed. Tested: Built and run through unit tests. Change-Id: I3d23544b2e7c8d8c6686effc03b3b7433ea18bf5 Signed-off-by: William A. Kennington III --- test/handle/copyable.cpp | 347 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 test/handle/copyable.cpp (limited to 'test/handle/copyable.cpp') diff --git a/test/handle/copyable.cpp b/test/handle/copyable.cpp new file mode 100644 index 0000000..6ce210f --- /dev/null +++ b/test/handle/copyable.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include + +namespace stdplus +{ +namespace +{ + +static std::vector reffed; +static int stored_ref = 0; +static std::vector dropped; +static int stored_drop = 0; + +int ref(const int& i) +{ + reffed.push_back(i); + return i + 1; +} + +void drop(int&& i) +{ + dropped.push_back(std::move(i)); +} + +int ref(const int& i, std::string&, int& si) +{ + reffed.push_back(i); + // Make sure we can update the stored data + stored_ref = si++; + return i + 1; +} + +void drop(int&& i, std::string&, int& si) +{ + dropped.push_back(std::move(i)); + // Make sure we can update the stored data + stored_drop = si++; +} + +using SimpleHandle = Copyable::Handle; +using StoreHandle = Copyable::Handle; + +class CopyableHandleTest : public ::testing::Test +{ + protected: + void SetUp() + { + reffed.clear(); + dropped.clear(); + } + + void TearDown() + { + EXPECT_TRUE(reffed.empty()); + EXPECT_TRUE(dropped.empty()); + } +}; + +TEST_F(CopyableHandleTest, EmptyNoStorage) +{ + SimpleHandle h(std::nullopt); + EXPECT_FALSE(h); + EXPECT_THROW(h.value(), std::bad_optional_access); + h.reset(); + EXPECT_FALSE(h); + EXPECT_THROW(h.value(), std::bad_optional_access); +} + +TEST_F(CopyableHandleTest, EmptyWithStorage) +{ + auto maybeV = std::nullopt; + StoreHandle h(maybeV, "str", 5); + EXPECT_FALSE(h); + EXPECT_THROW(h.value(), std::bad_optional_access); + h.reset(maybeV); + EXPECT_FALSE(h); + EXPECT_THROW(h.value(), std::bad_optional_access); +} + +TEST_F(CopyableHandleTest, SimplePopulated) +{ + constexpr int expected = 3; + { + int val = expected; + SimpleHandle h(std::move(val)); + 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(CopyableHandleTest, OptionalPopulated) +{ + constexpr int expected = 3; + { + std::optional maybeVal{expected}; + SimpleHandle h(std::move(maybeVal)); + EXPECT_TRUE(h); + EXPECT_EQ(expected, *h); + EXPECT_EQ(expected, h.value()); + EXPECT_TRUE(dropped.empty()); + } + EXPECT_TRUE(reffed.empty()); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + { + const std::optional maybeVal{expected}; + SimpleHandle h(maybeVal); + EXPECT_TRUE(h); + EXPECT_EQ(expected + 1, *h); + EXPECT_EQ(expected + 1, h.value()); + EXPECT_EQ(std::vector{expected}, reffed); + reffed.clear(); + EXPECT_TRUE(dropped.empty()); + } + EXPECT_EQ(std::vector{expected + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, SimplePopulatedWithStorage) +{ + constexpr int expected = 3; + { + StoreHandle h(expected, std::string{"str"}, 5); + EXPECT_TRUE(h); + EXPECT_EQ(expected + 1, *h); + EXPECT_EQ(expected + 1, h.value()); + EXPECT_EQ(5, stored_ref); + EXPECT_EQ(std::vector{expected}, reffed); + reffed.clear(); + EXPECT_TRUE(dropped.empty()); + } + EXPECT_EQ(6, stored_drop); + EXPECT_EQ(std::vector{expected + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, 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_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, 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(CopyableHandleTest, ResetCopyPopulated) +{ + constexpr int expected = 3, expected2 = 10; + { + SimpleHandle h(int{expected}); + EXPECT_TRUE(reffed.empty()); + EXPECT_TRUE(dropped.empty()); + const std::optional maybe2{expected2}; + h.reset(maybe2); + EXPECT_TRUE(h); + EXPECT_EQ(expected2 + 1, *h); + EXPECT_EQ(expected2 + 1, h.value()); + EXPECT_EQ(std::vector{expected2}, reffed); + reffed.clear(); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + } + EXPECT_EQ(std::vector{expected2 + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, ResetCopyPopulatedWithStorage) +{ + constexpr int expected = 3, expected2 = 10; + { + StoreHandle h(int{expected}, "str", 5); + EXPECT_TRUE(dropped.empty()); + h.reset(expected2); + EXPECT_TRUE(h); + EXPECT_EQ(expected2 + 1, *h); + EXPECT_EQ(expected2 + 1, h.value()); + EXPECT_EQ(5, stored_ref); + EXPECT_EQ(std::vector{expected2}, reffed); + reffed.clear(); + EXPECT_EQ(6, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + } + EXPECT_EQ(7, stored_drop); + EXPECT_EQ(std::vector{expected2 + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, MoveConstructWithStorage) +{ + constexpr int expected = 3; + StoreHandle h1(int{expected}, "str", 5); + { + StoreHandle h2(std::move(h1)); + EXPECT_TRUE(dropped.empty()); + EXPECT_FALSE(h1); + EXPECT_THROW(h1.value(), std::bad_optional_access); + EXPECT_TRUE(h2); + EXPECT_EQ(expected, *h2); + EXPECT_EQ(expected, h2.value()); + } + EXPECT_EQ(5, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, 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_drop); + EXPECT_EQ(std::vector{expected2}, dropped); + dropped.clear(); + EXPECT_FALSE(h1); + EXPECT_THROW(h1.value(), std::bad_optional_access); + EXPECT_TRUE(h2); + EXPECT_EQ(expected, *h2); + EXPECT_EQ(expected, h2.value()); + } + EXPECT_EQ(5, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, CopyConstructSrcEmptyWithStorage) +{ + StoreHandle h1(std::nullopt, "str", 5); + StoreHandle h2(h1); +} + +TEST_F(CopyableHandleTest, CopyConstructWithStorage) +{ + constexpr int expected = 3; + StoreHandle h1(int{expected}, "str", 5); + StoreHandle h2(h1); + EXPECT_EQ(5, stored_ref); + EXPECT_EQ(std::vector{expected}, reffed); + reffed.clear(); + + h1.reset(); + EXPECT_EQ(5, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + h2.reset(); + EXPECT_EQ(6, stored_drop); + EXPECT_EQ(std::vector{expected + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, CopyAssignBothEmptyWithStorage) +{ + StoreHandle h1(std::nullopt, "str", 5); + StoreHandle h2(std::nullopt, "str", 10); + h2 = h1; +} + +TEST_F(CopyableHandleTest, CopyAssignSrcEmptyWithStorage) +{ + constexpr int expected = 3; + StoreHandle h1(std::nullopt, "str", 5); + StoreHandle h2(int{expected}, "str", 10); + h2 = h1; + EXPECT_EQ(10, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, CopyAssignDstEmptyWithStorage) +{ + constexpr int expected = 3; + StoreHandle h1(int{expected}, "str", 5); + StoreHandle h2(std::nullopt, "str", 10); + h2 = h1; + EXPECT_EQ(5, stored_ref); + EXPECT_EQ(std::vector{expected}, reffed); + reffed.clear(); + + h1.reset(); + EXPECT_EQ(5, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + h2.reset(); + EXPECT_EQ(6, stored_drop); + EXPECT_EQ(std::vector{expected + 1}, dropped); + dropped.clear(); +} + +TEST_F(CopyableHandleTest, CopyAssignWithStorage) +{ + constexpr int expected = 3, expected2 = 15; + StoreHandle h1(int{expected}, "str", 5); + StoreHandle h2(int{expected2}, "str", 10); + h2 = h1; + EXPECT_EQ(10, stored_drop); + EXPECT_EQ(std::vector{expected2}, dropped); + dropped.clear(); + EXPECT_EQ(5, stored_ref); + EXPECT_EQ(std::vector{expected}, reffed); + reffed.clear(); + + h1.reset(); + EXPECT_EQ(5, stored_drop); + EXPECT_EQ(std::vector{expected}, dropped); + dropped.clear(); + h2.reset(); + EXPECT_EQ(6, stored_drop); + EXPECT_EQ(std::vector{expected + 1}, dropped); + dropped.clear(); +} + +} // namespace +} // namespace stdplus -- cgit v1.2.3