refcnt: + unit test

This commit is contained in:
Roland Conybeare 2023-09-24 12:56:22 -04:00
commit f8ca4dbe09
4 changed files with 374 additions and 0 deletions

60
utest/CMakeLists.txt Normal file
View file

@ -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

7
utest/README Normal file
View file

@ -0,0 +1,7 @@
* to run unit tests for this directoyr
$ cd path/to/kalman/build
$ ./refcnt/utest/utest.refcnt

View file

@ -0,0 +1,301 @@
/* @file intrusive_ptr.test.cpp */
#include "refcnt/Refcounted.hpp"
#include "indentlog/scope.hpp"
#include "catch2/catch.hpp"
#include <string_view>
#include <type_traits>
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<ref::Refcount>() == true);
REQUIRE(std::has_virtual_destructor<ref::Refcount>() == 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<JustRefcount>() == true);
rp<JustRefcount> p1;
rp<JustRefcount> 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<JustRefcount> 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<JustRefcount> p1_brw = p1.borrow();
brw<JustRefcount> 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<JustRefcount> pp = p1_brw.promote();
REQUIRE(p1.get() == pp.get());
/* comparisons */
REQUIRE(Borrow<JustRefcount>::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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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<JustRefcount> 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 */

View file

@ -0,0 +1,6 @@
/* @file refcnt_utest_main.cpp */
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
/* end refcnt_utest_main.cpp */