use super::common::get_vertex_ids_in_order;
use crate::engine::{DependencyGraph, Scheduler};
use formualizer_common::{ExcelErrorKind, LiteralValue};
use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
fn ref_ast(row: u32, col: u32) -> ASTNode {
ASTNode {
node_type: ASTNodeType::Reference {
original: format!("R{row}C{col}"),
reference: ReferenceType::cell(None, row, col),
},
source_token: None,
contains_volatile: false,
}
}
fn op_ast(left: ASTNode, right: ASTNode) -> ASTNode {
ASTNode {
node_type: ASTNodeType::BinaryOp {
op: "+".to_string(),
left: Box::new(left),
right: Box::new(right),
},
source_token: None,
contains_volatile: false,
}
}
#[test]
fn test_kahn_topological_layers() {
let mut graph = DependencyGraph::new();
graph
.set_cell_value("Sheet1", 1, 1, LiteralValue::Int(10))
.unwrap(); graph
.set_cell_value("Sheet1", 2, 1, LiteralValue::Int(20))
.unwrap();
graph
.set_cell_formula("Sheet1", 3, 1, op_ast(ref_ast(1, 1), ref_ast(2, 1)))
.unwrap(); graph
.set_cell_formula("Sheet1", 4, 1, ref_ast(2, 1))
.unwrap();
graph
.set_cell_formula("Sheet1", 5, 1, op_ast(ref_ast(3, 1), ref_ast(4, 1)))
.unwrap();
let scheduler = Scheduler::new(&graph);
let all_vertex_ids = get_vertex_ids_in_order(&graph);
let sccs = scheduler.tarjan_scc(&all_vertex_ids).unwrap();
let (_, acyclic_sccs) = scheduler.separate_cycles(sccs);
let layers = scheduler.build_layers(acyclic_sccs).unwrap();
assert_eq!(layers.len(), 3, "Should be 3 topological layers");
assert_eq!(layers[0].vertices.len(), 2);
assert!(layers[0].vertices.contains(&all_vertex_ids[0])); assert!(layers[0].vertices.contains(&all_vertex_ids[1]));
assert_eq!(layers[1].vertices.len(), 2);
assert!(layers[1].vertices.contains(&all_vertex_ids[2])); assert!(layers[1].vertices.contains(&all_vertex_ids[3]));
assert_eq!(layers[2].vertices.len(), 1);
assert!(layers[2].vertices.contains(&all_vertex_ids[4])); }
#[test]
fn test_layer_parallelism_safety_setup() {
let mut graph = DependencyGraph::new();
graph
.set_cell_value("Sheet1", 1, 1, LiteralValue::Int(10))
.unwrap();
graph
.set_cell_value("Sheet1", 2, 1, LiteralValue::Int(20))
.unwrap();
graph
.set_cell_formula("Sheet1", 3, 1, op_ast(ref_ast(1, 1), ref_ast(2, 1)))
.unwrap();
graph
.set_cell_formula("Sheet1", 4, 1, ref_ast(2, 1))
.unwrap();
let scheduler = Scheduler::new(&graph);
let all_vertex_ids = get_vertex_ids_in_order(&graph);
let sccs = scheduler.tarjan_scc(&all_vertex_ids).unwrap();
let (_, acyclic_sccs) = scheduler.separate_cycles(sccs);
let layers = scheduler.build_layers(acyclic_sccs).unwrap();
assert_eq!(layers.len(), 2);
assert_eq!(
layers[0].vertices.len(),
2,
"Layer 0 should have 2 vertices, suitable for parallel execution"
);
assert_eq!(
layers[1].vertices.len(),
2,
"Layer 1 should have 2 vertices, suitable for parallel execution"
);
}
#[test]
fn test_empty_layer_handling() {
let graph = DependencyGraph::new();
let scheduler = Scheduler::new(&graph);
let layers = scheduler.build_layers(vec![]).unwrap();
assert!(
layers.is_empty(),
"Building layers from no components should result in no layers"
);
}
#[test]
fn test_build_layers_with_cycle_errors() {
let mut graph = DependencyGraph::new();
graph
.set_cell_formula("Sheet1", 1, 1, ref_ast(2, 1))
.unwrap(); graph
.set_cell_formula("Sheet1", 2, 1, ref_ast(1, 1))
.unwrap();
let scheduler = Scheduler::new(&graph);
let all_vertex_ids = get_vertex_ids_in_order(&graph);
let cyclic_scc = vec![vec![all_vertex_ids[0], all_vertex_ids[1]]];
let result = scheduler.build_layers(cyclic_scc);
assert!(result.is_err());
if let Err(e) = result {
assert_eq!(e.kind, ExcelErrorKind::Circ);
}
}