From f8ca4dbe096b50c9628ba7e64ce46fde5d304d04 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 12:56:22 -0400 Subject: [PATCH] refcnt: + unit test --- utest/CMakeLists.txt | 60 +++++++ utest/README | 7 + utest/intrusive_ptr.test.cpp | 301 +++++++++++++++++++++++++++++++++++ utest/refcnt_utest_main.cpp | 6 + 4 files changed, 374 insertions(+) create mode 100644 utest/CMakeLists.txt create mode 100644 utest/README create mode 100644 utest/intrusive_ptr.test.cpp create mode 100644 utest/refcnt_utest_main.cpp diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt new file mode 100644 index 00000000..74706592 --- /dev/null +++ b/utest/CMakeLists.txt @@ -0,0 +1,60 @@ +# build unittest 'refcnt/utest/utest.refcnt + +set(SELF_EXECUTABLE_NAME utest.refcnt) + +# These tests can use the Catch2-provided main +set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp) +add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) +xo_include_options(${SELF_EXECUTABLE_NAME}) + +add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) +target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) + +#target_link_libraries(${SELF_EXECUTABLE_NAME} PRIVATE Catch2::Catch2WithMain) + +# ---------------------------------------------------------------- +# generic project dependency + +# PROJECT_SOURCE_DIR: +# so we can for example write +# #include "indentlog/scope.hpp" +# from anywhere in the project +# PROJECT_BINARY_DIR: +# since version file will be in build directory, need that directory +# to also be included in compiler's include path +# +target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR}) + +# ---------------------------------------------------------------- +# internal dependencies: refcnt, ... + +target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) + +# ---------------------------------------------------------------- +# 3rd part dependency: catch2: + +find_package(Catch2 2 REQUIRED) + +# need this so that catch2/include appears in compile_commands.json, +# on which lsp integration relies. +# +# See also /nix/store/*-catch2-*/lib/cmake/Catch2/ParseAndAddCatchTests.cmake; +# commands here derived from ^ .cmake file +# +#find_path(CATCH_INCLUDE_DIR "catch2/catch.hpp") +#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC ${CATCH_INCLUDE_DIR}) + +# ---------------------------------------------------------------- +# make standard directories for std:: includes explicit +# so that +# (1) they appear in compile_commands.json. +# (2) clangd (run from emacs lsp-mode) can find them +# +if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + +# end CMakeLists.txt diff --git a/utest/README b/utest/README new file mode 100644 index 00000000..85cc27c2 --- /dev/null +++ b/utest/README @@ -0,0 +1,7 @@ +* to run unit tests for this directoyr + + $ cd path/to/kalman/build + $ ./refcnt/utest/utest.refcnt + + + \ No newline at end of file diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp new file mode 100644 index 00000000..d8d756e2 --- /dev/null +++ b/utest/intrusive_ptr.test.cpp @@ -0,0 +1,301 @@ +/* @file intrusive_ptr.test.cpp */ + +#include "refcnt/Refcounted.hpp" +#include "indentlog/scope.hpp" +#include "catch2/catch.hpp" +#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 */ diff --git a/utest/refcnt_utest_main.cpp b/utest/refcnt_utest_main.cpp new file mode 100644 index 00000000..327713b7 --- /dev/null +++ b/utest/refcnt_utest_main.cpp @@ -0,0 +1,6 @@ +/* @file refcnt_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end refcnt_utest_main.cpp */