Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ comment:
ignore:
- "CxxTestProps/**"
- "CxxTestUtils/**"
- "RTLTestRunApp/**"
- "RTLBenchmarkApp/**"
- "ReflectionTemplateLib/rtl/detail/src/RObjectConverters_string.cpp"
292 changes: 126 additions & 166 deletions RTLTestRunApp/src/RObjectTests/RObjectReflecting_stdUniquePtr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using namespace test_utils;
using namespace rtl;

// TODO: Refine these, remove moot cases after this-> UPDATE: MOVING DISALLOWED NOW.

namespace rtl::unit_test
{
TEST(RObject_reflecting_unique_ptr, clone_on__heap_stack)
Expand Down Expand Up @@ -53,19 +55,18 @@ namespace rtl::unit_test
auto view = robj.view<std::unique_ptr<int>>();
ASSERT_TRUE(view);

// get() - moves out the 'unique_ptr<int>' from 'robj'.
// 'robj' still remains alive and type-consistent, but now holds an empty unique_ptr
std::unique_ptr<int> uptr = view->get();
// get() - UPDATE: MOVING DISALLOWED NOW.
const std::unique_ptr<int>& uptr = view->get();
ASSERT_TRUE(uptr);

// RTL gave up the ownership after move-op.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);

// Access the moved-out value
EXPECT_EQ(*uptr, NUM);

int* ptr = uptr.release();
// Addresses must be same.
EXPECT_EQ(numPtr, ptr);
delete ptr; //RTL must not delete again, once 'robj' out of scope.
// this is compile error now.
//int* ptr = uptr.release();
}
// there must not be any crash.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
Expand All @@ -89,19 +90,13 @@ namespace rtl::unit_test
auto view = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view);

// get() - moves out the 'unique_ptr<int>' from 'robj'.
// 'robj' still remains alive and type-consistent, but now holds an empty unique_ptr
std::unique_ptr<Node> uptr = view->get();
// get() - UPDATE: MOVING DISALLOWED NOW.
const std::unique_ptr<Node>& uptr = view->get();
ASSERT_TRUE(uptr);
// RTL gave up the ownership after move-op.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
// Access the moved-out value
// RTL still owns it.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
// Access the value
EXPECT_EQ(uptr->data(), NUM);

Node* ptr = uptr.release();
// Addresses must be same.
EXPECT_TRUE(ptr->data() == NUM);
delete ptr; //RTL must not delete again, once 'robj' out of scope.
}
// there must not be any crash.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
Expand Down Expand Up @@ -144,9 +139,8 @@ namespace rtl::unit_test
// unique_ptr, ownership managed by RTL.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);

// A move from any view transfers ownership of the underlying object
// RObject remains alive and type-consistent, but now holds an empty unique_ptr
std::unique_ptr<int> uptr0 = view0->get();
// get() - UPDATE: MOVING DISALLOWED NOW.
const std::unique_ptr<int>& uptr0 = view0->get();
ASSERT_TRUE(uptr0);

// Access the moved-out value
Expand All @@ -155,191 +149,156 @@ namespace rtl::unit_test
// Verify the original pointer address matches
EXPECT_EQ(uptr0.get(), &valueNum);

// RTL gave up the ownership after move-op.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);

// RObject still exists with correct metadata, but the stored unique_ptr is now empty
ASSERT_FALSE(robj.isEmpty());

// Any subsequent view will still be obtainable
auto view3 = robj.view<std::unique_ptr<int>>();
ASSERT_TRUE(view3);

// But the unique_ptr inside is now empty due to the earlier move
std::unique_ptr<int> uptr3 = view3->get();
ASSERT_TRUE(uptr3 == nullptr);

// All earlier views now yield empty unique_ptrs as well- no dangling pointers, no UB
std::unique_ptr<int> uptr2 = view2->get();
ASSERT_TRUE(uptr3 == nullptr);

std::unique_ptr<int> uptr1 = view1->get();
ASSERT_TRUE(uptr3 == nullptr);

// Even reusing the moved-from view0 is safe- just returns empty
std::unique_ptr<int> uptr00 = view0->get();
ASSERT_TRUE(uptr00 == nullptr);
// RTL still owns it
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
}


