use super::super::super::{EdgeId, Timestamp, VertexId};
use super::super::overflow::OVERFLOW_REPACK_CHUNKS_PER_VERTEX;
use super::super::row::{graded_overflow_chunk_edges, OVERFLOW_CHUNK_MAX, OVERFLOW_CHUNK_MIN};
use super::super::MutableCsr;
#[test]
fn test_overflow_insert() {
let mut csr = MutableCsr::with_capacity(10, 100);
csr.insert_edge(0u32, VertexId::edge_endpoint_key(1, 0), EdgeId(100), 1)
.unwrap();
csr.insert_edge(0u32, VertexId::edge_endpoint_key(2, 0), EdgeId(101), 1)
.unwrap();
csr.insert_edge(0u32, VertexId::edge_endpoint_key(3, 0), EdgeId(102), 1)
.unwrap();
csr.insert_edge(0u32, VertexId::edge_endpoint_key(4, 0), EdgeId(103), 1)
.unwrap();
csr.insert_edge(0u32, VertexId::edge_endpoint_key(5, 0), EdgeId(104), 1)
.unwrap();
assert_eq!(csr.edge_count(), 5);
let edges = csr.edges_of(0u32, 1);
assert_eq!(edges.len(), 5);
assert!(csr
.insert_edge(0u32, VertexId::edge_endpoint_key(5, 0), EdgeId(105), 1)
.is_err());
assert!(csr.delete_edge(0u32, EdgeId(104), 2).unwrap());
}
#[test]
fn test_overflow_storage_lookup() {
let mut csr = MutableCsr::with_overflow_chunk_edges(10, 100, 2);
for vid in 0..5u32 {
for i in 0..6 {
let dst = VertexId::edge_endpoint_key((vid + 1) * 100 + i as u32, 0);
csr.insert_edge(vid, dst, EdgeId(vid as u64 * 10 + i as u64), 1)
.unwrap();
}
}
assert!(csr.get_overflow_chunks(0).is_some());
assert!(csr.get_overflow_chunks(999).is_none());
}
#[test]
fn test_overflow_get_chunks_transparent() {
let mut csr = MutableCsr::with_overflow_chunk_edges(10, 100, 2);
for vid in 0..20u32 {
for i in 0..6 {
let dst = VertexId::edge_endpoint_key((vid + 1) * 100 + i as u32, 0);
csr.insert_edge(vid, dst, EdgeId(vid as u64 * 10 + i as u64), 1)
.unwrap();
}
}
for vid in 0..20u32 {
let chunks = csr.get_overflow_chunks(vid).expect("should have overflow");
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0].len(), 2);
}
for vid in 0..20u32 {
let edges = csr.edges_of(vid, 1);
assert_eq!(edges.len(), 6);
}
}
#[test]
fn test_supernode_overflow_consolidates_repack_into_single_block() {
let mut csr = MutableCsr::with_overflow_chunk_edges(1, 4, 32);
for i in 0..4_096u64 {
csr.insert_edge(
0,
VertexId::edge_endpoint_key(i as u32 + 1, 0),
EdgeId(i + 1),
1,
)
.unwrap();
}
let chunks = csr.overflow_chunks.get(0).expect("vertex 0 has overflow");
assert!(
chunks.len() <= OVERFLOW_REPACK_CHUNKS_PER_VERTEX + 1,
"overflow chain must stay bounded, got {}",
chunks.len()
);
let consolidated = chunks.iter().filter(|chunk| chunk.capacity() > 32).count();
assert!(
consolidated <= 1,
"at most one consolidated block per row, got {}",
consolidated
);
assert!(chunks.iter().all(|chunk| chunk.len() <= chunk.capacity()));
assert_eq!(csr.physical_edges_of(0).len(), 4_096);
assert_eq!(csr.edges_of(0, 1).len(), 4_096);
}
#[test]
fn test_graded_overflow_tiers_bound_small_row_chunks() {
assert_eq!(graded_overflow_chunk_edges(0), OVERFLOW_CHUNK_MIN);
assert_eq!(graded_overflow_chunk_edges(1), OVERFLOW_CHUNK_MIN);
assert_eq!(graded_overflow_chunk_edges(5), OVERFLOW_CHUNK_MIN);
assert_eq!(graded_overflow_chunk_edges(64), 64);
assert_eq!(graded_overflow_chunk_edges(65), 128);
assert_eq!(graded_overflow_chunk_edges(1024), 1024);
assert_eq!(graded_overflow_chunk_edges(1025), 2048);
assert_eq!(graded_overflow_chunk_edges(1 << 20), OVERFLOW_CHUNK_MAX);
let mut csr = MutableCsr::with_capacity(10, 100);
for i in 1..=300i64 {
csr.insert_edge(
0u32,
VertexId::edge_endpoint_key((i) as u32, 0),
EdgeId(i as u64),
1,
)
.unwrap();
}
let chunks = csr.get_overflow_chunks(0).expect("vertex 0 has overflow");
assert_eq!(chunks[0].capacity(), OVERFLOW_CHUNK_MIN);
assert!(
chunks
.iter()
.all(|chunk| chunk.capacity() <= OVERFLOW_CHUNK_MAX),
"graded chunks must stay at or below the cap for 300 live edges"
);
assert_eq!(csr.edges_of(0u32, 1).len(), 300);
assert!(
chunks.len() <= OVERFLOW_REPACK_CHUNKS_PER_VERTEX + 1,
"overflow chunks must stay bounded, got {}",
chunks.len()
);
}
#[test]
fn test_overflow_repack_preserves_unexpired_tombstones() {
let mut csr = MutableCsr::with_overflow_chunk_edges(10, 100, 2);
for i in 0..8u64 {
csr.insert_edge(
0u32,
VertexId::edge_endpoint_key(100 + i as u32, 0),
EdgeId(i),
1,
)
.unwrap();
}
assert!(csr.delete_edge(0u32, EdgeId(6), 10).unwrap());
assert!(csr.delete_edge(0u32, EdgeId(7), 10).unwrap());
let overflow_before: usize = csr
.get_overflow_chunks(0)
.map(|chunks| chunks.iter().map(|chunk| chunk.len()).sum())
.unwrap_or(0);
assert!(overflow_before > 0);
let mut reported = Vec::new();
csr.compact_overflow_for_vertex(0, Timestamp::MAX, &mut |id, ts| reported.push((id, ts)));
assert!(reported.is_empty());
let kept: usize = csr
.get_overflow_chunks(0)
.map(|chunks| chunks.iter().map(|chunk| chunk.len()).sum())
.unwrap_or(0);
assert_eq!(kept, overflow_before);
let mut reported = Vec::new();
csr.compact_overflow_for_vertex(0, 10, &mut |id, ts| reported.push((id, ts)));
assert_eq!(reported.len(), 2);
let kept: usize = csr
.get_overflow_chunks(0)
.map(|chunks| chunks.iter().map(|chunk| chunk.len()).sum())
.unwrap_or(0);
assert_eq!(kept, overflow_before - 2);
}
#[test]
fn test_overflow_repack_consolidates_to_single_chunk() {
let mut csr = MutableCsr::with_overflow_chunk_edges(10, 100, 2);
for i in 0..24u64 {
csr.insert_edge(
0u32,
VertexId::edge_endpoint_key(100 + i as u32, 0),
EdgeId(i),
1,
)
.unwrap();
}
let before = csr.physical_edges_of(0);
assert_eq!(before.len(), 24);
let mut noop = |_: EdgeId, _: Timestamp| {};
csr.compact_overflow_for_vertex(0, Timestamp::MAX, &mut noop);
let chunks = csr
.get_overflow_chunks(0)
.expect("overflow remains after preserve-all repack");
assert_eq!(
chunks.len(),
1,
"repack must consolidate the row to one contiguous chunk"
);
assert_eq!(csr.physical_edges_of(0), before);
assert_eq!(csr.edges_of(0u32, 1).len(), 24);
}
#[test]
fn test_graded_chunks_monotonic_capped_and_floored() {
let mut prev = graded_overflow_chunk_edges(0);
assert_eq!(prev, OVERFLOW_CHUNK_MIN);
for live in 1..(1 << 22) {
let cur = graded_overflow_chunk_edges(live);
assert!(cur >= prev, "live={live} cur={cur} prev={prev}");
assert!(cur >= OVERFLOW_CHUNK_MIN, "live={live} cur={cur}");
assert!(cur <= OVERFLOW_CHUNK_MAX, "live={live} cur={cur}");
prev = cur;
if live >= OVERFLOW_CHUNK_MAX && cur == OVERFLOW_CHUNK_MAX {
break;
}
}
assert_eq!(prev, OVERFLOW_CHUNK_MAX);
}