/* @file intrusive_ptr.test.cpp */ #include "Refcounted.hpp" #include "xo/indentlog/scope.hpp" #include #include #include namespace xo { using xo::ref::Refcount; using xo::ref::Borrow; using xo::ref::rp; using xo::ref::brw; using xo::ref::intrusive_ptr_refcount; using xo::ref::intrusive_ptr_add_ref; using xo::ref::intrusive_ptr_release; namespace ut { namespace { static uint32_t ctor_count = 0; static uint32_t dtor_count = 0; /* empty object, except for refcount */ class JustRefcount : public ref::Refcount { public: JustRefcount() { ++ctor_count; } ~JustRefcount() { ++dtor_count; } }; /*JustRefcount*/ inline std::ostream & operator<<(std::ostream & os, JustRefcount & x) { os << "JustRefcount"; return os; } /*operator<<*/ } /*namespace*/ TEST_CASE("refcount", "[refcnt][trivial]") { REQUIRE(std::is_default_constructible() == true); REQUIRE(std::has_virtual_destructor() == true); /* refcount object self-initializes to 0 */ Refcount x; REQUIRE(x.reference_counter() == 0); } /*TEST_CASE(refcount)*/ TEST_CASE("null-intrusive-ptr", "[refcnt][trivial]") { //constexpr std::string_view c_self = "TEST_CASE:null-intrusive-ptr"; REQUIRE(std::has_virtual_destructor() == true); rp p1; rp p2; REQUIRE(sizeof(p1) == sizeof(JustRefcount*)); REQUIRE(p1.get() == nullptr); REQUIRE(p1.operator->() == nullptr); REQUIRE(p2.get() == nullptr); REQUIRE(p2.operator->() == nullptr); /* can assign a nullptr */ rp p3; REQUIRE(p3.get() == nullptr); p3 = p1; REQUIRE(p3.get() == nullptr); /* can use aux functions on null pointers */ REQUIRE(intrusive_ptr_refcount(p1.get()) == 0); intrusive_ptr_add_ref(nullptr); intrusive_ptr_release(nullptr); /* can borrow a null intrusive_ptr */ brw p1_brw = p1.borrow(); brw p2_brw = p2.borrow(); REQUIRE(p1_brw.get() == nullptr); REQUIRE(p1_brw.operator->() == nullptr); /* null borrow is false-y */ REQUIRE(p1_brw == false); /* can promote a borrowed pointer */ rp pp = p1_brw.promote(); REQUIRE(p1.get() == pp.get()); /* comparisons */ REQUIRE(Borrow::compare(p1_brw, p2_brw) == 0); REQUIRE(p1_brw == p2_brw); REQUIRE((p1_brw != p2_brw) == false); REQUIRE(p1 == p1_brw); REQUIRE((p1 != p1_brw) == false); REQUIRE(p1_brw == p1); REQUIRE((p1_brw != p1) == false); } /*TEST_CASE(null-intrusive_ptr)*/ TEST_CASE("intrusive-ptr-identity", "[refcnt][identity]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1.get() == p1.operator->()); REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); REQUIRE(p1->reference_counter() == 1); intrusive_ptr_add_ref(p1.get()); REQUIRE(intrusive_ptr_refcount(p1.get()) == 2); intrusive_ptr_release(p1.get()); REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); rp p2(new JustRefcount()); REQUIRE(ctor_count == cc + 2); REQUIRE(dtor_count == dc); REQUIRE(p2.get() != nullptr); REQUIRE(p2.get() != p1.get()); REQUIRE(p2.get() == p2.operator->()); REQUIRE(p2->reference_counter() == 1); /* can borrow a non-null intrusive-ptr */ brw p1_brw = p1.borrow(); REQUIRE(p1_brw.get() == p1.get()); /* borrowing does not change refcount, borrow not tracked */ REQUIRE(ctor_count == cc + 2); REQUIRE(dtor_count == dc); REQUIRE(p1.get()->reference_counter() == 1); /* copying borrowed pointer does not touch refcount */ brw p1_brw2 = p1_brw; REQUIRE(ctor_count == cc + 2); REQUIRE(dtor_count == dc); REQUIRE(p1_brw2.get() == p1.get()); REQUIRE(p1.get()->reference_counter() == 1); } /*TEST_CASE(identity-intrusive-ptr)*/ TEST_CASE("intrusive-ptr-release", "[refcnt][release]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1->reference_counter() == 1); /* reference count going to 0 -> delete object */ p1 = nullptr; REQUIRE(p1.get() == nullptr); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc + 1); } /*TEST_CASE(intrusive-ptr-release)*/ TEST_CASE("intrusive-ptr-copy", "[refcnt][copy]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); JustRefcount * p1_native = p1.get(); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1->reference_counter() == 1); /* copy ctor ran to make copy of p1, did not allocate */ rp p2(p1); REQUIRE(p1->reference_counter() == 2); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); } /*TEST_CASE(intrusive-ptr-copy)*/ TEST_CASE("intrusive-ptr-move", "[refcnt][move]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); JustRefcount * p1_native = p1.get(); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1->reference_counter() == 1); rp p2{std::move(p1)}; REQUIRE(p2->reference_counter() == 1); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p2 = nullptr; REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc + 1); } /*TEST_CASE(intrusive-ptr-move)*/ TEST_CASE("instrusive-ptr-assign", "[refcnt][assign]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); JustRefcount * p1_native = p1.get(); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1->reference_counter() == 1); rp p2; REQUIRE(p2.get() == nullptr); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p2 = p1; REQUIRE(p2.get() == p1.get()); REQUIRE(p2->reference_counter() == 2); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p1 = nullptr; REQUIRE(p2->reference_counter() == 1); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p2 = nullptr; REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc + 1); } /*TEST_CASE(intrusive-ptr-assign)*/ TEST_CASE("intrusive-ptr-move-assign", "[refcnt][move-assign]") { uint32_t cc = ctor_count; uint32_t dc = dtor_count; rp p1(new JustRefcount()); JustRefcount * p1_native = p1.get(); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); REQUIRE(p1.get() != nullptr); REQUIRE(p1->reference_counter() == 1); rp p2; REQUIRE(p2.get() == nullptr); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p2 = std::move(p1); REQUIRE(p1.get() == nullptr); REQUIRE(p2.get() == p1_native); REQUIRE(p2->reference_counter() == 1); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p1 = nullptr; /*no-op*/ REQUIRE(p2->reference_counter() == 1); REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc); p2 = nullptr; REQUIRE(ctor_count == cc + 1); REQUIRE(dtor_count == dc + 1); } /*TEST_CASE(intrusive-ptr-move-assign)*/ } /*namespace ut*/ } /*namespace xo*/ /* end intrusive_ptr.test.cpp */