TEST(RObject_reflecting_unique_ptr, init_with_lvalue)
{
constexpr const int NUM = 16238;
std::unique_ptr<Node> uptr = std::make_unique<Node>(NUM);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
// Reflect a move-only type directly into RObject
RObject robj = reflect(std::move(uptr));
ASSERT_FALSE(robj.isEmpty());

// RObject can transparently expose the pointee type
EXPECT_TRUE(robj.canViewAs<Node>());

auto nodeView = robj.view<Node>();
ASSERT_TRUE(nodeView);

const Node& node = nodeView->get();
EXPECT_EQ(node.data(), NUM);

// RObject can also reflect as the original move-only type
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Multiple independent views to the same stored object- all valid before a move
auto view0 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view0);

auto view1 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view1);
constexpr const int NUM = 16238;
std::unique_ptr<Node> uptr = std::make_unique<Node>(NUM);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
// Reflect a move-only type directly into RObject
RObject robj = reflect(std::move(uptr));
ASSERT_FALSE(robj.isEmpty());

auto view2 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view2);
// RObject can transparently expose the pointee type
EXPECT_TRUE(robj.canViewAs<Node>());

// unique_ptr, ownership managed by RTL.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
auto nodeView = robj.view<Node>();
ASSERT_TRUE(nodeView);

// A move from any view transfers ownership of the underlying object
// RObject remains alive and type-consistent, but now holds an empty unique_ptr
std::unique_ptr<Node> uptr0 = view0->get();
ASSERT_TRUE(uptr0);
const Node& node = nodeView->get();
EXPECT_EQ(node.data(), NUM);

// Access the moved-out value
EXPECT_EQ(uptr0->data(), NUM);
// RObject can also reflect as the original move-only type
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Multiple independent views to the same stored object- all valid before a move
auto view0 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view0);

// Verify the original pointer address matches
EXPECT_EQ(uptr0.get(), &node);
auto view1 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view1);

// RObject still exists with correct metadata, but the stored unique_ptr is now empty
ASSERT_FALSE(robj.isEmpty());
auto view2 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view2);

// RTL gave up the ownership after move-op.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
// Node still exists.
EXPECT_TRUE(Node::instanceCount() == 1);
// unique_ptr, ownership managed by RTL.
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);

// Any subsequent view will still be obtainable
auto view3 = robj.view<std::unique_ptr<Node>>();
ASSERT_TRUE(view3);
// UPDATE: MOVING DISALLOWED NOW.
// A move from any view transfers ownership of the underlying object
// RObject remains alive and type-consistent, but now holds an empty unique_ptr
const std::unique_ptr<Node>& uptr0 = view0->get();
ASSERT_TRUE(uptr0);

// But the unique_ptr inside is now empty due to the earlier move
std::unique_ptr<Node> uptr3 = view3->get();
ASSERT_TRUE(uptr3 == nullptr);
// Access the moved-out value
EXPECT_EQ(uptr0->data(), NUM);

// All earlier views now yield empty unique_ptrs as well- no dangling pointers, no UB
std::unique_ptr<Node> uptr2 = view2->get();
ASSERT_TRUE(uptr3 == nullptr);
// Verify the original pointer address matches
EXPECT_EQ(uptr0.get(), &node);

std::unique_ptr<Node> uptr1 = view1->get();
ASSERT_TRUE(uptr3 == nullptr);
// RObject still exists with correct metadata, but the stored unique_ptr is now empty
ASSERT_FALSE(robj.isEmpty());

// Even reusing the moved-from view0 is safe- just returns empty
std::unique_ptr<Node> uptr00 = view0->get();
ASSERT_TRUE(uptr00 == nullptr);
// RTL still owns it
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
// Node still exists.
EXPECT_TRUE(Node::instanceCount() == 1);
}
EXPECT_TRUE(Node::instanceCount() == 1);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
}
EXPECT_TRUE(Node::instanceCount() == 0);
EXPECT_TRUE(Node::assertResourcesReleased());
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
}



