/** @file StringTable.test.cpp * * @author Roland Conybeare, Jan 2026 **/ #include #include #include namespace xo { using xo::scm::StringTable; using xo::scm::DString; namespace ut { TEST_CASE("StringTable-lookup-empty", "[expression2][StringTable]") { StringTable table(1024); // lookup on empty table returns nullptr REQUIRE(table.lookup("foo") == nullptr); REQUIRE(table.lookup("") == nullptr); } TEST_CASE("StringTable-intern", "[expression2][StringTable]") { StringTable table(1024); const DString * s1 = table.intern("hello"); REQUIRE(s1 != nullptr); REQUIRE(std::strcmp(s1->chars(), "hello") == 0); REQUIRE(s1->size() == 5); } TEST_CASE("StringTable-intern-idempotent", "[expression2][StringTable]") { StringTable table(1024); const DString * s1 = table.intern("hello"); const DString * s2 = table.intern("hello"); // same key returns same pointer REQUIRE(s1 != nullptr); REQUIRE(s2 != nullptr); REQUIRE(s1 == s2); } TEST_CASE("StringTable-lookup-after-intern", "[expression2][StringTable]") { StringTable table(1024); REQUIRE(table.lookup("hello") == nullptr); const DString * s1 = table.intern("hello"); const DString * s2 = table.lookup("hello"); REQUIRE(s2 != nullptr); REQUIRE(s1 == s2); } TEST_CASE("StringTable-multiple-strings", "[expression2][StringTable]") { StringTable table(1024); const DString * s1 = table.intern("apple"); const DString * s2 = table.intern("banana"); const DString * s3 = table.intern("cherry"); // all different pointers REQUIRE(s1 != s2); REQUIRE(s2 != s3); REQUIRE(s1 != s3); // correct contents REQUIRE(std::strcmp(s1->chars(), "apple") == 0); REQUIRE(std::strcmp(s2->chars(), "banana") == 0); REQUIRE(std::strcmp(s3->chars(), "cherry") == 0); // lookup still works REQUIRE(table.lookup("apple") == s1); REQUIRE(table.lookup("banana") == s2); REQUIRE(table.lookup("cherry") == s3); REQUIRE(table.lookup("date") == nullptr); } TEST_CASE("StringTable-intern-empty-string", "[expression2][StringTable]") { StringTable table(1024); const DString * s1 = table.intern(""); REQUIRE(s1 != nullptr); REQUIRE(s1->size() == 0); REQUIRE(s1->chars()[0] == '\0'); // idempotent for empty string too const DString * s2 = table.intern(""); REQUIRE(s1 == s2); } TEST_CASE("StringTable-verify_ok", "[expression2][StringTable]") { StringTable table(4096); { INFO("1. empty table"); // empty table passes verify_ok REQUIRE(table.verify_ok()); } // after interning strings, still passes { INFO("2. intern(hello)"); table.intern("hello"); REQUIRE(table.verify_ok()); } { INFO("3. intern(world)"); table.intern("world"); REQUIRE(table.verify_ok()); } { INFO("4. intern(foo)"); table.intern("foo"); REQUIRE(table.verify_ok()); } { INFO("5. intern(bar)"); table.intern("bar"); REQUIRE(table.verify_ok()); } // idempotent intern doesn't break invariants { INFO("6. intern(hello)"); table.intern("hello"); REQUIRE(table.verify_ok()); } { INFO("7. intern(world)"); table.intern("world"); REQUIRE(table.verify_ok()); } } } /*namespace ut*/ } /*namespace xo*/ /* end StringTable.test.cpp */