xo-alloc2: + utest harness for catch2

accept additional commandline arguments
This commit is contained in:
Roland Conybeare 2026-05-19 08:27:10 -04:00
commit 908c4908c5
13 changed files with 241 additions and 9 deletions

View file

@ -4,12 +4,11 @@
set(UTEST_EXE utest.alloc2) set(UTEST_EXE utest.alloc2)
set(UTEST_SRCS set(UTEST_SRCS
alloc2_utest_main.cpp alloc2_utest_main.cpp
TestUtil.cpp
objectmodel.test.cpp objectmodel.test.cpp
arena.test.cpp arena.test.cpp
IAllocator_Any.test.cpp IAllocator_Any.test.cpp
DArenaIterator.test.cpp DArenaIterator.test.cpp
# Collector.test.cpp
# DX1CollectorIterator.test.cpp
Generation.test.cpp Generation.test.cpp
Role.test.cpp Role.test.cpp
VisitReason.test.cpp VisitReason.test.cpp

View file

@ -3,10 +3,11 @@
* @author Roland Conybeare, Dec 2025 * @author Roland Conybeare, Dec 2025
**/ **/
#include "Allocator.hpp" #include "TestUtil.hpp"
#include "AllocIterator.hpp" #include <xo/alloc2/Allocator.hpp>
#include "Arena.hpp" #include <xo/alloc2/AllocIterator.hpp>
//#include "arena/IAllocator_DArena.hpp" #include <xo/alloc2/Arena.hpp>
#include <xo/alloc2/ArenaIterator.hpp>
#include "arena/IAllocIterator_DArenaIterator.hpp" #include "arena/IAllocIterator_DArenaIterator.hpp"
#include "padding.hpp" #include "padding.hpp"
#include <xo/indentlog/scope.hpp> #include <xo/indentlog/scope.hpp>
@ -39,6 +40,8 @@ namespace xo {
namespace ut { namespace ut {
TEST_CASE("IAllocIterator_Xfer_DArenaIterator", "[alloc2]") TEST_CASE("IAllocIterator_Xfer_DArenaIterator", "[alloc2]")
{ {
auto log = Utest::ut_scope();
/* verify IAllocIterator_Xfer is constructible + satisfies concept checks */ /* verify IAllocIterator_Xfer is constructible + satisfies concept checks */
IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator> xfer; IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator> xfer;
REQUIRE(IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator>::_valid); REQUIRE(IAllocIterator_Xfer<DArenaIterator, IAllocIterator_DArenaIterator>::_valid);
@ -46,6 +49,8 @@ namespace xo {
TEST_CASE("IAllocIterator_Any", "[alloc2]") TEST_CASE("IAllocIterator_Any", "[alloc2]")
{ {
auto log = Utest::ut_scope();
/* verify IAllocIterator_Any is constructible + satisfies concept checks */ /* verify IAllocIterator_Any is constructible + satisfies concept checks */
IAllocIterator_Any any; IAllocIterator_Any any;
REQUIRE(IAllocIterator_Any::_valid); REQUIRE(IAllocIterator_Any::_valid);
@ -53,6 +58,8 @@ namespace xo {
TEST_CASE("obj_IAllocIterator", "[alloc2]") TEST_CASE("obj_IAllocIterator", "[alloc2]")
{ {
auto log = Utest::ut_scope();
/* verify variant obj constructible */ /* verify variant obj constructible */
obj<AAllocIterator> obj_any; obj<AAllocIterator> obj_any;
REQUIRE(obj_any.iface()); REQUIRE(obj_any.iface());
@ -61,6 +68,8 @@ namespace xo {
TEST_CASE("IAllocIterator-disabled-1", "[alloc2]") TEST_CASE("IAllocIterator-disabled-1", "[alloc2]")
{ {
auto log = Utest::ut_scope();
/* verify iteration over empty arena */ /* verify iteration over empty arena */
/* typed allocator a1o */ /* typed allocator a1o */
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
@ -97,6 +106,8 @@ namespace xo {
TEST_CASE("IAllocIterator-emptyarena", "[alloc2]") TEST_CASE("IAllocIterator-emptyarena", "[alloc2]")
{ {
auto log = Utest::ut_scope();
/* verify iteration over empty arena */ /* verify iteration over empty arena */
/* typed allocator a1o */ /* typed allocator a1o */
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
@ -153,7 +164,7 @@ namespace xo {
TEST_CASE("IAllocIterator-singlearena", "[alloc2]") TEST_CASE("IAllocIterator-singlearena", "[alloc2]")
{ {
scope log(XO_DEBUG(false)); auto log = Utest::ut_scope();
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 64*1024, .size_ = 64*1024,

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include "Generation.hpp" #include "Generation.hpp"
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
@ -14,6 +15,8 @@ namespace xo {
TEST_CASE("Generation-1", "[Generation]") TEST_CASE("Generation-1", "[Generation]")
{ {
auto log = Utest::ut_scope();
REQUIRE(Generation::nursery() == 0); REQUIRE(Generation::nursery() == 0);
REQUIRE(Generation::g0() == 0); REQUIRE(Generation::g0() == 0);
REQUIRE(Generation::g1() == 1); REQUIRE(Generation::g1() == 1);

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <sys/wait.h> #include <sys/wait.h>
@ -16,6 +17,8 @@ namespace xo {
TEST_CASE("IAllocator_Any", "[alloc2][death]") TEST_CASE("IAllocator_Any", "[alloc2][death]")
{ {
auto log = Utest::ut_scope();
// null allocator // null allocator
obj<AAllocator> alloc_any; obj<AAllocator> alloc_any;

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include <xo/alloc2/ResourceVisitor.hpp> #include <xo/alloc2/ResourceVisitor.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
@ -13,6 +14,8 @@ namespace xo {
TEST_CASE("ResourceVisitor-1", "[resourcevisitor]") TEST_CASE("ResourceVisitor-1", "[resourcevisitor]")
{ {
auto log = Utest::ut_scope();
obj<AResourceVisitor> v; obj<AResourceVisitor> v;
REQUIRE(v.iface()); REQUIRE(v.iface());

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include "role.hpp" #include "role.hpp"
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
@ -13,6 +14,8 @@ namespace xo {
TEST_CASE("Role-1", "[Role]") TEST_CASE("Role-1", "[Role]")
{ {
auto log = Utest::ut_scope();
/* 1. there are two distinct valid roles, 'to' and 'from', /* 1. there are two distinct valid roles, 'to' and 'from',
* 2. valid roles fall in interval [begin, end) * 2. valid roles fall in interval [begin, end)
*/ */

24
utest/TestUtil.cpp Normal file
View file

@ -0,0 +1,24 @@
/** @file TestUtil.cpp
*
* @author Roland Conybeare, May 2026
**/
#include "TestUtil.hpp"
#include <catch2/catch.hpp>
namespace xo {
UtestConfig *
UtestConfig::instance() {
static UtestConfig s_instance;
return &s_instance;
};
scope
Utest::ut_scope() {
return scope(XO_DEBUG(UtestConfig::instance()->debug_flag()),
xtag("name", Catch::getResultCapture().getCurrentTestName()));
}
}
/* end TestUtil.cpp */

44
utest/TestUtil.hpp Normal file
View file

@ -0,0 +1,44 @@
/** @file TestUtil.hpp
*
* @author Roland Conybeare, May 2026
**/
#pragma once
#include <xo/indentlog/scope.hpp>
namespace xo {
/** unit-test configuration here
*
* TODO: promote to its own library, along with UtestListener
**/
struct UtestConfig {
bool debug_flag() const { return debug_flag_; }
/** announce each test using catch2's listener api **/
bool announce_flag_ = false;
/** enable debug output for all (!) tests **/
bool debug_flag_ = false;
static UtestConfig * instance();
};
/** RAII logging for catch unit tests
*
* Use:
* TEST_CASE(name, tags, ..)
* {
* scope log = Utest::ut_scope();
*
* ...
* log && log(xtag("foo", ...));
* }
**/
struct Utest {
static scope ut_scope();
};
} /*namespace xo*/
/* end TestUtil.hpp */

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include <xo/alloc2/VisitReason.hpp> #include <xo/alloc2/VisitReason.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
@ -13,6 +14,8 @@ namespace xo {
TEST_CASE("VisitReason-1", "[visitreason]") TEST_CASE("VisitReason-1", "[visitreason]")
{ {
auto log = Utest::ut_scope();
REQUIRE(VisitReason::unspecified() == VisitReason::unspecified()); REQUIRE(VisitReason::unspecified() == VisitReason::unspecified());
REQUIRE(VisitReason::unspecified() != VisitReason::forward()); REQUIRE(VisitReason::unspecified() != VisitReason::forward());

View file

@ -1,6 +1,100 @@
/* file alloc2_utest_main.cpp */ /* file alloc2_utest_main.cpp */
#define CATCH_CONFIG_MAIN #include "TestUtil.hpp"
#include "catch2/catch.hpp" #include <xo/subsys/Subsystem.hpp>
#include <xo/indentlog/scope.hpp>
#include <CLI/CLI.hpp>
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
namespace xo {
struct UtestListener : Catch::TestEventListenerBase {
using TestEventListenerBase::TestEventListenerBase;
// TestCasweInfo members: .name, .className, .description, .tags, lineInfo {.file, .line}
virtual void testCaseStarting(const Catch::TestCaseInfo & info) override {
using std::cerr;
using std::endl;
// preamble
if (UtestConfig::instance()->announce_flag_) {
cerr << "Starting unit test: "
<< "[" << info.name << "]"
<< " at "
<< "[" << info.lineInfo.file << ":" << info.lineInfo.line << "]"
<< endl;
}
}
virtual void testCaseEnded(const Catch::TestCaseStats & stats) override {
// postamble
}
// also sectionStarting / sectionEnded
};
CATCH_REGISTER_LISTENER(UtestListener);
}
int
main(int argc, char* argv[])
{
using xo::UtestConfig;
using xo::scope;
using xo::xtag;
using std::cout;
using std::cerr;
using std::endl;
//cerr << xtag("cli11", CLI11_VERSION) << endl; // version 2.5.0
CLI::App app{"utest.alloc2: xo-alloc2 unit tests"};
app.set_help_flag(); // disable default help impl, see below
{
app.add_flag("--debug",
UtestConfig::instance()->debug_flag_,
"enable debug logging (for all tests)");
app.add_flag("--announce",
UtestConfig::instance()->announce_flag_,
"announce each test via UtestListener");
}
bool help_flag = false;
{
app.add_flag("--help,-h,-?", help_flag, "print this help message and exit");
}
app.allow_extras();
CLI11_PARSE(app, argc, argv);
std::vector<const char *> argv2 = {argv[0]};
if (help_flag) {
// actual help impl, falls through to Session below
cout << "utest.alloc2 options" << endl;
cout << app.help() << endl;
cout << "catch2 options" << endl;
argv2.push_back("--help");
} else {
// keep program name
for (auto & x : app.remaining())
argv2.push_back(x.c_str());
using xo::Subsystem;
Subsystem::initialize_all();
}
scope log(XO_DEBUG(UtestConfig::instance()->debug_flag()), "start catch2 session");
// run catch2's test session / help
return Catch::Session().run(argv2.size(), argv2.data());
}
/* end alloc2_utest_main.cpp */ /* end alloc2_utest_main.cpp */

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, Dec 2025 * @author Roland Conybeare, Dec 2025
**/ **/
#include "TestUtil.hpp"
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator.hpp>
#include <xo/alloc2/Arena.hpp> #include <xo/alloc2/Arena.hpp>
#include <xo/arena/print.hpp> #include <xo/arena/print.hpp>
@ -33,6 +34,8 @@ namespace xo {
namespace ut { namespace ut {
TEST_CASE("IAllocator_Xfer_DArena", "[alloc2]") TEST_CASE("IAllocator_Xfer_DArena", "[alloc2]")
{ {
auto log = Utest::ut_scope();
IAllocator_Xfer<DArena, IAllocator_DArena> xfer; IAllocator_Xfer<DArena, IAllocator_DArena> xfer;
REQUIRE(IAllocator_Xfer<DArena, IAllocator_DArena>::_valid); REQUIRE(IAllocator_Xfer<DArena, IAllocator_DArena>::_valid);
@ -40,6 +43,8 @@ namespace xo {
TEST_CASE("DArena-medium", "[alloc2][DArena]") TEST_CASE("DArena-medium", "[alloc2][DArena]")
{ {
auto log = Utest::ut_scope();
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 10*1024*1024 }; .size_ = 10*1024*1024 };
DArena arena = DArena::map(cfg); DArena arena = DArena::map(cfg);
@ -83,6 +88,8 @@ namespace xo {
TEST_CASE("allocator-any-1", "[alloc2][AAllocator]") TEST_CASE("allocator-any-1", "[alloc2][AAllocator]")
{ {
auto log = Utest::ut_scope();
/* empty allocator alloc1 */ /* empty allocator alloc1 */
obj<AAllocator> alloc1; obj<AAllocator> alloc1;
@ -122,6 +129,8 @@ namespace xo {
TEST_CASE("allocator-expand-1", "[alloc2][AAllocator]") TEST_CASE("allocator-expand-1", "[alloc2][AAllocator]")
{ {
auto log = Utest::ut_scope();
/* typed allocator a1o */ /* typed allocator a1o */
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 1, .size_ = 1,
@ -160,6 +169,8 @@ namespace xo {
TEST_CASE("allocator-alloc-1", "[alloc2][AAllocator]") TEST_CASE("allocator-alloc-1", "[alloc2][AAllocator]")
{ {
auto log = Utest::ut_scope();
/* typed allocator a1o */ /* typed allocator a1o */
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 64*1024, .size_ = 64*1024,
@ -207,6 +218,8 @@ namespace xo {
TEST_CASE("allocator-alloc-2", "[alloc2][Allocator]") TEST_CASE("allocator-alloc-2", "[alloc2][Allocator]")
{ {
auto log = Utest::ut_scope();
using header_type = AllocHeader; using header_type = AllocHeader;
/* typed allocator a1o, with object header */ /* typed allocator a1o, with object header */
@ -293,6 +306,8 @@ namespace xo {
TEST_CASE("allocator-alloc-3", "[alloc2][Allocator]") TEST_CASE("allocator-alloc-3", "[alloc2][Allocator]")
{ {
auto log = Utest::ut_scope();
using header_type = AllocHeader; using header_type = AllocHeader;
/* typed allocator a1o, with object header + guard bytes */ /* typed allocator a1o, with object header + guard bytes */
@ -363,6 +378,8 @@ namespace xo {
TEST_CASE("allocator-fail-1", "[alloc2][AAllocator]") TEST_CASE("allocator-fail-1", "[alloc2][AAllocator]")
{ {
auto log = Utest::ut_scope();
/* typed allocator a1o */ /* typed allocator a1o */
ArenaConfig cfg { .name_ = "testarena", ArenaConfig cfg { .name_ = "testarena",
.size_ = 64*1024, .size_ = 64*1024,

View file

@ -3,6 +3,7 @@
* @author Roland Conybeare, May 2026 * @author Roland Conybeare, May 2026
**/ **/
#include "TestUtil.hpp"
#include "dp.hpp" #include "dp.hpp"
#include <xo/alloc2/Allocator.hpp> #include <xo/alloc2/Allocator.hpp>
#include <xo/alloc2/Arena.hpp> #include <xo/alloc2/Arena.hpp>
@ -33,6 +34,8 @@ namespace xo {
TEST_CASE("dp-1", "[dp]") TEST_CASE("dp-1", "[dp]")
{ {
auto log = Utest::ut_scope();
//ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 }; //ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 };
//DArena arena = DArena::map(cfg); //DArena arena = DArena::map(cfg);
//auto mm = obj<AAllocator,DArena>(&arena); //auto mm = obj<AAllocator,DArena>(&arena);
@ -55,6 +58,8 @@ namespace xo {
TEST_CASE("dp-2", "[dp]") TEST_CASE("dp-2", "[dp]")
{ {
auto log = Utest::ut_scope();
uint32_t counter = 0; uint32_t counter = 0;
Foo foo(&counter); Foo foo(&counter);
@ -77,6 +82,8 @@ namespace xo {
TEST_CASE("dp-DArena", "[dp][DArena]") TEST_CASE("dp-DArena", "[dp][DArena]")
{ {
auto log = Utest::ut_scope();
ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 }; ArenaConfig cfg { .name_ = "testarena", .size_ = 1024 };
DArena arena = DArena::map(cfg); DArena arena = DArena::map(cfg);
//auto mm = obj<AAllocator,DArena>(&arena); //auto mm = obj<AAllocator,DArena>(&arena);

View file

@ -79,6 +79,7 @@
* Application code will deal with ubox<AComplex,DPolarCoords> * Application code will deal with ubox<AComplex,DPolarCoords>
**/ **/
#include "TestUtil.hpp"
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <cmath> #include <cmath>
#include <cassert> #include <cassert>
@ -576,6 +577,8 @@ namespace xo {
TEST_CASE("objectmodel-specific-1", "[objectmodel]") TEST_CASE("objectmodel-specific-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
/* arg=0, mag=1 -> 1+0i */ /* arg=0, mag=1 -> 1+0i */
DPolarCoords polar{0.0, 1.0}; DPolarCoords polar{0.0, 1.0};
IComplex_Specific<DPolarCoords> polar_iface; IComplex_Specific<DPolarCoords> polar_iface;
@ -588,6 +591,8 @@ namespace xo {
TEST_CASE("objectmodel-specific-2", "[objectmodel]") TEST_CASE("objectmodel-specific-2", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
/* arg=0, mag=1 -> 1+0i */ /* arg=0, mag=1 -> 1+0i */
DRectCoords rect{1.0, 0.0}; DRectCoords rect{1.0, 0.0};
IComplex_Specific<DRectCoords> rect_iface; IComplex_Specific<DRectCoords> rect_iface;
@ -600,6 +605,8 @@ namespace xo {
TEST_CASE("uniquebox-1", "[objectmodel]") TEST_CASE("uniquebox-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0); auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0);
OUniqueBox<AComplex, DPolarCoords> box{tmp.release()}; OUniqueBox<AComplex, DPolarCoords> box{tmp.release()};
@ -611,6 +618,8 @@ namespace xo {
TEST_CASE("router-1", "[objectmodel]") TEST_CASE("router-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
using Object = OUniqueBox<AComplex, DPolarCoords>; using Object = OUniqueBox<AComplex, DPolarCoords>;
auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0); auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0);
@ -624,6 +633,8 @@ namespace xo {
TEST_CASE("routing-type-1", "[objectmodel]") TEST_CASE("routing-type-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
using Object = OUniqueBox<AComplex, DPolarCoords>; using Object = OUniqueBox<AComplex, DPolarCoords>;
auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0); auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0);
@ -637,6 +648,8 @@ namespace xo {
TEST_CASE("ubox-1", "[objectmodel]") TEST_CASE("ubox-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0); auto tmp = std::make_unique<DPolarCoords>(0.0, 1.0);
ubox<AComplex,DPolarCoords> box{tmp.release()}; ubox<AComplex,DPolarCoords> box{tmp.release()};
@ -648,6 +661,8 @@ namespace xo {
TEST_CASE("ubox-2", "[objectmodel]") TEST_CASE("ubox-2", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
auto tmp = std::make_unique<DRectCoords>(1.0, 0.0); auto tmp = std::make_unique<DRectCoords>(1.0, 0.0);
ubox<AComplex,DRectCoords> box{tmp.release()}; ubox<AComplex,DRectCoords> box{tmp.release()};
@ -659,12 +674,16 @@ namespace xo {
TEST_CASE("ubox-any-1", "[objectmodel]") TEST_CASE("ubox-any-1", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
/* default ctor */ /* default ctor */
ubox<AComplex> any; ubox<AComplex> any;
} }
TEST_CASE("ubox-any-2", "[objectmodel]") TEST_CASE("ubox-any-2", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
/* equivalent to ubox<AComplex,DRectCoords>, but impl doesn't use std::unique_ptr */ /* equivalent to ubox<AComplex,DRectCoords>, but impl doesn't use std::unique_ptr */
ubox<AComplex,DRectCoords> any{new DRectCoords{1.0, 0.0}}; ubox<AComplex,DRectCoords> any{new DRectCoords{1.0, 0.0}};
@ -676,6 +695,8 @@ namespace xo {
TEST_CASE("ubox-any-3", "[objectmodel]") TEST_CASE("ubox-any-3", "[objectmodel]")
{ {
auto log = Utest::ut_scope();
/* equivalent to ubox<AComplex,DRectCoords>, but impl doesn't use std::unique_ptr */ /* equivalent to ubox<AComplex,DRectCoords>, but impl doesn't use std::unique_ptr */
ubox<AComplex,DRectCoords> z1{new DRectCoords{1.0, 0.0}}; ubox<AComplex,DRectCoords> z1{new DRectCoords{1.0, 0.0}};