use super::common::{create_binary_op_ast, create_cell_ref_ast};
use crate::builtins::random::RandFn;
use crate::engine::{Engine, EvalConfig};
use crate::test_workbook::TestWorkbook;
use formualizer_common::LiteralValue;
use formualizer_parse::parser::{ASTNode, ASTNodeType};
#[test]
fn test_evaluate_linear_chain() {
let mut engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
engine
.set_cell_value("Sheet1", 1, 1, LiteralValue::Int(10))
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
2,
create_binary_op_ast(
create_cell_ref_ast(None, 1, 1),
ASTNode {
node_type: ASTNodeType::Literal(LiteralValue::Int(2)),
source_token: None,
contains_volatile: false,
},
"*",
),
)
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
3,
create_binary_op_ast(
create_cell_ref_ast(None, 1, 2),
ASTNode {
node_type: ASTNodeType::Literal(LiteralValue::Int(5)),
source_token: None,
contains_volatile: false,
},
"+",
),
)
.unwrap();
let result = engine.evaluate_all().unwrap();
assert_eq!(result.computed_vertices, 2); assert_eq!(result.cycle_errors, 0);
assert_eq!(
engine.get_cell_value("Sheet1", 1, 3),
Some(LiteralValue::Number(25.0)) );
}
#[test]
fn test_evaluate_diamond_dependency() {
let mut engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
engine
.set_cell_value("Sheet1", 1, 1, LiteralValue::Int(10))
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
2,
create_binary_op_ast(
create_cell_ref_ast(None, 1, 1),
ASTNode {
node_type: ASTNodeType::Literal(LiteralValue::Int(2)),
source_token: None,
contains_volatile: false,
},
"*",
),
)
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
3,
create_binary_op_ast(
create_cell_ref_ast(None, 1, 1),
ASTNode {
node_type: ASTNodeType::Literal(LiteralValue::Int(3)),
source_token: None,
contains_volatile: false,
},
"+",
),
)
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
4,
create_binary_op_ast(
create_cell_ref_ast(None, 1, 2),
create_cell_ref_ast(None, 1, 3),
"+",
),
)
.unwrap();
let result = engine.evaluate_all().unwrap();
assert_eq!(result.computed_vertices, 3); assert_eq!(result.cycle_errors, 0);
assert_eq!(
engine.get_cell_value("Sheet1", 1, 4),
Some(LiteralValue::Number(33.0))
);
}
#[test]
fn test_evaluation_with_cycles() {
let mut engine = Engine::new(TestWorkbook::new(), EvalConfig::default());
engine
.set_cell_formula("Sheet1", 1, 1, create_cell_ref_ast(None, 1, 2))
.unwrap();
engine
.set_cell_formula("Sheet1", 1, 2, create_cell_ref_ast(None, 1, 1))
.unwrap();
engine
.set_cell_formula(
"Sheet1",
1,
3,
ASTNode {
node_type: ASTNodeType::Literal(LiteralValue::Int(5)),
source_token: None,
contains_volatile: false,
},
)
.unwrap();
let result = engine.evaluate_all().unwrap();
assert_eq!(result.computed_vertices, 1); assert_eq!(result.cycle_errors, 1); for (r, c) in [(1u32, 1u32), (1u32, 2u32)] {
match engine.get_cell_value("Sheet1", r, c) {
Some(LiteralValue::Error(e)) => {
assert_eq!(e.kind, formualizer_common::ExcelErrorKind::Circ);
}
v => panic!("expected #CIRC! at R{r}C{c}, got {v:?}"),
}
}
assert_eq!(
engine.get_cell_value("Sheet1", 1, 3),
Some(LiteralValue::Number(5.0))
);
}
#[test]
fn test_volatile_cells_are_always_evaluated() {
crate::builtins::random::register_builtins();
let wb = TestWorkbook::new().with_function(std::sync::Arc::new(RandFn));
let mut engine = Engine::new(wb, EvalConfig::default());
engine
.set_cell_formula(
"Sheet1",
1,
1,
ASTNode {
node_type: ASTNodeType::Function {
name: "RAND".to_string(),
args: vec![],
},
source_token: None,
contains_volatile: true,
},
)
.unwrap();
engine
.set_cell_formula("Sheet1", 1, 2, create_cell_ref_ast(None, 1, 1))
.unwrap();
engine.evaluate_all().unwrap();
let first_val = engine.get_cell_value("Sheet1", 1, 2);
assert!(first_val.is_some());
engine.set_workbook_seed(0xDEAD_BEEF_F00D_CAFE);
engine.evaluate_all().unwrap();
let second_val = engine.get_cell_value("Sheet1", 1, 2);
assert!(second_val.is_some());
assert_ne!(first_val, second_val, "Volatile cell did not re-evaluate");
}