TEST(RObject_reflecting_unique_ptr, destructor_call__reflectecting_lvalue)
{
const int NUM = 24028;
std::unique_ptr<Node> nodePtr = std::make_unique<Node>(NUM);
RObject robj = reflect(nodePtr);
ASSERT_FALSE(robj.isEmpty());

// Check if RObject can reflect as `Node`
EXPECT_TRUE(robj.canViewAs<Node>());
{
auto view = robj.view<Node>();
ASSERT_TRUE(view);
const int NUM = 24028;
std::unique_ptr<Node> nodePtr = std::make_unique<Node>(NUM);
RObject robj = reflect(nodePtr);
ASSERT_FALSE(robj.isEmpty());

const Node& node = view->get();
EXPECT_EQ(node.data(), NUM);
// Ensure no copy is made for viewing.
EXPECT_TRUE(Node::instanceCount() == 1);
}
// Check if RObject can reflect as `shared_ptr<Node>`
EXPECT_FALSE(robj.canViewAs<std::shared_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::shared_ptr<Node>>();
ASSERT_FALSE(view);
}
// Check if RObject can reflect as `unique_ptr<Node>`
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::unique_ptr<Node>>();
EXPECT_TRUE(view);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() != 0);
// Check if RObject can reflect as `Node`
EXPECT_TRUE(robj.canViewAs<Node>());
{
auto view = robj.view<Node>();
ASSERT_TRUE(view);

const Node& node = view->get();
EXPECT_EQ(node.data(), NUM);
// Ensure no copy is made for viewing.
EXPECT_TRUE(Node::instanceCount() == 1);
}
// Check if RObject can reflect as `shared_ptr<Node>`
EXPECT_FALSE(robj.canViewAs<std::shared_ptr<Node>>());
{
std::unique_ptr<Node> movedOutPtr = view->get();
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::shared_ptr<Node>>();
EXPECT_FALSE(view);
}
// Check if RObject can reflect as `unique_ptr<Node>`
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::unique_ptr<Node>>();
EXPECT_TRUE(view);
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
EXPECT_TRUE(Node::instanceCount() == 1);
}
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
EXPECT_TRUE(Node::instanceCount() == 0);
EXPECT_TRUE(Node::assertResourcesReleased());
}
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
EXPECT_TRUE(Node::instanceCount() == 0);
EXPECT_TRUE(Node::assertResourcesReleased());
}


TEST(RObject_reflecting_unique_ptr, destructor_call__reflectecting_rvalue)
{
const int NUM = 28228;
RObject robj = reflect(std::make_unique<Node>(NUM));
ASSERT_FALSE(robj.isEmpty());

// Check if RObject can reflect as `Node`
EXPECT_TRUE(robj.canViewAs<Node>());
{
auto view = robj.view<Node>();
ASSERT_TRUE(view);
const int NUM = 28228;
RObject robj = reflect(std::make_unique<Node>(NUM));
ASSERT_FALSE(robj.isEmpty());

const Node& node = view->get();
EXPECT_EQ(node.data(), NUM);
// Ensure no copy is made for viewing.
EXPECT_TRUE(Node::instanceCount() == 1);
}
// Check if RObject can reflect as `shared_ptr<Node>`
EXPECT_FALSE(robj.canViewAs<std::shared_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::shared_ptr<Node>>();
ASSERT_FALSE(view);
}
// Check if RObject can reflect as `unique_ptr<Node>`
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::unique_ptr<Node>>();
EXPECT_TRUE(view);
ASSERT_TRUE(rtl::getRtlManagedHeapInstanceCount() != 0);
// Check if RObject can reflect as `Node`
EXPECT_TRUE(robj.canViewAs<Node>());
{
std::unique_ptr<Node> movedOutPtr = view->get();
auto view = robj.view<Node>();
ASSERT_TRUE(view);

const Node& node = view->get();
EXPECT_EQ(node.data(), NUM);
// Ensure no copy is made for viewing.
EXPECT_TRUE(Node::instanceCount() == 1);
}
// Check if RObject can reflect as `shared_ptr<Node>`
EXPECT_FALSE(robj.canViewAs<std::shared_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::shared_ptr<Node>>();
ASSERT_FALSE(view);
}
// Check if RObject can reflect as `unique_ptr<Node>`
EXPECT_TRUE(robj.canViewAs<std::unique_ptr<Node>>());
{
// Get a view of the view as `shared_ptr<int>`
auto view = robj.view<std::unique_ptr<Node>>();
EXPECT_TRUE(view);
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 1);
EXPECT_TRUE(Node::instanceCount() == 1);
}
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
EXPECT_TRUE(Node::instanceCount() == 0);
EXPECT_TRUE(Node::assertResourcesReleased());
}
EXPECT_TRUE(rtl::getRtlManagedHeapInstanceCount() == 0);
EXPECT_TRUE(Node::instanceCount() == 0);
EXPECT_TRUE(Node::assertResourcesReleased());
}


Expand All @@ -366,7 +325,8 @@ namespace rtl::unit_test
ASSERT_TRUE(view);
ASSERT_FALSE(robj.isEmpty());

std::unique_ptr<Node> uptrNode = std::move(view->get());
// UPDATE: MOVING DISALLOWED NOW.
const std::unique_ptr<Node>& uptrNode = view->get();
EXPECT_EQ(uptrNode->data(), NUM);
}
}
Expand Down
Loading