use std::sync::Arc;
use crate::optimizer::stats::StatisticsManager;
use linkrs_core::value::Value;
use super::*;
use super::super::config::ESTIMATED_ROW_WIDTH_BYTES;
#[test]
fn test_calculate_scan_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let cost = calculator.calculate_scan_vertices_cost("test", "NonExistent");
assert_eq!(cost, 0.0);
}
#[test]
fn scan_cost_includes_io_and_point_lookup_is_cheaper() {
use crate::optimizer::stats::TagStatistics;
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 1000;
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
let scan = calculator.calculate_scan_vertices_cost("test", "person");
assert!(scan > 10.0, "scan={scan} should include I/O");
let point = calculator.calculate_get_vertices_cost(1);
assert!(point < scan, "point={point} scan={scan}");
assert_eq!(calculator.calculate_get_vertices_cost(0), 0.0);
}
#[test]
fn scan_cost_grows_with_allocated_slots() {
use crate::optimizer::stats::TagStatistics;
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 1000;
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
let no_holes = calculator.calculate_scan_vertices_cost("test", "person");
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 1000;
tag_stats.allocated_slots = Some(5000);
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
let with_holes = calculator.calculate_scan_vertices_cost("test", "person");
assert!(
with_holes > no_holes,
"with_holes={with_holes} no_holes={no_holes}"
);
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 1000;
tag_stats.allocated_slots = Some(10);
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
assert_eq!(
calculator.calculate_scan_vertices_cost("test", "person"),
no_holes
);
}
#[test]
fn index_scan_heap_cost_grows_with_hole_rate() {
use crate::optimizer::stats::TagStatistics;
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 10_000;
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
let no_holes = calculator.calculate_index_scan_cost("test", "person", "name", 0.1);
let stats_manager = Arc::new(StatisticsManager::new());
let mut tag_stats = TagStatistics::new("person".to_string());
tag_stats.vertex_count = 10_000;
tag_stats.allocated_slots = Some(20_000);
stats_manager.update_tag_stats("test", tag_stats);
let calculator = CostCalculator::new(stats_manager);
let with_holes = calculator.calculate_index_scan_cost("test", "person", "name", 0.1);
assert!(
with_holes > no_holes,
"with_holes={with_holes} no_holes={no_holes}"
);
assert!(
with_holes < 2.0 * no_holes,
"with_holes={with_holes} no_holes={no_holes}"
);
}
#[test]
fn tag_and_edge_selectivity_share_space_census() {
use crate::optimizer::stats::{EdgeTypeStatistics, TagStatistics};
let stats_manager = Arc::new(StatisticsManager::new());
let mut a = TagStatistics::new("a".to_string());
a.vertex_count = 750;
stats_manager.update_tag_stats("s", a);
let mut b = TagStatistics::new("b".to_string());
b.vertex_count = 250;
stats_manager.update_tag_stats("s", b);
let calculator = CostCalculator::new(stats_manager);
let sel = calculator.estimate_tag_selectivity("s", "a");
assert!((sel - 0.75).abs() < 1e-9, "selectivity={sel}");
assert_eq!(calculator.estimate_tag_selectivity("s", "missing"), 1.0);
let stats_manager = Arc::new(StatisticsManager::new());
let mut e = EdgeTypeStatistics::new("knows".to_string());
e.edge_count = 900;
stats_manager.update_edge_stats("s", e);
let mut f = EdgeTypeStatistics::new("likes".to_string());
f.edge_count = 100;
stats_manager.update_edge_stats("s", f);
let calculator = CostCalculator::new(stats_manager);
let sel = calculator.estimate_edge_selectivity("s", "knows");
assert!((sel - 0.9).abs() < 1e-9, "selectivity={sel}");
}
#[test]
fn test_calculate_filter_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let cost = calculator.calculate_filter_cost(1000, 3);
assert!(cost > 0.0);
assert_eq!(cost, 7.5);
}
#[test]
fn test_calculate_hash_join_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let cost = calculator.calculate_hash_join_cost(100, 200);
assert!(cost > 0.0);
assert_eq!(cost, 3.025);
}
#[test]
fn test_calculate_sort_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let cost = calculator.calculate_sort_cost(1000, 2, None);
assert!(cost > 0.0);
let zero_cost = calculator.calculate_sort_cost(0, 2, None);
assert_eq!(zero_cost, 0.0);
let topn_cost = calculator.calculate_sort_cost(1000, 2, Some(50));
assert!(topn_cost < cost);
let small_limit_cost = calculator.calculate_sort_cost(1000, 2, Some(200));
assert!(small_limit_cost >= cost * 0.99 && small_limit_cost <= cost * 1.01);
}
#[test]
fn test_calculate_topn_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let cost = calculator.calculate_topn_cost(10000, 10);
assert!(cost > 0.0);
assert!(cost > 80.0 && cost < 85.0);
}
#[test]
fn test_with_config() {
let stats_manager = Arc::new(StatisticsManager::new());
let config = CostModelConfig::for_ssd();
let calculator = CostCalculator::with_config(stats_manager, config);
assert_eq!(calculator.config().random_page_cost, 1.1);
}
#[test]
fn test_memory_aware_cost() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let base_cost = 100.0;
let normal_memory = 1024 * 1024; let normal_cost = calculator.calculate_memory_aware_cost(base_cost, normal_memory);
assert!(normal_cost > base_cost);
let high_memory = 200 * 1024 * 1024; let high_cost = calculator.calculate_memory_aware_cost(base_cost, high_memory);
assert!(high_cost > normal_cost); }
#[test]
fn test_estimate_aggregate_memory() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let mem_no_groups = calculator.estimate_aggregate_memory(1000, 0);
assert!(mem_no_groups > 0);
let mem_with_groups = calculator.estimate_aggregate_memory(10000, 3);
assert!(mem_with_groups > 0);
}
#[test]
fn test_estimate_sort_memory() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let memory = calculator.estimate_sort_memory(1000, 2);
assert_eq!(memory, 1000 * ESTIMATED_ROW_WIDTH_BYTES);
}
#[test]
fn test_estimate_hash_join_memory() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let memory = calculator.estimate_hash_join_memory(500);
assert_eq!(memory, 500 * ESTIMATED_ROW_WIDTH_BYTES);
}
#[test]
fn test_calculate_aggregate_cost_enhanced() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let (cost, memory) = calculator.calculate_aggregate_cost_enhanced(1000, 2, 1);
assert!(cost > 0.0);
assert!(memory > 0);
}
#[test]
fn test_calculate_sort_cost_enhanced() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let (cost, memory) = calculator.calculate_sort_cost_enhanced(1000, 2, None);
assert!(cost > 0.0);
assert_eq!(memory, 1000 * ESTIMATED_ROW_WIDTH_BYTES);
}
#[test]
fn test_calculate_hash_join_cost_enhanced() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
let (cost, memory) = calculator.calculate_hash_join_cost_enhanced(100, 200);
assert!(cost > 0.0);
assert_eq!(memory, 100 * ESTIMATED_ROW_WIDTH_BYTES);
}
#[test]
fn test_get_type_cost_factor() {
let stats_manager = Arc::new(StatisticsManager::new());
let calculator = CostCalculator::new(stats_manager);
assert_eq!(
calculator.get_type_cost_factor(&Value::Int(42)),
calculator.config.fixed_type_cost_factor
);
assert_eq!(
calculator.get_type_cost_factor(&Value::Bool(true)),
calculator.config.fixed_type_cost_factor
);
assert_eq!(
calculator.get_type_cost_factor(&Value::string("test")),
calculator.config.variable_type_cost_factor
);
assert_eq!(
calculator.get_type_cost_factor(&Value::List(Box::<linkrs_core::value::List>::default())),
calculator.config.complex_type_cost_factor
);
use linkrs_core::vertex_edge_path::Vertex;
let vertex = Vertex::new(
linkrs_core::types::VertexId::try_from_int64(1).expect("valid vertex id"),
linkrs_core::vertex_edge_path::Tag::new(String::new(), std::collections::HashMap::new()),
);
assert_eq!(
calculator.get_type_cost_factor(&Value::Vertex(Box::new(vertex))),
calculator.config.graph_type_cost_factor
);
}