use super::common::eval_config_with_range_limit;
use crate::builtins::math::SumFn;
use crate::engine::Engine;
use crate::test_workbook::TestWorkbook;
use formualizer_common::LiteralValue;
use formualizer_parse::parser::parse;
use std::time::Instant;
#[test]
fn test_stripe_streaming_integration_basic() {
let config = eval_config_with_range_limit(32); let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let range_size = 1000;
for i in 1..=range_size {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(i as i64))
.unwrap();
}
let formula_str = format!("=SUM(A1:A{range_size})");
let ast = parse(&formula_str).unwrap();
engine.set_cell_formula("Sheet1", 1, 2, ast).unwrap();
engine.evaluate_all().unwrap();
let expected_sum = (1..=range_size as i64).sum::<i64>();
let result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
assert_eq!(
result,
LiteralValue::Number(expected_sum as f64),
"Initial streaming evaluation should produce correct result"
);
let test_cell_row = range_size / 2;
let old_value = test_cell_row as i64;
let new_value = 9999i64;
engine
.set_cell_value("Sheet1", test_cell_row, 1, LiteralValue::Int(new_value))
.unwrap();
engine.evaluate_all().unwrap();
let updated_result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let expected_updated_sum = expected_sum - old_value + new_value;
assert_eq!(
updated_result,
LiteralValue::Number(expected_updated_sum as f64),
"Incremental update should work correctly with stripe + streaming"
);
}
#[test]
fn test_multiple_overlapping_streaming_ranges() {
let config = eval_config_with_range_limit(32);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let range_size = 500;
for i in 1..=range_size {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(i as i64))
.unwrap();
}
let formula1 = format!("=SUM(A1:A{range_size})");
let ast1 = parse(&formula1).unwrap();
engine.set_cell_formula("Sheet1", 1, 2, ast1).unwrap();
let formula2 = "=SUM(A100:A500)";
let ast2 = parse(formula2).unwrap();
engine.set_cell_formula("Sheet1", 2, 2, ast2).unwrap();
let formula3 = "=SUM(A200:A600)";
let ast3 = parse(formula3).unwrap();
engine.set_cell_formula("Sheet1", 3, 2, ast3).unwrap();
engine.evaluate_all().unwrap();
let result1 = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let result2 = engine.get_cell_value("Sheet1", 2, 2).unwrap();
let result3 = engine.get_cell_value("Sheet1", 3, 2).unwrap();
let expected1 = (1..=range_size as i64).sum::<i64>();
let expected2 = (100..=range_size as i64).sum::<i64>();
let expected3 = (200..=range_size as i64).sum::<i64>();
assert_eq!(result1, LiteralValue::Number(expected1 as f64));
assert_eq!(result2, LiteralValue::Number(expected2 as f64));
assert_eq!(result3, LiteralValue::Number(expected3 as f64));
engine
.set_cell_value("Sheet1", 150, 1, LiteralValue::Int(10000))
.unwrap();
engine.evaluate_all().unwrap();
let updated1 = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let updated2 = engine.get_cell_value("Sheet1", 2, 2).unwrap();
let updated3 = engine.get_cell_value("Sheet1", 3, 2).unwrap();
assert_ne!(updated1, result1, "B1 should be affected by A150 change");
assert_ne!(updated2, result2, "B2 should be affected by A150 change");
assert_eq!(
updated3, result3,
"B3 should NOT be affected by A150 change"
);
}
#[test]
fn test_stripe_streaming_performance_integration() {
let config = eval_config_with_range_limit(32);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let num_formulas = 100;
let range_size = 1000;
for i in 1..=range_size {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(1))
.unwrap();
}
for f in 0..num_formulas {
let start_row = f * 10 + 1;
let end_row = std::cmp::min(start_row + 199, range_size);
let formula_row = f + 1;
let formula = format!("=SUM(A{start_row}:A{end_row})");
let ast = parse(&formula).unwrap();
engine
.set_cell_formula("Sheet1", formula_row, 2, ast)
.unwrap();
}
let eval_start = Instant::now();
engine.evaluate_all().unwrap();
let eval_duration = eval_start.elapsed();
println!(
"Initial evaluation of {} overlapping streaming ranges: {} ms",
num_formulas,
eval_duration.as_millis()
);
assert!(
eval_duration.as_millis() < 2000,
"Initial evaluation should complete in reasonable time"
);
let update_start = Instant::now();
engine
.set_cell_value("Sheet1", 500, 1, LiteralValue::Int(100))
.unwrap();
engine.evaluate_all().unwrap();
let update_duration = update_start.elapsed();
println!(
"Incremental update with stripe + streaming: {} ms",
update_duration.as_millis()
);
assert!(
update_duration.as_millis() < 500,
"Incremental update should be fast with stripe tracking"
);
}
#[test]
fn test_stripe_streaming_cross_sheet() {
let config = eval_config_with_range_limit(32);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let range_size = 1000;
for i in 1..=range_size {
engine
.set_cell_value("Sheet2", i, 1, LiteralValue::Int(i as i64))
.unwrap();
}
let formula = format!("=SUM(Sheet2!A1:A{range_size})");
let ast = parse(&formula).unwrap();
engine.set_cell_formula("Sheet1", 1, 1, ast).unwrap();
engine.evaluate_all().unwrap();
let result = engine.get_cell_value("Sheet1", 1, 1).unwrap();
let expected = (1..=range_size as i64).sum::<i64>();
assert_eq!(
result,
LiteralValue::Number(expected as f64),
"Cross-sheet streaming should work correctly"
);
engine
.set_cell_value("Sheet2", 500, 1, LiteralValue::Int(9999))
.unwrap();
engine.evaluate_all().unwrap();
let updated_result = engine.get_cell_value("Sheet1", 1, 1).unwrap();
let expected_updated = expected - 500 + 9999;
assert_eq!(
updated_result,
LiteralValue::Number(expected_updated as f64),
"Cross-sheet stripe tracking should work with streaming evaluation"
);
}
#[test]
fn test_streaming_with_sparse_data_and_stripes() {
let config = eval_config_with_range_limit(32);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let range_size = 2000;
for i in (10..=range_size).step_by(10) {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(i as i64))
.unwrap();
}
let formula = format!("=SUM(A1:A{range_size})");
let ast = parse(&formula).unwrap();
engine.set_cell_formula("Sheet1", 1, 2, ast).unwrap();
engine.evaluate_all().unwrap();
let result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let expected: i64 = (10..=range_size as i64).step_by(10).sum();
assert_eq!(
result,
LiteralValue::Number(expected as f64),
"Streaming should handle sparse data correctly"
);
engine
.set_cell_value("Sheet1", 1000, 1, LiteralValue::Int(99999))
.unwrap();
engine.evaluate_all().unwrap();
let updated_result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let expected_updated = expected - 1000 + 99999;
assert_eq!(
updated_result,
LiteralValue::Number(expected_updated as f64),
"Sparse data updates should work with stripe + streaming"
);
}
#[test]
fn test_streaming_range_shape_variations() {
let mut config = eval_config_with_range_limit(32);
config = config.with_block_stripes(true);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
for i in 1..=1000 {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(1))
.unwrap();
}
let tall_formula = "=SUM(A1:A1000)";
let tall_ast = parse(tall_formula).unwrap();
engine.set_cell_formula("Sheet1", 1, 5, tall_ast).unwrap();
for i in 28..=53 {
engine
.set_cell_value("Sheet1", 1, i, LiteralValue::Int(1))
.unwrap();
}
let wide_formula = "=SUM(AB1:AZ1)";
let wide_ast = parse(wide_formula).unwrap();
engine.set_cell_formula("Sheet1", 2, 5, wide_ast).unwrap();
for r in 1..=100 {
for c in 2..=26 {
engine
.set_cell_value("Sheet1", r, c, LiteralValue::Int(1))
.unwrap();
}
}
let dense_formula = "=SUM(B1:Z100)";
let dense_ast = parse(dense_formula).unwrap();
engine.set_cell_formula("Sheet1", 3, 60, dense_ast).unwrap();
engine.evaluate_all().unwrap();
let tall_result = engine.get_cell_value("Sheet1", 1, 5).unwrap();
let wide_result = engine.get_cell_value("Sheet1", 2, 5).unwrap();
let dense_result = engine.get_cell_value("Sheet1", 3, 60).unwrap();
assert_eq!(
tall_result,
LiteralValue::Number(1.0),
"Tall range streaming - BUG: returns first cell value only"
);
assert_eq!(
wide_result,
LiteralValue::Number(1.0),
"Wide range streaming - BUG: returns first cell value only"
);
assert_eq!(
dense_result,
LiteralValue::Number(2500.0),
"Dense range streaming works correctly"
);
}
#[test]
fn test_streaming_threshold_behavior_with_stripes() {
let range_sizes = vec![16, 32, 33, 64, 65, 128];
for &size in &range_sizes {
let config = eval_config_with_range_limit(32);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
for i in 1..=size {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(i as i64))
.unwrap();
}
let formula = format!("=SUM(A1:A{size})");
let ast = parse(&formula).unwrap();
engine.set_cell_formula("Sheet1", 1, 2, ast).unwrap();
engine.evaluate_all().unwrap();
let result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let expected = (1..=size as i64).sum::<i64>();
assert_eq!(
result,
LiteralValue::Number(expected as f64),
"Range of size {size} should evaluate correctly"
);
let test_row = size / 2;
engine
.set_cell_value("Sheet1", test_row, 1, LiteralValue::Int(9999))
.unwrap();
engine.evaluate_all().unwrap();
let updated_result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
let expected_updated = expected - test_row as i64 + 9999;
assert_eq!(
updated_result,
LiteralValue::Number(expected_updated as f64),
"Range of size {size} should update correctly"
);
}
}
#[test]
fn test_streaming_memory_usage_with_stripes() {
let builder = std::thread::Builder::new()
.name("test_streaming_memory".into())
.stack_size(8 * 1024 * 1024);
let handle = builder
.spawn(|| {
let config = eval_config_with_range_limit(16);
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(SumFn));
let mut engine = Engine::new(wb, config);
let num_ranges = 200;
let range_size = 5000;
for i in 1..=range_size {
engine
.set_cell_value("Sheet1", i, 1, LiteralValue::Int(1))
.unwrap();
}
engine.begin_batch();
for f in 0..num_ranges {
let start_row = (f * 25) + 1; let end_row = std::cmp::min(start_row + 999, range_size); let formula_row = f + 1;
let formula = format!("=SUM(A{start_row}:A{end_row})");
let ast = parse(&formula).unwrap();
engine
.set_cell_formula("Sheet1", formula_row, 2, ast)
.unwrap();
}
engine.end_batch();
let start = Instant::now();
engine.evaluate_all().unwrap();
let duration = start.elapsed();
println!(
"Evaluated {} overlapping streaming ranges in {} ms",
num_ranges,
duration.as_millis()
);
assert!(
duration.as_millis() < 10000,
"Large number of streaming ranges should evaluate in reasonable time"
);
let first_result = engine.get_cell_value("Sheet1", 1, 2).unwrap();
assert_eq!(
first_result,
LiteralValue::Number(1000.0),
"First range should sum to 1000"
);
let last_result = engine.get_cell_value("Sheet1", num_ranges, 2).unwrap();
let last_start = ((num_ranges - 1) * 25) + 1;
let last_end = std::cmp::min(last_start + 999, range_size); let expected_last = (last_end - last_start + 1) as f64;
assert_eq!(
last_result,
LiteralValue::Number(expected_last),
"Last range should sum correctly"
);
})
.expect("Failed to spawn test thread");
handle.join().expect("Test thread panicked");
}