/* * Copyright (c) 2026, Luke Wilde * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include class TestCell : public GC::Cell { GC_CELL(TestCell, GC::Cell); GC_DECLARE_ALLOCATOR(TestCell); // Padding to satisfy minimum cell size (must be >= sizeof(FreelistEntry)). u8 m_padding[16] {}; }; GC_DEFINE_ALLOCATOR(TestCell); static GC::Heap& test_heap() { static GC::Heap heap([](auto&) { }); return heap; } class TestVisitor : public GC::Cell::Visitor { virtual void visit_impl(GC::Cell& cell) override { visited_cells.set(&cell); } virtual void visit_impl(ReadonlySpan) override { } virtual void visit_possible_values(ReadonlyBytes) override { } public: HashTable visited_cells; }; static bool possible_values_contain(GC::ConservativeVectorBase const& container, GC::Cell* cell) { auto target = bit_cast(cell); for (auto value : container.possible_values()) { if (value == target) return true; } return false; } TEST_CASE(root_vector_reports_roots) { auto& heap = test_heap(); GC::RootVector> vector(heap); auto cell = heap.allocate(); vector.append(cell); HashMap roots; vector.gather_roots(roots); EXPECT(roots.contains(cell.ptr())); EXPECT_EQ(roots.size(), 1u); } TEST_CASE(root_vector_ptr_reports_roots) { auto& heap = test_heap(); GC::RootVector> vector(heap); auto cell = heap.allocate(); vector.append(cell); HashMap roots; vector.gather_roots(roots); EXPECT(roots.contains(cell.ptr())); EXPECT_EQ(roots.size(), 1u); } TEST_CASE(root_hash_map_value_reports_roots) { auto& heap = test_heap(); GC::RootHashMap> map(heap); auto cell = heap.allocate(); map.set(42, cell); HashMap roots; map.gather_roots(roots); EXPECT(roots.contains(cell.ptr())); EXPECT_EQ(roots.size(), 1u); } TEST_CASE(root_hash_map_key_reports_roots) { auto& heap = test_heap(); GC::RootHashMap, int> map(heap); auto cell = heap.allocate(); map.set(cell, 42); HashMap roots; map.gather_roots(roots); EXPECT(roots.contains(cell.ptr())); EXPECT_EQ(roots.size(), 1u); } TEST_CASE(root_hash_map_key_and_value_reports_roots) { auto& heap = test_heap(); GC::RootHashMap, GC::Ref> map(heap); auto key_cell = heap.allocate(); auto value_cell = heap.allocate(); map.set(key_cell, value_cell); HashMap roots; map.gather_roots(roots); EXPECT(roots.contains(key_cell.ptr())); EXPECT(roots.contains(value_cell.ptr())); EXPECT_EQ(roots.size(), 2u); } TEST_CASE(root_hash_map_non_gc_key_skipped) { auto& heap = test_heap(); GC::RootHashMap> map(heap); auto cell = heap.allocate(); map.set(42, cell); HashMap roots; map.gather_roots(roots); // Only the value should be reported, not the int key EXPECT_EQ(roots.size(), 1u); EXPECT(roots.contains(cell.ptr())); } TEST_CASE(cleared_container_reports_no_roots) { auto& heap = test_heap(); GC::RootVector> vector(heap); auto cell = heap.allocate(); vector.append(cell); vector.clear(); HashMap roots; vector.gather_roots(roots); EXPECT_EQ(roots.size(), 0u); } TEST_CASE(conservative_vector_reports_possible_values) { auto& heap = test_heap(); GC::ConservativeVector> vector(heap); auto cell = heap.allocate(); vector.append(cell); EXPECT(possible_values_contain(vector, cell.ptr())); } TEST_CASE(heap_vector_visit_edges_reports_cells) { auto& heap = test_heap(); auto vector = heap.allocate>>(); auto cell = heap.allocate(); vector->elements().append(cell); TestVisitor visitor; vector->visit_edges(visitor); EXPECT(visitor.visited_cells.contains(cell.ptr())); } TEST_CASE(empty_heap_vector_visit_edges_reports_nothing) { auto& heap = test_heap(); auto vector = heap.allocate>>(); TestVisitor visitor; vector->visit_edges(visitor); EXPECT_EQ(visitor.visited_cells.size(), 0u); } TEST_CASE(heap_hash_table_visit_edges_reports_cells) { auto& heap = test_heap(); auto table = heap.allocate>>(); auto cell = heap.allocate(); table->table().set(cell); TestVisitor visitor; table->visit_edges(visitor); EXPECT(visitor.visited_cells.contains(cell.ptr())); } TEST_CASE(empty_heap_hash_table_visit_edges_reports_nothing) { auto& heap = test_heap(); auto table = heap.allocate>>(); TestVisitor visitor; table->visit_edges(visitor); EXPECT_EQ(visitor.visited_cells.size(), 0u); }