aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/C_API.cpp141
-rw-r--r--test/CursorManager.cpp86
-rw-r--r--test/LifecycleManager.cpp96
-rw-r--r--test/MultigroupCodec.cpp67
-rw-r--r--test/NamespaceReferenceManager.cpp162
-rw-r--r--test/ObjectManager.cpp33
-rw-r--r--test/VM.cpp159
7 files changed, 318 insertions, 426 deletions
diff --git a/test/C_API.cpp b/test/C_API.cpp
index f186374..892ed24 100644
--- a/test/C_API.cpp
+++ b/test/C_API.cpp
@@ -15,27 +15,26 @@
// test does not affect another.
// ---------------------------------------------------------------------------
-namespace
-{
+namespace {
struct InitGuard {
- InitGuard() { REQUIRE(rnt_init("memory", nullptr) == 0); }
+ InitGuard() {
+ REQUIRE(rnt_init("memory", nullptr) == 0);
+ }
};
- std::string take_string(char* p)
- {
+ std::string take_string(char* p) {
std::string s = p ? p : "";
rnt_free_string(p);
return s;
}
-}
+} // namespace
// ---------------------------------------------------------------------------
// Step 2 — Branch hash-pointer model
// ---------------------------------------------------------------------------
TEST_CASE("rnt_register_branch with empty target creates an unborn branch",
- "[step2][capi][branch]")
-{
+ "[step2][capi][branch]") {
InitGuard _;
const char* path = "/system/branches/test_step2_unborn";
REQUIRE(rnt_register_branch(path, "") == 0);
@@ -50,25 +49,20 @@ TEST_CASE("rnt_register_branch with empty target creates an unborn branch",
REQUIRE(rnt_close_handle(h) == 0);
}
-TEST_CASE("rnt_register_branch rejects an unregistered target hash",
- "[step2][capi][branch]")
-{
+TEST_CASE("rnt_register_branch rejects an unregistered target hash", "[step2][capi][branch]") {
InitGuard _;
- REQUIRE(rnt_register_branch("/system/branches/test_step2_bad_init",
- "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
- != 0);
+ REQUIRE(rnt_register_branch(
+ "/system/branches/test_step2_bad_init",
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") != 0);
}
-TEST_CASE("rnt_branch_advance rejects an unregistered target hash",
- "[step2][capi][branch]")
-{
+TEST_CASE("rnt_branch_advance rejects an unregistered target hash", "[step2][capi][branch]") {
InitGuard _;
const char* bpath = "/system/branches/test_step2_bad_advance";
REQUIRE(rnt_register_branch(bpath, "") == 0);
REQUIRE(rnt_branch_advance(
- bpath,
- "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") != 0);
+ bpath, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") != 0);
// Target unchanged.
rnt_handle_t h = rnt_open_handle(bpath, nullptr);
@@ -79,9 +73,7 @@ TEST_CASE("rnt_branch_advance rejects an unregistered target hash",
rnt_close_handle(h);
}
-TEST_CASE("Writing a tuple advances the branch to a new snapshot",
- "[step2][step3][capi][branch]")
-{
+TEST_CASE("Writing a tuple advances the branch to a new snapshot", "[step2][step3][capi][branch]") {
InitGuard _;
const char* bpath = "/system/branches/test_step2_advance";
const char* rpath = "/system/branches/test_step2_advance/multigroups/public/relations/items";
@@ -117,9 +109,7 @@ TEST_CASE("Writing a tuple advances the branch to a new snapshot",
// Step 3+4 — Copy-on-write writes and resolver wiring
// ---------------------------------------------------------------------------
-TEST_CASE("link_tuple commits a new snapshot and relation_root reflects it",
- "[step3][capi][cow]")
-{
+TEST_CASE("link_tuple commits a new snapshot and relation_root reflects it", "[step3][capi][cow]") {
InitGuard _;
const char* bpath = "/system/branches/test_step3_link";
const char* rpath = "/system/branches/test_step3_link/multigroups/public/relations/items";
@@ -138,12 +128,10 @@ TEST_CASE("link_tuple commits a new snapshot and relation_root reflects it",
REQUIRE(rnt_relation_root(rpath, &root) == 0);
const std::string new_root = take_string(root);
- REQUIRE(new_root.size() == 64); // a real Merkle root now
+ REQUIRE(new_root.size() == 64); // a real Merkle root now
}
-TEST_CASE("unlink_tuple and clear_relation walk back through new snapshots",
- "[step3][capi][cow]")
-{
+TEST_CASE("unlink_tuple and clear_relation walk back through new snapshots", "[step3][capi][cow]") {
InitGuard _;
const char* bpath = "/system/branches/test_step3_unlink";
const char* rpath = "/system/branches/test_step3_unlink/multigroups/public/relations/items";
@@ -171,8 +159,7 @@ TEST_CASE("unlink_tuple and clear_relation walk back through new snapshots",
}
TEST_CASE("rnt_open_handle resolves branch-relative paths to snapshot relations",
- "[step4][capi][resolver]")
-{
+ "[step4][capi][resolver]") {
InitGuard _;
const char* bpath = "/system/branches/test_step4_resolve";
const char* rpath = "/system/branches/test_step4_resolve/multigroups/public/relations/items";
@@ -203,26 +190,23 @@ TEST_CASE("rnt_open_handle resolves branch-relative paths to snapshot relations"
rnt_close_handle(h);
}
-TEST_CASE("Reading an unborn branch's relation fails cleanly",
- "[step4][capi][resolver]")
-{
+TEST_CASE("Reading an unborn branch's relation fails cleanly", "[step4][capi][resolver]") {
InitGuard _;
const char* bpath = "/system/branches/test_step4_unborn";
REQUIRE(rnt_register_branch(bpath, "") == 0);
// No relation registered: relation_root miss returns nonzero.
char* root = nullptr;
- REQUIRE(rnt_relation_root(
- "/system/branches/test_step4_unborn/multigroups/public/relations/missing", &root) != 0);
+ REQUIRE(
+ rnt_relation_root("/system/branches/test_step4_unborn/multigroups/public/relations/missing",
+ &root) != 0);
}
// ---------------------------------------------------------------------------
// Step 5 — Session lifecycle and overrides
// ---------------------------------------------------------------------------
-TEST_CASE("Each rnt_session_open returns a distinct 64-char hex hash",
- "[step5][capi][session]")
-{
+TEST_CASE("Each rnt_session_open returns a distinct 64-char hex hash", "[step5][capi][session]") {
InitGuard _;
char* a = nullptr;
char* b = nullptr;
@@ -238,17 +222,14 @@ TEST_CASE("Each rnt_session_open returns a distinct 64-char hex hash",
REQUIRE(rnt_session_close(hb.c_str()) == 0);
}
-TEST_CASE("rnt_session_close on an unknown hash fails",
- "[step5][capi][session]")
-{
+TEST_CASE("rnt_session_close on an unknown hash fails", "[step5][capi][session]") {
InitGuard _;
- REQUIRE(rnt_session_close(
- "0000000000000000000000000000000000000000000000000000000000000000") != 0);
+ REQUIRE(rnt_session_close("0000000000000000000000000000000000000000000000000000000000000000") !=
+ 0);
}
TEST_CASE("Session override redirects branch-relative reads to a different snapshot",
- "[step5][capi][session][resolver]")
-{
+ "[step5][capi][session][resolver]") {
InitGuard _;
const char* bpath = "/system/branches/test_step5_override";
const char* rpath = "/system/branches/test_step5_override/multigroups/public/relations/items";
@@ -273,9 +254,7 @@ TEST_CASE("Session override redirects branch-relative reads to a different snaps
char* sess = nullptr;
REQUIRE(rnt_session_open(nullptr, &sess) == 0);
const std::string sid = take_string(sess);
- REQUIRE(rnt_session_set_branch(sid.c_str(),
- "test_step5_override",
- s1.c_str()) == 0);
+ REQUIRE(rnt_session_set_branch(sid.c_str(), "test_step5_override", s1.c_str()) == 0);
// Advance the branch to S2 — global HEAD moves on; the session override
// keeps S1 resident.
@@ -283,9 +262,8 @@ TEST_CASE("Session override redirects branch-relative reads to a different snaps
rnt_free_string(th);
// Session view: still S1 (one tuple).
- const std::string spath =
- "/system/sessions/" + sid +
- "/branches/test_step5_override/multigroups/public/relations/items";
+ const std::string spath = "/system/sessions/" + sid +
+ "/branches/test_step5_override/multigroups/public/relations/items";
rnt_handle_t h_session = rnt_open_handle(spath.c_str(), nullptr);
REQUIRE(h_session != nullptr);
@@ -294,8 +272,7 @@ TEST_CASE("Session override redirects branch-relative reads to a different snaps
int rows_under_session = 0;
char* row = nullptr;
- while (rnt_cursor_next(c_session, &row) == 1)
- {
+ while (rnt_cursor_next(c_session, &row) == 1) {
++rows_under_session;
rnt_free_string(row);
}
@@ -310,7 +287,10 @@ TEST_CASE("Session override redirects branch-relative reads to a different snaps
rnt_cursor_t c_global = rnt_cursor_open(h_global);
REQUIRE(c_global != nullptr);
int rows_global = 0;
- while (rnt_cursor_next(c_global, &row) == 1) { ++rows_global; rnt_free_string(row); }
+ while (rnt_cursor_next(c_global, &row) == 1) {
+ ++rows_global;
+ rnt_free_string(row);
+ }
REQUIRE(rows_global == 2);
rnt_cursor_close(c_global);
rnt_close_handle(h_global);
@@ -319,8 +299,7 @@ TEST_CASE("Session override redirects branch-relative reads to a different snaps
}
TEST_CASE("Clearing a session override falls back to the global branch HEAD",
- "[step5][capi][session][resolver]")
-{
+ "[step5][capi][session][resolver]") {
InitGuard _;
const char* bpath = "/system/branches/test_step5_clear";
const char* rpath = "/system/branches/test_step5_clear/multigroups/public/relations/items";
@@ -351,15 +330,17 @@ TEST_CASE("Clearing a session override falls back to the global branch HEAD",
// Now the session sees the global view (two tuples).
const std::string spath =
- "/system/sessions/" + sid +
- "/branches/test_step5_clear/multigroups/public/relations/items";
+ "/system/sessions/" + sid + "/branches/test_step5_clear/multigroups/public/relations/items";
rnt_handle_t h = rnt_open_handle(spath.c_str(), nullptr);
REQUIRE(h != nullptr);
rnt_cursor_t c = rnt_cursor_open(h);
REQUIRE(c != nullptr);
int rows = 0;
char* row = nullptr;
- while (rnt_cursor_next(c, &row) == 1) { ++rows; rnt_free_string(row); }
+ while (rnt_cursor_next(c, &row) == 1) {
+ ++rows;
+ rnt_free_string(row);
+ }
REQUIRE(rows == 2);
rnt_cursor_close(c);
rnt_close_handle(h);
@@ -367,18 +348,15 @@ TEST_CASE("Clearing a session override falls back to the global branch HEAD",
REQUIRE(rnt_session_close(sid.c_str()) == 0);
}
-TEST_CASE("rnt_session_set_branch rejects an unregistered snapshot",
- "[step5][capi][session]")
-{
+TEST_CASE("rnt_session_set_branch rejects an unregistered snapshot", "[step5][capi][session]") {
InitGuard _;
char* sess = nullptr;
REQUIRE(rnt_session_open(nullptr, &sess) == 0);
const std::string sid = take_string(sess);
REQUIRE(rnt_session_set_branch(
- sid.c_str(),
- "anything",
- "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") != 0);
+ sid.c_str(), "anything",
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") != 0);
REQUIRE(rnt_session_close(sid.c_str()) == 0);
}
@@ -389,39 +367,34 @@ TEST_CASE("rnt_session_set_branch rejects an unregistered snapshot",
// or its enumerable relation set.
// ---------------------------------------------------------------------------
-TEST_CASE("Two multigroups under one branch advance independently",
- "[capi][multi-mg]")
-{
+TEST_CASE("Two multigroups under one branch advance independently", "[capi][multi-mg]") {
InitGuard _;
const std::string bpath = "/system/branches/test_multi_mg";
REQUIRE(rnt_register_branch(bpath.c_str(), "") == 0);
// Register one relation in each of two distinct multigroups.
- REQUIRE(rnt_register_relation(
- (bpath + "/multigroups/warehouse/relations/orders").c_str()) == 0);
- REQUIRE(rnt_register_relation(
- (bpath + "/multigroups/audit/relations/events").c_str()) == 0);
+ REQUIRE(rnt_register_relation((bpath + "/multigroups/warehouse/relations/orders").c_str()) ==
+ 0);
+ REQUIRE(rnt_register_relation((bpath + "/multigroups/audit/relations/events").c_str()) == 0);
char* mgs_raw = nullptr;
REQUIRE(rnt_list_branch_multigroups(bpath.c_str(), &mgs_raw) == 0);
const std::string mgs = take_string(mgs_raw);
REQUIRE(mgs.find("warehouse\t") != std::string::npos);
- REQUIRE(mgs.find("audit\t") != std::string::npos);
+ REQUIRE(mgs.find("audit\t") != std::string::npos);
// Insert into warehouse; record audit's root before/after.
char* audit_before = nullptr;
- REQUIRE(rnt_list_relations(
- (bpath + "/multigroups/audit").c_str(), &audit_before) == 0);
+ REQUIRE(rnt_list_relations((bpath + "/multigroups/audit").c_str(), &audit_before) == 0);
const std::string audit_before_s = take_string(audit_before);
char* th = nullptr;
- REQUIRE(rnt_link_tuple((bpath + "/multigroups/warehouse/relations/orders").c_str(),
- "id=1\n", &th) == 0);
+ REQUIRE(rnt_link_tuple((bpath + "/multigroups/warehouse/relations/orders").c_str(), "id=1\n",
+ &th) == 0);
rnt_free_string(th);
char* audit_after = nullptr;
- REQUIRE(rnt_list_relations(
- (bpath + "/multigroups/audit").c_str(), &audit_after) == 0);
+ REQUIRE(rnt_list_relations((bpath + "/multigroups/audit").c_str(), &audit_after) == 0);
const std::string audit_after_s = take_string(audit_after);
// audit's relation set is byte-identical: the warehouse-side mutation
@@ -430,13 +403,13 @@ TEST_CASE("Two multigroups under one branch advance independently",
// warehouse/orders is now non-empty.
char* root = nullptr;
- REQUIRE(rnt_relation_root(
- (bpath + "/multigroups/warehouse/relations/orders").c_str(), &root) == 0);
+ REQUIRE(rnt_relation_root((bpath + "/multigroups/warehouse/relations/orders").c_str(), &root) ==
+ 0);
REQUIRE(!take_string(root).empty());
// audit/events still empty.
char* root2 = nullptr;
- REQUIRE(rnt_relation_root(
- (bpath + "/multigroups/audit/relations/events").c_str(), &root2) == 0);
+ REQUIRE(rnt_relation_root((bpath + "/multigroups/audit/relations/events").c_str(), &root2) ==
+ 0);
REQUIRE(take_string(root2).empty());
}
diff --git a/test/CursorManager.cpp b/test/CursorManager.cpp
index 8c7de1a..c823ec5 100644
--- a/test/CursorManager.cpp
+++ b/test/CursorManager.cpp
@@ -27,55 +27,48 @@
// the handle's death is degenerate but safe.
// ---------------------------------------------------------------------------
-namespace
-{
- std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label)
- {
- auto t = std::make_unique<nt::ObjectManager::object_type>();
- t->label = label;
- t->methods = { OPEN, CLOSE };
+namespace {
+ std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label) {
+ auto t = std::make_unique<nt::ObjectManager::object_type>();
+ t->label = label;
+ t->methods = {OPEN, CLOSE};
return t;
}
std::unique_ptr<nt::ObjectManager::ephemeral_object_type>
- make_constant_eph_type(std::vector<nt::Attribute> attrs)
- {
- auto t = std::make_unique<nt::ObjectManager::ephemeral_object_type>();
- t->label = EPHEMERAL_RELATION;
- t->methods = { OPEN, CLOSE };
+ make_constant_eph_type(std::vector<nt::Attribute> attrs) {
+ auto t = std::make_unique<nt::ObjectManager::ephemeral_object_type>();
+ t->label = EPHEMERAL_RELATION;
+ t->methods = {OPEN, CLOSE};
t->cardinality = nt::ObjectManager::ephemeral_object_type::Cardinality::Finite;
- t->generator = [attrs](const std::vector<std::string>&,
- std::size_t offset, std::size_t limit)
- -> std::vector<nt::Tuple>
- {
- if (offset > 0 || limit == 0) return {};
- return { nt::Tuple(attrs) };
+ t->generator = [attrs](const std::vector<std::string>&, std::size_t offset,
+ std::size_t limit) -> std::vector<nt::Tuple> {
+ if (offset > 0 || limit == 0)
+ return {};
+ return {nt::Tuple(attrs)};
};
return t;
}
struct Stack {
- nt::InMemoryBackend backend;
- nt::ObjectManager objects;
- nt::PermissionsManager permissions;
- nt::IdentityManager identities;
- nt::LifecycleManager lifecycles { objects, backend };
- nt::NamespaceReferenceManager references { objects, backend };
- nt::HandlerManager handler { objects, permissions, identities,
- lifecycles, references };
- nt::CursorManager cursors { backend, &lifecycles, &objects };
+ nt::InMemoryBackend backend;
+ nt::ObjectManager objects;
+ nt::PermissionsManager permissions;
+ nt::IdentityManager identities;
+ nt::LifecycleManager lifecycles{objects, backend};
+ nt::NamespaceReferenceManager references{objects, backend};
+ nt::HandlerManager handler{objects, permissions, identities, lifecycles, references};
+ nt::CursorManager cursors{backend, &lifecycles, &objects};
};
-}
+} // namespace
-TEST_CASE("Cursor pins the parent snapshot and unpins on close",
- "[step6][cursor][pin]")
-{
+TEST_CASE("Cursor pins the parent snapshot and unpins on close", "[step6][cursor][pin]") {
Stack s;
// Stand up a minimal snapshot with one relation.
auto& om = s.objects;
- om.Register({"system", "snapshots", "Hcur"},
- std::make_unique<nt::ObjectManager::Multigroup>(), make_type(MULTIGROUP));
+ om.Register({"system", "snapshots", "Hcur"}, std::make_unique<nt::ObjectManager::Multigroup>(),
+ make_type(MULTIGROUP));
om.Register({"system", "snapshots", "Hcur", "relations", "items"},
std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
@@ -93,27 +86,25 @@ TEST_CASE("Cursor pins the parent snapshot and unpins on close",
int conn = 1;
auto* h = s.handler.Open({"system", "snapshots", "Hcur", "relations", "items"}, &conn);
REQUIRE(h != nullptr);
- REQUIRE(mg_entry->head->reference_count == 1); // external pin only
+ REQUIRE(mg_entry->head->reference_count == 1); // external pin only
auto* c = s.cursors.Open(h);
REQUIRE(c != nullptr);
REQUIRE(c->pinned_snapshot == mg_entry);
- REQUIRE(mg_entry->head->reference_count == 2); // external + cursor pin
+ REQUIRE(mg_entry->head->reference_count == 2); // external + cursor pin
s.cursors.Close(c);
- REQUIRE(mg_entry->head->reference_count == 1); // back to external only
+ REQUIRE(mg_entry->head->reference_count == 1); // back to external only
s.handler.Close(h);
- s.lifecycles.Unpin(mg_entry); // releases the external pin; cascade-collects.
+ s.lifecycles.Unpin(mg_entry); // releases the external pin; cascade-collects.
}
TEST_CASE("Closing the handle mid-iteration does not crash an ephemeral cursor",
- "[step6][cursor][handle-independence]")
-{
+ "[step6][cursor][handle-independence]") {
Stack s;
// Register an ephemeral relation; its generator yields one tuple.
- s.objects.Register({"eph"},
- std::make_unique<nt::ObjectManager::Relation>(),
+ s.objects.Register({"eph"}, std::make_unique<nt::ObjectManager::Relation>(),
make_constant_eph_type({{"k", "v"}}));
int conn = 1;
@@ -124,7 +115,7 @@ TEST_CASE("Closing the handle mid-iteration does not crash an ephemeral cursor",
// Ephemeral cursors start exhausted; reset to simulate a JOIN probe.
c->exhausted = false;
- c->args = {};
+ c->args = {};
// Now violate the documented contract: close the handle BEFORE Next().
// The cursor must still produce its tuple from the captured generator
@@ -135,18 +126,19 @@ TEST_CASE("Closing the handle mid-iteration does not crash an ephemeral cursor",
REQUIRE(tuple != nullptr);
bool found = false;
for (const auto& a : tuple->attrs())
- if (a.name == "k" && a.value == "v") { found = true; break; }
+ if (a.name == "k" && a.value == "v") {
+ found = true;
+ break;
+ }
REQUIRE(found);
s.cursors.Close(c);
}
TEST_CASE("Cursor opened against a non-snapshot relation does not pin anything",
- "[step6][cursor][pin]")
-{
+ "[step6][cursor][pin]") {
Stack s;
- s.objects.Register({"plain", "rel"},
- std::make_unique<nt::ObjectManager::Relation>(),
+ s.objects.Register({"plain", "rel"}, std::make_unique<nt::ObjectManager::Relation>(),
make_type(RELATION));
int conn = 1;
auto* h = s.handler.Open({"plain", "rel"}, &conn);
diff --git a/test/LifecycleManager.cpp b/test/LifecycleManager.cpp
index aa75e14..3712522 100644
--- a/test/LifecycleManager.cpp
+++ b/test/LifecycleManager.cpp
@@ -16,22 +16,18 @@
// unpin every Relation registered under their snapshot subtree.
// ---------------------------------------------------------------------------
-namespace
-{
- std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label)
- {
- auto t = std::make_unique<nt::ObjectManager::object_type>();
- t->label = label;
- t->methods = { OPEN, CLOSE };
+namespace {
+ std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label) {
+ auto t = std::make_unique<nt::ObjectManager::object_type>();
+ t->label = label;
+ t->methods = {OPEN, CLOSE};
return t;
}
-}
+} // namespace
-TEST_CASE("Pin and Unpin adjust reference_count symmetrically",
- "[step6][lifecycle][pin]")
-{
+TEST_CASE("Pin and Unpin adjust reference_count symmetrically", "[step6][lifecycle][pin]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
om.Register({"r1"}, std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* entry = om.Find({"r1"});
@@ -48,19 +44,17 @@ TEST_CASE("Pin and Unpin adjust reference_count symmetrically",
REQUIRE(om.Find({"r1"}) != nullptr);
}
-TEST_CASE("Pin on nullptr is a no-op", "[step6][lifecycle][pin]")
-{
+TEST_CASE("Pin on nullptr is a no-op", "[step6][lifecycle][pin]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
REQUIRE_NOTHROW(lm.Pin(nullptr));
REQUIRE_NOTHROW(lm.Unpin(nullptr));
}
-TEST_CASE("Unpin below zero does not underflow", "[step6][lifecycle][pin]")
-{
+TEST_CASE("Unpin below zero does not underflow", "[step6][lifecycle][pin]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
om.Register({"r1"}, std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* entry = om.Find({"r1"});
@@ -69,10 +63,9 @@ TEST_CASE("Unpin below zero does not underflow", "[step6][lifecycle][pin]")
REQUIRE(entry->head->reference_count == 0);
}
-TEST_CASE("GC is gated on both counters reaching zero", "[step6][lifecycle][gc]")
-{
+TEST_CASE("GC is gated on both counters reaching zero", "[step6][lifecycle][gc]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
om.Register({"r1"}, std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* entry = om.Find({"r1"});
@@ -81,22 +74,21 @@ TEST_CASE("GC is gated on both counters reaching zero", "[step6][lifecycle][gc]"
lm.Pin(entry);
// handle_count=1, ref_count=1.
- lm.Unpin(entry); // ref_count → 0, but handle_count still 1.
+ lm.Unpin(entry); // ref_count → 0, but handle_count still 1.
REQUIRE(om.Find({"r1"}) != nullptr);
lm.Pin(entry);
- lm.Unmonitor(entry); // handle_count → 0, but ref_count still 1.
+ lm.Unmonitor(entry); // handle_count → 0, but ref_count still 1.
REQUIRE(om.Find({"r1"}) != nullptr);
- lm.Unpin(entry); // both zero → GC.
+ lm.Unpin(entry); // both zero → GC.
REQUIRE(om.Find({"r1"}) == nullptr);
}
TEST_CASE("Unmonitor that brings both counters to zero collects the entry",
- "[step6][lifecycle][gc]")
-{
+ "[step6][lifecycle][gc]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
om.Register({"r1"}, std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* entry = om.Find({"r1"});
@@ -107,11 +99,9 @@ TEST_CASE("Unmonitor that brings both counters to zero collects the entry",
REQUIRE(om.Find({"r1"}) == nullptr);
}
-TEST_CASE("IsEligibleForGC reflects the joint-zero rule",
- "[step6][lifecycle][gc]")
-{
+TEST_CASE("IsEligibleForGC reflects the joint-zero rule", "[step6][lifecycle][gc]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
om.Register({"r1"}, std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* entry = om.Find({"r1"});
@@ -122,21 +112,18 @@ TEST_CASE("IsEligibleForGC reflects the joint-zero rule",
}
TEST_CASE("CascadeMultigroup unpins child Relations on snapshot collection",
- "[step6][lifecycle][cascade]")
-{
+ "[step6][lifecycle][cascade]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
- om.Register({"system", "snapshots", "H1"},
- std::make_unique<nt::ObjectManager::Multigroup>(), make_type(MULTIGROUP));
+ om.Register({"system", "snapshots", "H1"}, std::make_unique<nt::ObjectManager::Multigroup>(),
+ make_type(MULTIGROUP));
auto* mg_entry = om.Find({"system", "snapshots", "H1"});
- for (const auto& n : { std::string{"foo"}, std::string{"bar"} })
- {
+ for (const auto& n : {std::string{"foo"}, std::string{"bar"}}) {
om.Register({"system", "snapshots", "H1", "relations", n},
- std::make_unique<nt::ObjectManager::Relation>(),
- make_type(RELATION));
+ std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
// The parent Multigroup's pin on each child, mirroring what
// register_snapshot does in the C API layer.
lm.Pin(om.Find({"system", "snapshots", "H1", "relations", n}));
@@ -148,38 +135,35 @@ TEST_CASE("CascadeMultigroup unpins child Relations on snapshot collection",
lm.Pin(mg_entry);
lm.Unpin(mg_entry);
- REQUIRE(om.Find({"system", "snapshots", "H1"}) == nullptr);
- REQUIRE(om.Find({"system", "snapshots", "H1", "relations", "foo"}) == nullptr);
- REQUIRE(om.Find({"system", "snapshots", "H1", "relations", "bar"}) == nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H1"}) == nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H1", "relations", "foo"}) == nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H1", "relations", "bar"}) == nullptr);
}
-TEST_CASE("Cascade survives a child with an open handle",
- "[step6][lifecycle][cascade]")
-{
+TEST_CASE("Cascade survives a child with an open handle", "[step6][lifecycle][cascade]") {
nt::InMemoryBackend store;
- nt::ObjectManager om;
+ nt::ObjectManager om;
nt::LifecycleManager lm(om, store);
- om.Register({"system", "snapshots", "H2"},
- std::make_unique<nt::ObjectManager::Multigroup>(), make_type(MULTIGROUP));
+ om.Register({"system", "snapshots", "H2"}, std::make_unique<nt::ObjectManager::Multigroup>(),
+ make_type(MULTIGROUP));
auto* mg_entry = om.Find({"system", "snapshots", "H2"});
om.Register({"system", "snapshots", "H2", "relations", "live"},
- std::make_unique<nt::ObjectManager::Relation>(),
- make_type(RELATION));
+ std::make_unique<nt::ObjectManager::Relation>(), make_type(RELATION));
auto* child = om.Find({"system", "snapshots", "H2", "relations", "live"});
- lm.Pin(child); // parent's pin
- lm.Monitor(child); // simulate an open handle on the child
+ lm.Pin(child); // parent's pin
+ lm.Monitor(child); // simulate an open handle on the child
// Drop the Multigroup's external ref: cascade unpins child (ref → 0),
// but handle_count is still 1 — child must survive.
lm.Pin(mg_entry);
lm.Unpin(mg_entry);
- REQUIRE(om.Find({"system", "snapshots", "H2"}) == nullptr);
- REQUIRE(om.Find({"system", "snapshots", "H2", "relations", "live"}) != nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H2"}) == nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H2", "relations", "live"}) != nullptr);
// Closing the handle now releases the child.
lm.Unmonitor(child);
- REQUIRE(om.Find({"system", "snapshots", "H2", "relations", "live"}) == nullptr);
+ REQUIRE(om.Find({"system", "snapshots", "H2", "relations", "live"}) == nullptr);
}
diff --git a/test/MultigroupCodec.cpp b/test/MultigroupCodec.cpp
index c1e2509..b04c405 100644
--- a/test/MultigroupCodec.cpp
+++ b/test/MultigroupCodec.cpp
@@ -17,48 +17,42 @@
namespace {
-// Pad a short label into a deterministic 64-char hex string so the codec can
-// decode it as a 32-byte payload.
-std::string mk_root(char c, char d = '0')
-{
- std::string h(64, '0');
- h[0] = c;
- h[1] = d;
- return h;
-}
-
-} // namespace
-
-TEST_CASE("Build is order-independent - tree sorts intrinsically",
- "[multigroup-codec]")
-{
+ // Pad a short label into a deterministic 64-char hex string so the codec can
+ // decode it as a 32-byte payload.
+ std::string mk_root(char c, char d = '0') {
+ std::string h(64, '0');
+ h[0] = c;
+ h[1] = d;
+ return h;
+ }
+
+} // namespace
+
+TEST_CASE("Build is order-independent - tree sorts intrinsically", "[multigroup-codec]") {
nt::InMemoryBackend a, b;
std::vector<nt::MultigroupCodec::RelationEntry> in_order = {
- {"alpha", mk_root('a')},
- {"bravo", mk_root('b')},
+ {"alpha", mk_root('a')},
+ {"bravo", mk_root('b')},
{"charlie", mk_root('c')},
};
std::vector<nt::MultigroupCodec::RelationEntry> shuffled = {
{"charlie", mk_root('c')},
- {"alpha", mk_root('a')},
- {"bravo", mk_root('b')},
+ {"alpha", mk_root('a')},
+ {"bravo", mk_root('b')},
};
- REQUIRE(nt::MultigroupCodec::Build(a, in_order)
- == nt::MultigroupCodec::Build(b, shuffled));
+ REQUIRE(nt::MultigroupCodec::Build(a, in_order) == nt::MultigroupCodec::Build(b, shuffled));
}
-TEST_CASE("Build distinguishes content changes", "[multigroup-codec]")
-{
+TEST_CASE("Build distinguishes content changes", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> base = {{"foo", mk_root('a')}};
std::vector<nt::MultigroupCodec::RelationEntry> different_root = {{"foo", mk_root('b')}};
std::vector<nt::MultigroupCodec::RelationEntry> different_name = {{"bar", mk_root('a')}};
- std::vector<nt::MultigroupCodec::RelationEntry> extra_member = {
- {"foo", mk_root('a')}, {"bar", mk_root('b')}
- };
+ std::vector<nt::MultigroupCodec::RelationEntry> extra_member = {{"foo", mk_root('a')},
+ {"bar", mk_root('b')}};
const auto h_base = nt::MultigroupCodec::Build(backend, base);
REQUIRE(h_base != nt::MultigroupCodec::Build(backend, different_root));
@@ -66,9 +60,7 @@ TEST_CASE("Build distinguishes content changes", "[multigroup-codec]")
REQUIRE(h_base != nt::MultigroupCodec::Build(backend, extra_member));
}
-TEST_CASE("Build then List round-trips entries in key-sorted order",
- "[multigroup-codec]")
-{
+TEST_CASE("Build then List round-trips entries in key-sorted order", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> entries = {
@@ -86,8 +78,7 @@ TEST_CASE("Build then List round-trips entries in key-sorted order",
REQUIRE(listed[0].second == mk_root('b', '2'));
}
-TEST_CASE("Lookup returns payload by name", "[multigroup-codec]")
-{
+TEST_CASE("Lookup returns payload by name", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> entries = {
@@ -101,9 +92,7 @@ TEST_CASE("Lookup returns payload by name", "[multigroup-codec]")
REQUIRE(nt::MultigroupCodec::Lookup(backend, root, "missing").empty());
}
-TEST_CASE("InsertOne mutates one entry; sibling subtree is untouched",
- "[multigroup-codec]")
-{
+TEST_CASE("InsertOne mutates one entry; sibling subtree is untouched", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> entries = {
@@ -113,16 +102,13 @@ TEST_CASE("InsertOne mutates one entry; sibling subtree is untouched",
const auto root0 = nt::MultigroupCodec::Build(backend, entries);
// Mutate foo only; bar's payload must round-trip unchanged.
- const auto root1 = nt::MultigroupCodec::InsertOne(
- backend, root0, "foo", mk_root('d'));
+ const auto root1 = nt::MultigroupCodec::InsertOne(backend, root0, "foo", mk_root('d'));
REQUIRE(root1 != root0);
REQUIRE(nt::MultigroupCodec::Lookup(backend, root1, "foo") == mk_root('d'));
REQUIRE(nt::MultigroupCodec::Lookup(backend, root1, "bar") == mk_root('b'));
}
-TEST_CASE("RemoveOne deletes one entry; sibling preserved",
- "[multigroup-codec]")
-{
+TEST_CASE("RemoveOne deletes one entry; sibling preserved", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> entries = {
@@ -136,8 +122,7 @@ TEST_CASE("RemoveOne deletes one entry; sibling preserved",
REQUIRE(nt::MultigroupCodec::Lookup(backend, root1, "bar") == mk_root('b'));
}
-TEST_CASE("Empty multigroup builds to an empty root", "[multigroup-codec]")
-{
+TEST_CASE("Empty multigroup builds to an empty root", "[multigroup-codec]") {
nt::InMemoryBackend backend;
std::vector<nt::MultigroupCodec::RelationEntry> empty;
REQUIRE(nt::MultigroupCodec::Build(backend, empty).empty());
diff --git a/test/NamespaceReferenceManager.cpp b/test/NamespaceReferenceManager.cpp
index 27f0dcd..90e65e5 100644
--- a/test/NamespaceReferenceManager.cpp
+++ b/test/NamespaceReferenceManager.cpp
@@ -21,74 +21,62 @@
namespace {
-using Path = std::vector<std::string>;
-
-// Pad a label into a deterministic 64-char hex string so the merkle codec can
-// decode it as a 32-byte payload.
-std::string mk_hash(char c, char d = '0')
-{
- std::string h(64, '0');
- h[0] = c;
- h[1] = d;
- return h;
-}
-
-std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label)
-{
- auto t = std::make_unique<nt::ObjectManager::object_type>();
- t->label = label;
- t->methods = { OPEN, CLOSE };
- return t;
-}
-
-void register_branch(nt::ObjectManager& om,
- const std::string& name,
- const std::string& target_hash)
-{
- auto br = std::make_unique<nt::ObjectManager::Branch>();
- br->name = name;
- br->target_hash = target_hash;
- om.Register({"system", "branches", name}, std::move(br), make_type(BRANCH));
-}
-
-void register_session(nt::ObjectManager& om,
- const std::string& id,
- std::map<std::string, std::string> overrides = {})
-{
- auto s = std::make_unique<nt::ObjectManager::Session>();
- s->branch_overrides = std::move(overrides);
- om.Register({"system", "sessions", id}, std::move(s), make_type(SESSION));
-}
-
-// Builds a branch tree with a single (mg_name → mg_hash) entry. Returns the
-// branch-tree root hex.
-std::string build_branch_tree(nt::InMemoryBackend& store,
- const std::string& mg_name,
- const std::string& mg_hash_hex)
-{
- return nt::Merkle<std::string>::Insert(store, "", mg_name,
- nt::hex_to_bin(mg_hash_hex));
-}
-
-} // namespace
-
-TEST_CASE("Branch sub-path rewrites through the branch tree to the mg snapshot",
- "[resolve]")
-{
+ using Path = std::vector<std::string>;
+
+ // Pad a label into a deterministic 64-char hex string so the merkle codec can
+ // decode it as a 32-byte payload.
+ std::string mk_hash(char c, char d = '0') {
+ std::string h(64, '0');
+ h[0] = c;
+ h[1] = d;
+ return h;
+ }
+
+ std::unique_ptr<nt::ObjectManager::object_type> make_type(OBJECT_TYPE label) {
+ auto t = std::make_unique<nt::ObjectManager::object_type>();
+ t->label = label;
+ t->methods = {OPEN, CLOSE};
+ return t;
+ }
+
+ void register_branch(nt::ObjectManager& om, const std::string& name,
+ const std::string& target_hash) {
+ auto br = std::make_unique<nt::ObjectManager::Branch>();
+ br->name = name;
+ br->target_hash = target_hash;
+ om.Register({"system", "branches", name}, std::move(br), make_type(BRANCH));
+ }
+
+ void register_session(nt::ObjectManager& om, const std::string& id,
+ std::map<std::string, std::string> overrides = {}) {
+ auto s = std::make_unique<nt::ObjectManager::Session>();
+ s->branch_overrides = std::move(overrides);
+ om.Register({"system", "sessions", id}, std::move(s), make_type(SESSION));
+ }
+
+ // Builds a branch tree with a single (mg_name → mg_hash) entry. Returns the
+ // branch-tree root hex.
+ std::string build_branch_tree(nt::InMemoryBackend& store, const std::string& mg_name,
+ const std::string& mg_hash_hex) {
+ return nt::Merkle<std::string>::Insert(store, "", mg_name, nt::hex_to_bin(mg_hash_hex));
+ }
+
+} // namespace
+
+TEST_CASE("Branch sub-path rewrites through the branch tree to the mg snapshot", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
const std::string mg_hash = mk_hash('a');
- const std::string tree = build_branch_tree(store, "warehouse", mg_hash);
+ const std::string tree = build_branch_tree(store, "warehouse", mg_hash);
register_branch(om, "main", tree);
nt::NamespaceReferenceManager nrm(om, store);
- REQUIRE(nrm.Resolve({"system", "branches", "main", "multigroups", "warehouse", "relations", "items"})
- == Path{"system", "snapshots", mg_hash, "relations", "items"});
+ REQUIRE(nrm.Resolve({"system", "branches", "main", "multigroups", "warehouse", "relations",
+ "items"}) == Path{"system", "snapshots", mg_hash, "relations", "items"});
}
-TEST_CASE("Resolve on the branch object itself is left unchanged", "[resolve]")
-{
+TEST_CASE("Resolve on the branch object itself is left unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
const std::string tree = build_branch_tree(store, "warehouse", mk_hash('a'));
@@ -99,19 +87,17 @@ TEST_CASE("Resolve on the branch object itself is left unchanged", "[resolve]")
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Resolve on an unborn branch leaves the path unchanged", "[resolve]")
-{
+TEST_CASE("Resolve on an unborn branch leaves the path unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
- register_branch(om, "main", ""); // unborn
+ register_branch(om, "main", ""); // unborn
nt::NamespaceReferenceManager nrm(om, store);
const Path p{"system", "branches", "main", "multigroups", "warehouse", "relations", "items"};
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Resolve on an absent multigroup leaves the path unchanged", "[resolve]")
-{
+TEST_CASE("Resolve on an absent multigroup leaves the path unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
const std::string tree = build_branch_tree(store, "warehouse", mk_hash('a'));
@@ -122,8 +108,7 @@ TEST_CASE("Resolve on an absent multigroup leaves the path unchanged", "[resolve
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Resolve on an unknown branch leaves the path unchanged", "[resolve]")
-{
+TEST_CASE("Resolve on an unknown branch leaves the path unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
nt::NamespaceReferenceManager nrm(om, store);
@@ -131,8 +116,7 @@ TEST_CASE("Resolve on an unknown branch leaves the path unchanged", "[resolve]")
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Non-branch paths pass through unchanged", "[resolve]")
-{
+TEST_CASE("Non-branch paths pass through unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
nt::NamespaceReferenceManager nrm(om, store);
@@ -140,62 +124,52 @@ TEST_CASE("Non-branch paths pass through unchanged", "[resolve]")
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Session override takes precedence over the global branch HEAD",
- "[resolve]")
-{
+TEST_CASE("Session override takes precedence over the global branch HEAD", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
- const std::string mg_global = mk_hash('a');
+ const std::string mg_global = mk_hash('a');
const std::string mg_session = mk_hash('b');
- const std::string tree_global =
- build_branch_tree(store, "warehouse", mg_global);
- const std::string tree_session =
- build_branch_tree(store, "warehouse", mg_session);
+ const std::string tree_global = build_branch_tree(store, "warehouse", mg_global);
+ const std::string tree_session = build_branch_tree(store, "warehouse", mg_session);
register_branch(om, "main", tree_global);
register_session(om, "sess123", {{"main", tree_session}});
nt::NamespaceReferenceManager nrm(om, store);
- REQUIRE(nrm.Resolve({"system", "sessions", "sess123", "branches", "main",
- "multigroups", "warehouse", "relations", "items"})
- == Path{"system", "snapshots", mg_session, "relations", "items"});
+ REQUIRE(nrm.Resolve({"system", "sessions", "sess123", "branches", "main", "multigroups",
+ "warehouse", "relations", "items"}) ==
+ Path{"system", "snapshots", mg_session, "relations", "items"});
}
-TEST_CASE("Session resolution falls back to global branch when override is absent",
- "[resolve]")
-{
+TEST_CASE("Session resolution falls back to global branch when override is absent", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
const std::string mg_global = mk_hash('a');
- const std::string tree_global =
- build_branch_tree(store, "warehouse", mg_global);
+ const std::string tree_global = build_branch_tree(store, "warehouse", mg_global);
register_branch(om, "main", tree_global);
- register_session(om, "sess123"); // no overrides
+ register_session(om, "sess123"); // no overrides
nt::NamespaceReferenceManager nrm(om, store);
- REQUIRE(nrm.Resolve({"system", "sessions", "sess123", "branches", "main",
- "multigroups", "warehouse", "relations", "items"})
- == Path{"system", "snapshots", mg_global, "relations", "items"});
+ REQUIRE(nrm.Resolve({"system", "sessions", "sess123", "branches", "main", "multigroups",
+ "warehouse", "relations", "items"}) ==
+ Path{"system", "snapshots", mg_global, "relations", "items"});
}
-TEST_CASE("Session resolution with neither override nor global branch is unchanged",
- "[resolve]")
-{
+TEST_CASE("Session resolution with neither override nor global branch is unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
register_session(om, "sess123");
nt::NamespaceReferenceManager nrm(om, store);
- const Path p{"system", "sessions", "sess123", "branches", "main",
+ const Path p{"system", "sessions", "sess123", "branches", "main",
"multigroups", "warehouse", "relations", "items"};
REQUIRE(nrm.Resolve(p) == p);
}
-TEST_CASE("Empty input path is returned unchanged", "[resolve]")
-{
+TEST_CASE("Empty input path is returned unchanged", "[resolve]") {
nt::ObjectManager om;
nt::InMemoryBackend store;
nt::NamespaceReferenceManager nrm(om, store);
diff --git a/test/ObjectManager.cpp b/test/ObjectManager.cpp
index e97bcb2..f90de3a 100644
--- a/test/ObjectManager.cpp
+++ b/test/ObjectManager.cpp
@@ -13,23 +13,19 @@
// share.
// ---------------------------------------------------------------------------
-namespace
-{
- std::unique_ptr<nt::ObjectManager::object_type> session_type()
- {
- auto t = std::make_unique<nt::ObjectManager::object_type>();
- t->label = SESSION;
+namespace {
+ std::unique_ptr<nt::ObjectManager::object_type> session_type() {
+ auto t = std::make_unique<nt::ObjectManager::object_type>();
+ t->label = SESSION;
t->disposable = true;
- t->methods = { OPEN, CLOSE };
+ t->methods = {OPEN, CLOSE};
return t;
}
-}
+} // namespace
-TEST_CASE("Unregister removes the entry and Find returns nullptr", "[step5][object-manager]")
-{
+TEST_CASE("Unregister removes the entry and Find returns nullptr", "[step5][object-manager]") {
nt::ObjectManager om;
- om.Register({"system", "sessions", "alpha"},
- std::make_unique<nt::ObjectManager::Session>(),
+ om.Register({"system", "sessions", "alpha"}, std::make_unique<nt::ObjectManager::Session>(),
session_type());
REQUIRE(om.Find({"system", "sessions", "alpha"}) != nullptr);
@@ -37,15 +33,13 @@ TEST_CASE("Unregister removes the entry and Find returns nullptr", "[step5][obje
REQUIRE(om.Find({"system", "sessions", "alpha"}) == nullptr);
}
-TEST_CASE("Unregister returns false for missing paths", "[step5][object-manager]")
-{
+TEST_CASE("Unregister returns false for missing paths", "[step5][object-manager]") {
nt::ObjectManager om;
REQUIRE(om.Unregister({"never", "registered"}) == false);
}
TEST_CASE("Unregister splices a middle entry without disturbing neighbours",
- "[step5][object-manager]")
-{
+ "[step5][object-manager]") {
nt::ObjectManager om;
om.Register({"a"}, std::make_unique<nt::ObjectManager::Session>(), session_type());
om.Register({"b"}, std::make_unique<nt::ObjectManager::Session>(), session_type());
@@ -58,16 +52,15 @@ TEST_CASE("Unregister splices a middle entry without disturbing neighbours",
REQUIRE(om.Find({"c"}) != nullptr);
}
-TEST_CASE("Unregister at the head of the list works", "[step5][object-manager]")
-{
+TEST_CASE("Unregister at the head of the list works", "[step5][object-manager]") {
nt::ObjectManager om;
- om.Register({"first"}, std::make_unique<nt::ObjectManager::Session>(), session_type());
+ om.Register({"first"}, std::make_unique<nt::ObjectManager::Session>(), session_type());
om.Register({"second"}, std::make_unique<nt::ObjectManager::Session>(), session_type());
// The most recently registered entry is the list head (LIFO).
REQUIRE(om.Unregister({"second"}) == true);
REQUIRE(om.Find({"second"}) == nullptr);
- REQUIRE(om.Find({"first"}) != nullptr);
+ REQUIRE(om.Find({"first"}) != nullptr);
// And the new head is removable too.
REQUIRE(om.Unregister({"first"}) == true);
diff --git a/test/VM.cpp b/test/VM.cpp
index f4ce453..7e61da6 100644
--- a/test/VM.cpp
+++ b/test/VM.cpp
@@ -21,48 +21,45 @@
// ---------------------------------------------------------------------------
// Stores a tuple in the backend and updates the Relation object's Merkle root.
-static void store_tuple(nt::InMemoryBackend& backend,
- nt::ObjectManager& objects,
- const std::vector<std::string>& path,
- std::vector<nt::Attribute> attrs)
-{
+static void store_tuple(nt::InMemoryBackend& backend, nt::ObjectManager& objects,
+ const std::vector<std::string>& path, std::vector<nt::Attribute> attrs) {
auto bytes = nt::TupleCodec::Serialize(attrs);
- auto hash = backend.Put(std::move(bytes));
+ auto hash = backend.Put(std::move(bytes));
auto* entry = objects.Find(path);
- if (!entry) return;
+ if (!entry)
+ return;
auto* rel = dynamic_cast<nt::ObjectManager::Relation*>(entry->object.get());
- if (!rel) return;
+ if (!rel)
+ return;
auto hash_bin = nt::hex_to_bin(hash);
- rel->merkle_root = nt::Merkle<nt::Hash32>::Insert(backend, rel->merkle_root,
- hash_bin, hash_bin);
+ rel->merkle_root =
+ nt::Merkle<nt::Hash32>::Insert(backend, rel->merkle_root, hash_bin, hash_bin);
}
-static std::unique_ptr<nt::ObjectManager::object_type> make_relation_type()
-{
+static std::unique_ptr<nt::ObjectManager::object_type> make_relation_type() {
auto t = std::make_unique<nt::ObjectManager::object_type>();
- t->label = RELATION;
+ t->label = RELATION;
t->disposable = false;
- t->methods = { OPEN, CLOSE };
+ t->methods = {OPEN, CLOSE};
return t;
}
// Generator for eq[A, B]: returns one tuple when A == B, nothing otherwise.
// Treats both args as integers when parseable, strings otherwise.
-static std::unique_ptr<nt::ObjectManager::ephemeral_object_type> make_eq_type()
-{
+static std::unique_ptr<nt::ObjectManager::ephemeral_object_type> make_eq_type() {
auto t = std::make_unique<nt::ObjectManager::ephemeral_object_type>();
- t->label = EPHEMERAL_RELATION;
- t->disposable = false;
- t->methods = { OPEN, CLOSE };
+ t->label = EPHEMERAL_RELATION;
+ t->disposable = false;
+ t->methods = {OPEN, CLOSE};
t->cardinality = nt::ObjectManager::ephemeral_object_type::Cardinality::AlephZero;
- t->generator = [](const std::vector<std::string>& args,
- std::size_t offset,
- std::size_t limit) -> std::vector<nt::Tuple>
- {
- if (args.size() < 2 || offset > 0 || limit == 0) return {};
- if (args[0] != args[1]) return {};
- return { nt::Tuple({ { "left", args[0] }, { "right", args[1] } }) };
+ t->generator = [](const std::vector<std::string>& args, std::size_t offset,
+ std::size_t limit) -> std::vector<nt::Tuple> {
+ if (args.size() < 2 || offset > 0 || limit == 0)
+ return {};
+ if (args[0] != args[1])
+ return {};
+ return {nt::Tuple({{"left", args[0]}, {"right", args[1]}})};
};
return t;
}
@@ -71,16 +68,15 @@ static std::unique_ptr<nt::ObjectManager::ephemeral_object_type> make_eq_type()
// Fixture: shared manager stack
// ---------------------------------------------------------------------------
-struct Fixture
-{
- nt::InMemoryBackend backend;
- nt::ObjectManager objects;
+struct Fixture {
+ nt::InMemoryBackend backend;
+ nt::ObjectManager objects;
nt::PermissionsManager permissions;
- nt::IdentityManager identities;
- nt::LifecycleManager lifecycles { objects, backend };
- nt::NamespaceReferenceManager references { objects, backend };
- nt::HandlerManager handler { objects, permissions, identities, lifecycles, references };
- nt::CursorManager cursors { backend, &lifecycles, &objects };
+ nt::IdentityManager identities;
+ nt::LifecycleManager lifecycles{objects, backend};
+ nt::NamespaceReferenceManager references{objects, backend};
+ nt::HandlerManager handler{objects, permissions, identities, lifecycles, references};
+ nt::CursorManager cursors{backend, &lifecycles, &objects};
int conn = 1;
};
@@ -88,14 +84,13 @@ struct Fixture
// Tests
// ---------------------------------------------------------------------------
-TEST_CASE("SCAN returns all tuples from a stored relation", "[scan]")
-{
+TEST_CASE("SCAN returns all tuples from a stored relation", "[scan]") {
Fixture f;
- const std::vector<std::string> path = { "relations", "villager" };
+ const std::vector<std::string> path = {"relations", "villager"};
f.objects.Register(path, std::make_unique<nt::ObjectManager::Relation>(), make_relation_type());
- store_tuple(f.backend, f.objects, path, { { "name", "John" }, { "age", "30" } });
- store_tuple(f.backend, f.objects, path, { { "name", "Peter" }, { "age", "25" } });
+ store_tuple(f.backend, f.objects, path, {{"name", "John"}, {"age", "30"}});
+ store_tuple(f.backend, f.objects, path, {{"name", "Peter"}, {"age", "25"}});
auto* handle = f.handler.Open(path, &f.conn);
REQUIRE(handle != nullptr);
@@ -104,12 +99,13 @@ TEST_CASE("SCAN returns all tuples from a stored relation", "[scan]")
REQUIRE(cursor != nullptr);
nt::PlanNode plan;
- plan.op = nt::PlanNode::Op::SCAN;
+ plan.op = nt::FOL_OPERATION_SCAN;
plan.scan_cursor = cursor;
nt::VM vm(f.cursors);
int count = 0;
- while (vm.Next(&plan)) ++count;
+ while (vm.Next(&plan))
+ ++count;
REQUIRE(count == 2);
@@ -117,25 +113,21 @@ TEST_CASE("SCAN returns all tuples from a stored relation", "[scan]")
f.handler.Close(handle);
}
-TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]")
-{
+TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]") {
Fixture f;
- const std::vector<std::string> people_path = { "relations", "people" };
- const std::vector<std::string> eq_path = { "builtins", "eq" };
+ const std::vector<std::string> people_path = {"relations", "people"};
+ const std::vector<std::string> eq_path = {"builtins", "eq"};
// Register stored relation: people(name, age)
- f.objects.Register(people_path,
- std::make_unique<nt::ObjectManager::Relation>(),
+ f.objects.Register(people_path, std::make_unique<nt::ObjectManager::Relation>(),
make_relation_type());
- store_tuple(f.backend, f.objects, people_path, { { "name", "John" }, { "age", "30" } });
- store_tuple(f.backend, f.objects, people_path, { { "name", "Peter" }, { "age", "25" } });
- store_tuple(f.backend, f.objects, people_path, { { "name", "Jude" }, { "age", "25" } });
+ store_tuple(f.backend, f.objects, people_path, {{"name", "John"}, {"age", "30"}});
+ store_tuple(f.backend, f.objects, people_path, {{"name", "Peter"}, {"age", "25"}});
+ store_tuple(f.backend, f.objects, people_path, {{"name", "Jude"}, {"age", "25"}});
// Register ephemeral eq relation
- f.objects.Register(eq_path,
- std::make_unique<nt::ObjectManager::Relation>(),
- make_eq_type());
+ f.objects.Register(eq_path, std::make_unique<nt::ObjectManager::Relation>(), make_eq_type());
// Open handles and cursors
auto* people_handle = f.handler.Open(people_path, &f.conn);
@@ -143,8 +135,8 @@ TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]")
auto* eq_handle = f.handler.Open(eq_path, &f.conn);
REQUIRE(eq_handle != nullptr);
- auto* people_rel = dynamic_cast<nt::ObjectManager::Relation*>(
- f.objects.Find(people_path)->object.get());
+ auto* people_rel =
+ dynamic_cast<nt::ObjectManager::Relation*>(f.objects.Find(people_path)->object.get());
auto* people_cursor = f.cursors.Open(people_handle, people_rel->merkle_root);
REQUIRE(people_cursor != nullptr);
auto* eq_cursor = f.cursors.Open(eq_handle); // EPHEMERAL: starts exhausted, JOIN resets
@@ -152,24 +144,23 @@ TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]")
// Plan: JOIN( SCAN(people), SCAN(eq[ Var("age"), Const("25") ]) )
nt::PlanNode right;
- right.op = nt::PlanNode::Op::SCAN;
+ right.op = nt::FOL_OPERATION_SCAN;
right.scan_cursor = eq_cursor;
- right.scan_args = { nt::PathArg::Var("age"), nt::PathArg::Const("25") };
+ right.scan_args = {nt::PathArg::Var("age"), nt::PathArg::Const("25")};
nt::PlanNode left;
- left.op = nt::PlanNode::Op::SCAN;
+ left.op = nt::FOL_OPERATION_SCAN;
left.scan_cursor = people_cursor;
nt::PlanNode root;
- root.op = nt::PlanNode::Op::JOIN;
- root.left = &left;
+ root.op = nt::FOL_OPERATION_JOIN;
+ root.left = &left;
root.right = &right;
// Execute and collect matched names
nt::VM vm(f.cursors);
std::vector<std::string> matched;
- while (nt::Tuple* t = vm.Next(&root))
- {
+ while (nt::Tuple* t = vm.Next(&root)) {
for (const auto& attr : t->attrs())
if (attr.name == "name")
matched.push_back(attr.value);
@@ -177,9 +168,9 @@ TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]")
// Jude (age=25) and Peter (age=25) should match; Jogn (age=30) should not
REQUIRE(matched.size() == 2);
- REQUIRE(std::find(matched.begin(), matched.end(), "Jude") != matched.end());
+ REQUIRE(std::find(matched.begin(), matched.end(), "Jude") != matched.end());
REQUIRE(std::find(matched.begin(), matched.end(), "Peter") != matched.end());
- REQUIRE(std::find(matched.begin(), matched.end(), "John") == matched.end());
+ REQUIRE(std::find(matched.begin(), matched.end(), "John") == matched.end());
f.cursors.Close(people_cursor);
f.cursors.Close(eq_cursor);
@@ -187,15 +178,14 @@ TEST_CASE("JOIN filters a stored relation with eq ephemeral", "[join]")
f.handler.Close(eq_handle);
}
-TEST_CASE("TAKE limits tuples emitted from a SCAN", "[take]")
-{
+TEST_CASE("TAKE limits tuples emitted from a SCAN", "[take]") {
Fixture f;
- const std::vector<std::string> path = { "relations", "items" };
+ const std::vector<std::string> path = {"relations", "items"};
f.objects.Register(path, std::make_unique<nt::ObjectManager::Relation>(), make_relation_type());
- store_tuple(f.backend, f.objects, path, { { "id", "1" } });
- store_tuple(f.backend, f.objects, path, { { "id", "2" } });
- store_tuple(f.backend, f.objects, path, { { "id", "3" } });
+ store_tuple(f.backend, f.objects, path, {{"id", "1"}});
+ store_tuple(f.backend, f.objects, path, {{"id", "2"}});
+ store_tuple(f.backend, f.objects, path, {{"id", "3"}});
auto* handle = f.handler.Open(path, &f.conn);
REQUIRE(handle != nullptr);
@@ -204,17 +194,18 @@ TEST_CASE("TAKE limits tuples emitted from a SCAN", "[take]")
REQUIRE(cursor != nullptr);
nt::PlanNode scan;
- scan.op = nt::PlanNode::Op::SCAN;
+ scan.op = nt::FOL_OPERATION_SCAN;
scan.scan_cursor = cursor;
nt::PlanNode take;
- take.op = nt::PlanNode::Op::TAKE;
- take.left = &scan;
+ take.op = nt::FOL_OPERATION_TAKE;
+ take.left = &scan;
take.take_limit = 2;
nt::VM vm(f.cursors);
int count = 0;
- while (vm.Next(&take)) ++count;
+ while (vm.Next(&take))
+ ++count;
REQUIRE(count == 2);
@@ -222,13 +213,12 @@ TEST_CASE("TAKE limits tuples emitted from a SCAN", "[take]")
f.handler.Close(handle);
}
-TEST_CASE("PROJECT filters tuple attributes by name", "[project]")
-{
+TEST_CASE("PROJECT filters tuple attributes by name", "[project]") {
Fixture f;
- const std::vector<std::string> path = { "relations", "people" };
+ const std::vector<std::string> path = {"relations", "people"};
f.objects.Register(path, std::make_unique<nt::ObjectManager::Relation>(), make_relation_type());
- store_tuple(f.backend, f.objects, path, { { "name", "John" }, { "age", "30" }, { "city", "Lisbon" } });
+ store_tuple(f.backend, f.objects, path, {{"name", "John"}, {"age", "30"}, {"city", "Lisbon"}});
auto* handle = f.handler.Open(path, &f.conn);
REQUIRE(handle != nullptr);
@@ -237,20 +227,21 @@ TEST_CASE("PROJECT filters tuple attributes by name", "[project]")
REQUIRE(cursor != nullptr);
nt::PlanNode scan;
- scan.op = nt::PlanNode::Op::SCAN;
+ scan.op = nt::FOL_OPERATION_SCAN;
scan.scan_cursor = cursor;
nt::PlanNode project;
- project.op = nt::PlanNode::Op::PROJECT;
- project.left = &scan;
- project.project_attrs = { "name", "city" };
+ project.op = nt::FOL_OPERATION_PROJECT;
+ project.left = &scan;
+ project.project_attrs = {"name", "city"};
nt::VM vm(f.cursors);
nt::Tuple* t = vm.Next(&project);
REQUIRE(t != nullptr);
std::vector<std::string> names;
- for (const auto& attr : t->attrs()) names.push_back(attr.name);
+ for (const auto& attr : t->attrs())
+ names.push_back(attr.name);
REQUIRE(names.size() == 2);
REQUIRE(std::find(names.begin(), names.end(), "name") != names.end());