Files
wifi-densepose/crates/ruvector-mincut/tests/coverage_tests.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

340 lines
9.2 KiB
Rust

//! Comprehensive coverage tests for ruvector-mincut
//!
//! Ensures 100% test coverage across all modules.
use ruvector_mincut::certificate::{AuditData, AuditEntryType, AuditLogger, CutCertificate};
use ruvector_mincut::connectivity::DynamicConnectivity;
use ruvector_mincut::instance::{InstanceResult, StubInstance, WitnessHandle};
use ruvector_mincut::prelude::*;
use ruvector_mincut::wrapper::MinCutWrapper;
use std::sync::Arc;
// ============================================================================
// Connectivity Tests
// ============================================================================
#[test]
fn test_connectivity_edge_cases() {
let mut dc = DynamicConnectivity::new();
// Empty graph
assert!(!dc.is_connected());
assert_eq!(dc.component_count(), 0);
// Single vertex
dc.add_vertex(0);
assert!(dc.is_connected()); // Single vertex is connected
assert_eq!(dc.component_count(), 1);
// Two isolated vertices
dc.add_vertex(1);
assert!(!dc.is_connected());
assert_eq!(dc.component_count(), 2);
}
#[test]
fn test_connectivity_path_compression() {
let mut dc = DynamicConnectivity::new();
// Build long chain: 0-1-2-3-4-5
for i in 0..5 {
dc.insert_edge(i, i + 1);
}
// Query should trigger path compression
assert!(dc.connected(0, 5));
assert!(dc.connected(0, 3));
assert!(dc.connected(2, 4));
}
#[test]
fn test_connectivity_multiple_components() {
let mut dc = DynamicConnectivity::new();
// Component 1: 0-1-2
dc.insert_edge(0, 1);
dc.insert_edge(1, 2);
// Component 2: 3-4
dc.insert_edge(3, 4);
// Component 3: 5 alone
dc.add_vertex(5);
assert_eq!(dc.component_count(), 3);
assert!(dc.connected(0, 2));
assert!(dc.connected(3, 4));
assert!(!dc.connected(0, 3));
assert!(!dc.connected(0, 5));
}
// ============================================================================
// Witness Tests
// ============================================================================
#[test]
fn test_witness_large_vertex_ids() {
use roaring::RoaringBitmap;
// Test with vertex IDs near u32::MAX limit
let mut membership = RoaringBitmap::new();
membership.insert(u32::MAX - 1);
membership.insert(u32::MAX);
let witness = WitnessHandle::new((u32::MAX - 1) as u64, membership, 5);
assert!(witness.contains((u32::MAX - 1) as u64));
assert!(witness.contains(u32::MAX as u64));
assert!(!witness.contains(0));
}
#[test]
fn test_witness_equality() {
use roaring::RoaringBitmap;
let mut m1 = RoaringBitmap::new();
m1.insert(1);
m1.insert(2);
let mut m2 = RoaringBitmap::new();
m2.insert(1);
m2.insert(2);
let w1 = WitnessHandle::new(1, m1, 5);
let w2 = WitnessHandle::new(1, m2, 5);
assert_eq!(w1, w2);
}
#[test]
fn test_witness_materialize() {
use roaring::RoaringBitmap;
let mut membership = RoaringBitmap::new();
membership.insert(1);
membership.insert(3);
membership.insert(5);
let witness = WitnessHandle::new(1, membership, 3);
let (u, v_minus_u) = witness.materialize_partition();
assert!(u.contains(&1));
assert!(u.contains(&3));
assert!(u.contains(&5));
assert_eq!(u.len(), 3);
// v_minus_u contains 0, 2, 4 (up to max which is 5)
assert!(v_minus_u.contains(&0));
assert!(v_minus_u.contains(&2));
assert!(v_minus_u.contains(&4));
}
// ============================================================================
// Instance Tests
// ============================================================================
#[test]
fn test_stub_instance_range_behavior() {
let graph = DynamicGraph::new();
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(1, 2, 1.0).unwrap();
// Range [0, 0] - min cut 1 is above range
let mut instance = StubInstance::new(&graph, 0, 0);
assert!(matches!(instance.query(), InstanceResult::AboveRange));
// Range [0, 1] - min cut 1 is in range
let mut instance = StubInstance::new(&graph, 0, 1);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
// Range [0, 10] - min cut 1 is in range
let mut instance = StubInstance::new(&graph, 0, 10);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
}
// ============================================================================
// Wrapper Tests
// ============================================================================
#[test]
fn test_wrapper_time_tracking() {
let graph = Arc::new(DynamicGraph::new());
let mut wrapper = MinCutWrapper::new(graph);
assert_eq!(wrapper.current_time(), 0);
wrapper.insert_edge(0, 1, 2);
assert_eq!(wrapper.current_time(), 1);
wrapper.insert_edge(1, 3, 4);
assert_eq!(wrapper.current_time(), 2);
wrapper.delete_edge(0, 1, 2);
assert_eq!(wrapper.current_time(), 3);
}
// ============================================================================
// Certificate Tests
// ============================================================================
#[test]
fn test_certificate_json_roundtrip() {
let cert = CutCertificate::new();
let json = cert.to_json().expect("Failed to serialize certificate");
// Verify JSON is valid and contains expected fields
assert!(json.contains("\"witness_summaries\""));
assert!(json.contains("\"version\""));
assert!(json.contains("\"localkcut_responses\""));
assert!(json.contains("\"timestamp\""));
}
#[test]
fn test_audit_logger_capacity() {
let logger = AuditLogger::new(5);
// Add more than capacity
for i in 0..10 {
logger.log(
AuditEntryType::WitnessCreated,
AuditData::Witness {
hash: i,
boundary: i,
seed: i,
},
);
}
// Should only have last 5
let entries = logger.export();
assert_eq!(entries.len(), 5);
}
#[test]
fn test_audit_logger_filtering() {
let logger = AuditLogger::new(100);
logger.log(
AuditEntryType::WitnessCreated,
AuditData::Witness {
hash: 1,
boundary: 1,
seed: 1,
},
);
logger.log(
AuditEntryType::LocalKCutQuery,
AuditData::Query {
budget: 5,
radius: 10,
seeds: vec![1],
},
);
logger.log(
AuditEntryType::WitnessCreated,
AuditData::Witness {
hash: 2,
boundary: 2,
seed: 2,
},
);
let recent = logger.recent(2);
assert_eq!(recent.len(), 2);
}
// ============================================================================
// Integration Tests
// ============================================================================
#[test]
fn test_full_workflow_path_to_cycle() {
let graph = Arc::new(DynamicGraph::new());
let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
// Build path: 0-1-2-3
for i in 0..3 {
let edge_id = graph.insert_edge(i, i + 1, 1.0).unwrap();
wrapper.insert_edge(edge_id, i, i + 1);
}
let result = wrapper.query();
assert_eq!(result.value(), 1); // Path has min cut 1
// Add edge to form cycle
let edge_id = graph.insert_edge(3, 0, 1.0).unwrap();
wrapper.insert_edge(edge_id, 3, 0);
let result = wrapper.query();
assert_eq!(result.value(), 2); // Cycle has min cut 2
}
#[test]
fn test_full_workflow_split_and_merge() {
let graph = Arc::new(DynamicGraph::new());
let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
// Build triangle
let e1 = graph.insert_edge(0, 1, 1.0).unwrap();
let e2 = graph.insert_edge(1, 2, 1.0).unwrap();
let e3 = graph.insert_edge(2, 0, 1.0).unwrap();
wrapper.insert_edge(e1, 0, 1);
wrapper.insert_edge(e2, 1, 2);
wrapper.insert_edge(e3, 2, 0);
assert_eq!(wrapper.query().value(), 2);
// Delete edge to break cycle
graph.delete_edge(2, 0).unwrap();
wrapper.delete_edge(e3, 2, 0);
assert_eq!(wrapper.query().value(), 1); // Now a path
}
// ============================================================================
// Edge Cases
// ============================================================================
#[test]
fn test_self_loop_ignored() {
let graph = Arc::new(DynamicGraph::new());
// Self-loops should be ignored or handled gracefully
let _result = graph.insert_edge(0, 0, 1.0);
// Implementation may accept or reject - just ensure no panic
}
#[test]
fn test_parallel_edges() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
// Second edge between same vertices
let _result = graph.insert_edge(0, 1, 1.0);
// Should either fail (EdgeExists) or succeed
// Just ensure consistent behavior
}
#[test]
fn test_large_vertex_ids() {
let graph = Arc::new(DynamicGraph::new());
let large_id = 1_000_000u64;
graph.insert_edge(large_id, large_id + 1, 1.0).unwrap();
let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
wrapper.insert_edge(0, large_id, large_id + 1);
let result = wrapper.query();
assert!(result.is_connected());
}