From e1e48612afd6c2e789dcd2bb8df035192c16cf20 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 11 May 2026 09:27:24 -0400 Subject: [PATCH] xo-gc stack: coverage improvement + related tidying --- .../xo/gc/detail/IAllocator_DX1Collector.hpp | 2 + src/gc/IAllocator_DX1Collector.cpp | 2 + utest/CMakeLists.txt | 1 + utest/Collector.test.cpp | 22 ++++ utest/MockCollector.test.cpp | 119 ++++++++++++++++++ utest/X1Collector.test.cpp | 12 +- 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 utest/MockCollector.test.cpp diff --git a/include/xo/gc/detail/IAllocator_DX1Collector.hpp b/include/xo/gc/detail/IAllocator_DX1Collector.hpp index f61d9fa9..9fd90bf9 100644 --- a/include/xo/gc/detail/IAllocator_DX1Collector.hpp +++ b/include/xo/gc/detail/IAllocator_DX1Collector.hpp @@ -80,8 +80,10 @@ namespace xo { void ** lhs_data, AGCObject * rhs_iface, void * rhs_data); +#ifdef OBSOLETE /** invoke destructor **/ static void destruct_data(DX1Collector & d); +#endif }; } /*namespace mm*/ diff --git a/src/gc/IAllocator_DX1Collector.cpp b/src/gc/IAllocator_DX1Collector.cpp index cf307e0f..7cc6add5 100644 --- a/src/gc/IAllocator_DX1Collector.cpp +++ b/src/gc/IAllocator_DX1Collector.cpp @@ -144,11 +144,13 @@ namespace xo { d.barrier_assign_aux(parent, lhs_iface, lhs_data, rhs_iface, rhs_data); } +#ifdef OBSOLETE void IAllocator_DX1Collector::destruct_data(DX1Collector & d) { d.~DX1Collector(); } +#endif } /*namespace mm*/ } /*namespace xo*/ diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 401371f6..f226762e 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -14,6 +14,7 @@ set(UTEST_SRCS DMockCollector.cpp ICollector_DMockCollector.cpp + MockCollector.test.cpp MlsTestutil.cpp GcosTestutil.cpp diff --git a/utest/Collector.test.cpp b/utest/Collector.test.cpp index eff2eda1..26d1416e 100644 --- a/utest/Collector.test.cpp +++ b/utest/Collector.test.cpp @@ -58,6 +58,7 @@ namespace xo { /* empty variant collector */ obj gc1; + REQUIRE(!gc1._has_null_vptr()); REQUIRE(!gc1); REQUIRE(gc1.iface() != nullptr); REQUIRE(gc1.data() == nullptr); @@ -150,6 +151,12 @@ namespace xo { /* typed collector -- repr known at compile time */ obj x1(&gc); + REQUIRE(!x1._has_null_vptr()); + REQUIRE(x1.iface()); + REQUIRE(x1.data()); + + x1._drop(); + REQUIRE(x1.iface()); REQUIRE(x1.data()); } @@ -182,6 +189,11 @@ namespace xo { REQUIRE(x1.iface()); REQUIRE(x1.data()); + + x1._drop(); + + REQUIRE(x1.iface()); + REQUIRE(x1.data()); } TEST_CASE("collector-x1-alloc", "[alloc2][gc]") @@ -238,6 +250,11 @@ namespace xo { bool catch_flag = false; REQUIRE(utest::AllocUtil::random_allocs(c_n_alloc, c_max_alloc_payload, catch_flag, &rng, x1alloc)); + + x1gc._drop(); + + REQUIRE(x1gc.iface()); + REQUIRE(x1gc.data()); } TEST_CASE("collector-x1-alloc2", "[alloc2][gc]") @@ -298,6 +315,11 @@ namespace xo { // just testing ability to work as a low-level allocator REQUIRE(utest::AllocUtil::random_allocs(c_n_alloc, c_max_alloc_payload, c_debug_flag, &rng, x1alloc)); + + x1gc._drop(); + + REQUIRE(x1gc.iface()); + REQUIRE(x1gc.data()); } namespace { diff --git a/utest/MockCollector.test.cpp b/utest/MockCollector.test.cpp new file mode 100644 index 00000000..9092d2b1 --- /dev/null +++ b/utest/MockCollector.test.cpp @@ -0,0 +1,119 @@ +/** @file MockCollector.test.cpp + * + * @author Roland Conybeare, May 2026 + **/ + +#include "MockCollector.hpp" +#include +#include +#include +#include +#include + +namespace ut { + using xo::scm::DInteger; + using xo::mm::ACollector; + using xo::mm::DMockCollector; + using xo::mm::MutationLogConfig; + using xo::mm::MutationLogStore; + using xo::mm::GCObjectStoreConfig; + using xo::mm::GCObjectStore; + using xo::mm::AGCObject; + using xo::mm::Generation; + using xo::mm::Role; + using xo::mm::X1VerifyStats; + using xo::mm::AAllocator; + using xo::mm::ArenaConfig; + using xo::mm::DArena; + using xo::facet::obj; + + // Gilding the lily here. + // The only reason to 'test' MockCollector is to suppress + // false positives on coverage reports. + // Don't care about MockCollector itself, but it also shows up + // in ICollector_Xfer, at least on the osx/clang toolchain + + TEST_CASE("MockCollector-1", "[MockCollector]") + { + // need to create a {gcos, mls} pair + + constexpr uint32_t c_space_z = 64*1024; + constexpr uint32_t c_n_gen = 1; + constexpr uint32_t c_n_survive = 0; + X1VerifyStats verify_stats; + GCObjectStoreConfig gcos_config{ArenaConfig() + .with_name("gcos-arena-name-notused") + .with_size(c_space_z) + .with_store_header_flag(true), + c_n_gen, + c_n_survive, + 64*1024 /*object_type_z*/, + false /*debug_flag*/}; + DArena report_arena{ArenaConfig().with_name("report-arena") + .with_size(64*1024) + .with_store_header_flag(true)}; + DArena error_arena{ArenaConfig().with_name("error-arena") + .with_size(64*1024) + .with_store_header_flag(true)}; + MutationLogConfig mls_config{c_n_gen, + 1024 /*mlog_z*/, + false /*mlog_enabled_flag*/, + false /*debug_flag*/}; + + GCObjectStore gcos{gcos_config, &verify_stats}; + MutationLogStore mls{mls_config, &gcos}; + DMockCollector mock(&mls, &gcos); + + auto mockgc = obj(&mock); + auto report_mm = obj(&report_arena); + auto error_mm = obj(&error_arena); + + REQUIRE(mockgc.reserved(Generation::g0(), Role::to_space()) == c_space_z); + + { + int dummy; + + REQUIRE(mockgc.locate_address(&dummy) == -1); + REQUIRE(mockgc.contains(Role::to_space(), &dummy) == false); + } + + { + obj rpt; + { + // stub + REQUIRE(mockgc.report_statistics(report_mm, error_mm, &rpt) == false); + REQUIRE(report_mm.allocated() == 0); + } + { + mockgc.report_object_types(report_mm, error_mm, &rpt); + REQUIRE(rpt); + rpt.reset(); + report_mm.clear(); + } + { + mockgc.report_object_ages(report_mm, error_mm, &rpt); + REQUIRE(rpt); + rpt.reset(); + report_mm.clear(); + } + } + + { + auto g0_from = gcos.from_space(Generation::g0()); + REQUIRE(g0_from); + auto g0_from_mm = obj(g0_from); + + // note: we don't need object types in gcos for this test, + // since we're not traversing the object graph + + auto x = DInteger::box(g0_from_mm, 42); + REQUIRE(x); + + auto x_copy = mockgc.alloc_copy((std::byte*)x.data()); + REQUIRE(x_copy); + } + } + +} /*namespace ut*/ + +/* end MockCollector.test.cpp */ diff --git a/utest/X1Collector.test.cpp b/utest/X1Collector.test.cpp index 421c4639..00999be6 100644 --- a/utest/X1Collector.test.cpp +++ b/utest/X1Collector.test.cpp @@ -119,7 +119,8 @@ namespace ut { DX1Collector gc(cfg); - CollectorTypeRegistry::instance().install_types(obj(&gc)); + CollectorTypeRegistry::instance() + .install_types(obj(&gc)); DArena * to_0 = nullptr; @@ -219,6 +220,9 @@ namespace ut { + sizeof(AllocHeader) + sizeof(DInteger) + sizeof(AllocHeader) + sizeof(DList))); + auto to0_commit_z = to_0->committed(); + auto to0_reserved_z = to_0->reserved(); + { { REQUIRE(x0_o.iface() != nullptr); @@ -285,6 +289,12 @@ namespace ut { REQUIRE((void*)l0_o.data()->head_.data() == (void*)x0_o.data()); REQUIRE((void*)l0_o.data()->rest_ == (void*)DList::_nil()); + Generation g0 = Generation::g0(); + REQUIRE(c_o.committed(g0, Role::to_space()) == to0_commit_z); + REQUIRE(c_o.reserved(g0, Role::to_space()) == to0_reserved_z); + REQUIRE(c_o.locate_address(x0_o.data()) >= 0); + REQUIRE(c_o.contains(Role::to_space(), x0_o.data())); + } catch (std::exception & ex) { std::cerr << "caught exception: " << ex.what() << std::endl; REQUIRE(false);