Files
clice/tests/unit/support/context_bitmap_tests.cpp
ykiko e1dde9ee92 fix(index): address pre-PR review findings
- Fix use-after-partial-move and division-by-zero in benchmark
- Use std::exchange in ContextBitmap move operations
- Add clarifying comments on operator== mixed mode and hash() sorting
- Add tests: compilation-context merge cache hit, hash determinism,
  any_not_in empty inputs, remove edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:49:39 +08:00

298 lines
6.0 KiB
C++

#include "test/test.h"
#include "support/bitmap.h"
namespace clice::testing {
namespace {
TEST_SUITE(ContextBitmap) {
TEST_CASE(EmptyByDefault) {
ContextBitmap bm;
EXPECT_TRUE(bm.is_empty());
}
TEST_CASE(AddInline) {
ContextBitmap bm;
bm.add(0);
EXPECT_FALSE(bm.is_empty());
bm.add(62);
EXPECT_FALSE(bm.is_empty());
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(0));
EXPECT_TRUE(r.contains(62));
EXPECT_FALSE(r.contains(1));
EXPECT_EQ(r.cardinality(), 2U);
}
TEST_CASE(RemoveInline) {
ContextBitmap bm;
bm.add(5);
bm.add(10);
bm.remove(5);
auto r = bm.to_roaring();
EXPECT_FALSE(r.contains(5));
EXPECT_TRUE(r.contains(10));
EXPECT_EQ(r.cardinality(), 1U);
bm.remove(10);
EXPECT_TRUE(bm.is_empty());
}
TEST_CASE(UpgradeToHeap) {
ContextBitmap bm;
bm.add(5);
bm.add(63);
EXPECT_FALSE(bm.is_empty());
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(5));
EXPECT_TRUE(r.contains(63));
EXPECT_EQ(r.cardinality(), 2U);
}
TEST_CASE(HeapAddRemove) {
ContextBitmap bm;
bm.add(100);
bm.add(200);
bm.remove(100);
auto r = bm.to_roaring();
EXPECT_FALSE(r.contains(100));
EXPECT_TRUE(r.contains(200));
}
TEST_CASE(AnyNotInBothInline) {
ContextBitmap a, b;
a.add(1);
a.add(2);
b.add(1);
b.add(2);
EXPECT_FALSE(a.any_not_in(b));
a.add(3);
EXPECT_TRUE(a.any_not_in(b));
}
TEST_CASE(AnyNotInInlineVsHeap) {
ContextBitmap a, b;
a.add(5);
b.add(5);
b.add(100);
EXPECT_FALSE(a.any_not_in(b));
a.add(10);
EXPECT_TRUE(a.any_not_in(b));
}
TEST_CASE(AnyNotInHeapVsInline) {
ContextBitmap a, b;
a.add(100);
b.add(5);
// 100 >= 63 not in inline b
EXPECT_TRUE(a.any_not_in(b));
ContextBitmap c, d;
c.add(5);
c.add(100);
d.add(5);
// c has 100 which d doesn't have
EXPECT_TRUE(c.any_not_in(d));
}
TEST_CASE(AnyNotInBothHeap) {
ContextBitmap a, b;
a.add(100);
a.add(200);
b.add(100);
b.add(200);
EXPECT_FALSE(a.any_not_in(b));
a.add(300);
EXPECT_TRUE(a.any_not_in(b));
}
TEST_CASE(MoveConstruct) {
ContextBitmap a;
a.add(5);
a.add(10);
ContextBitmap b(std::move(a));
EXPECT_TRUE(a.is_empty());
EXPECT_FALSE(b.is_empty());
auto r = b.to_roaring();
EXPECT_TRUE(r.contains(5));
EXPECT_TRUE(r.contains(10));
}
TEST_CASE(MoveConstructHeap) {
ContextBitmap a;
a.add(100);
ContextBitmap b(std::move(a));
EXPECT_TRUE(a.is_empty());
auto r = b.to_roaring();
EXPECT_TRUE(r.contains(100));
}
TEST_CASE(MoveAssign) {
ContextBitmap a, b;
a.add(3);
b.add(7);
b = std::move(a);
EXPECT_TRUE(a.is_empty());
auto r = b.to_roaring();
EXPECT_TRUE(r.contains(3));
EXPECT_FALSE(r.contains(7));
}
TEST_CASE(FromRoaringInline) {
roaring::Roaring r;
r.add(0);
r.add(30);
r.add(62);
auto bm = ContextBitmap::from_roaring(r);
EXPECT_FALSE(bm.is_empty());
auto out = bm.to_roaring();
EXPECT_EQ(out.cardinality(), 3U);
EXPECT_TRUE(out.contains(0));
EXPECT_TRUE(out.contains(30));
EXPECT_TRUE(out.contains(62));
}
TEST_CASE(FromRoaringHeap) {
roaring::Roaring r;
r.add(5);
r.add(100);
auto bm = ContextBitmap::from_roaring(r);
auto out = bm.to_roaring();
EXPECT_EQ(out.cardinality(), 2U);
EXPECT_TRUE(out.contains(5));
EXPECT_TRUE(out.contains(100));
}
TEST_CASE(FromRoaringEmpty) {
roaring::Roaring r;
auto bm = ContextBitmap::from_roaring(r);
EXPECT_TRUE(bm.is_empty());
}
TEST_CASE(Equality) {
ContextBitmap a, b;
a.add(1);
a.add(2);
b.add(1);
b.add(2);
EXPECT_TRUE(a == b);
b.add(3);
EXPECT_FALSE(a == b);
}
TEST_CASE(BoundaryId62) {
ContextBitmap bm;
bm.add(62);
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(62));
EXPECT_EQ(r.cardinality(), 1U);
}
TEST_CASE(BoundaryId63) {
ContextBitmap bm;
bm.add(63);
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(63));
EXPECT_EQ(r.cardinality(), 1U);
}
TEST_CASE(MoveAssignHeap) {
ContextBitmap a, b;
a.add(100);
a.add(200);
b.add(300);
b.add(400);
b = std::move(a);
EXPECT_TRUE(a.is_empty());
auto r = b.to_roaring();
EXPECT_TRUE(r.contains(100));
EXPECT_TRUE(r.contains(200));
EXPECT_FALSE(r.contains(300));
EXPECT_FALSE(r.contains(400));
}
TEST_CASE(EqualityHeap) {
ContextBitmap a, b;
a.add(100);
a.add(200);
b.add(100);
b.add(200);
EXPECT_TRUE(a == b);
b.add(300);
EXPECT_FALSE(a == b);
}
TEST_CASE(AddSmallAfterUpgrade) {
ContextBitmap bm;
bm.add(100);
bm.add(5);
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(100));
EXPECT_TRUE(r.contains(5));
EXPECT_EQ(r.cardinality(), 2U);
}
TEST_CASE(AnyNotInEmpty) {
ContextBitmap empty_a, empty_b, non_empty;
non_empty.add(5);
EXPECT_FALSE(empty_a.any_not_in(non_empty));
EXPECT_TRUE(non_empty.any_not_in(empty_b));
EXPECT_FALSE(empty_a.any_not_in(empty_b));
}
TEST_CASE(RemoveNonexistent) {
// Inline mode: remove id that was never added.
ContextBitmap inline_bm;
inline_bm.add(3);
inline_bm.remove(10);
EXPECT_FALSE(inline_bm.is_empty());
auto r1 = inline_bm.to_roaring();
EXPECT_TRUE(r1.contains(3));
EXPECT_EQ(r1.cardinality(), 1U);
// Heap mode: remove id that was never added.
ContextBitmap heap_bm;
heap_bm.add(100);
heap_bm.remove(200);
EXPECT_FALSE(heap_bm.is_empty());
auto r2 = heap_bm.to_roaring();
EXPECT_TRUE(r2.contains(100));
EXPECT_EQ(r2.cardinality(), 1U);
}
TEST_CASE(RemoveLargeInInline) {
ContextBitmap bm;
bm.add(5);
bm.add(10);
// Remove id >= 63 while in inline mode; should be a no-op.
bm.remove(63);
bm.remove(100);
EXPECT_FALSE(bm.is_empty());
auto r = bm.to_roaring();
EXPECT_TRUE(r.contains(5));
EXPECT_TRUE(r.contains(10));
EXPECT_EQ(r.cardinality(), 2U);
}
}; // TEST_SUITE(ContextBitmap)
} // namespace
} // namespace clice::testing