formualizer-eval 0.14.0

High-performance Arrow-backed Excel formula engine with dependency graph and incremental recalculation
Documentation
use super::*;
use crate::engine::{CancelToken, EvalConfig};
use crate::test_workbook::TestWorkbook;
use formualizer_common::ExcelErrorKind;
use formualizer_parse::parser::parse;

fn setup_dynamic() -> (Engine<TestWorkbook>, VertexId) {
    crate::builtins::load_builtins();
    let mut engine = Engine::new(
        TestWorkbook::new(),
        EvalConfig {
            enable_parallel: false,
            arrow_storage_enabled: true,
            delta_overlay_enabled: true,
            write_formula_overlay_enabled: true,
            ..EvalConfig::default()
        },
    );
    engine
        .set_cell_value("Sheet1", 1, 2, LiteralValue::Number(10.0))
        .unwrap();
    engine
        .set_cell_value("Sheet1", 1, 3, LiteralValue::Number(20.0))
        .unwrap();
    engine
        .set_cell_formula("Sheet1", 1, 1, parse("=INDIRECT(\"B1\")").unwrap())
        .unwrap();
    engine.evaluate_all().unwrap();
    let cell = engine.graph.make_cell_ref("Sheet1", 1, 1);
    let vertex = engine.graph.get_vertex_id_for_address(&cell).unwrap();
    assert_eq!(
        engine.get_cell_value("Sheet1", 1, 1),
        Some(LiteralValue::Number(10.0))
    );
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_some()
    );
    (engine, vertex)
}

#[test]
fn observed_value_binding_is_not_published_by_effect_planning() {
    let (mut engine, vertex) = setup_dynamic();
    engine
        .set_cell_formula("Sheet1", 1, 1, parse("=INDIRECT(\"C1\")").unwrap())
        .unwrap();
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_none()
    );
    engine.freshness.armed = true;
    let cell = engine.graph.make_cell_ref("Sheet1", 1, 1);
    let result = engine
        .freshness_evaluate_recorded(
            vertex,
            "Sheet1",
            cell,
            engine.graph.formula_view(vertex).unwrap(),
        )
        .unwrap()
        .unwrap();
    assert_eq!(result, LiteralValue::Number(20.0));
    let effects = engine.plan_vertex_effects(vertex, result).unwrap();
    assert_ne!(
        engine.get_cell_value("Sheet1", 1, 1),
        Some(LiteralValue::Number(20.0))
    );
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_none(),
        "staged reads must not certify an unwritten result"
    );
    for effect in effects {
        engine.apply_effect(&effect, None, None).unwrap();
    }
    assert_eq!(
        engine.get_cell_value("Sheet1", 1, 1),
        Some(LiteralValue::Number(20.0))
    );
    let reads = engine
        .graph
        .authority_host()
        .committed_observed(vertex)
        .unwrap();
    assert!(reads.contains(&(0, 0, 2, 0, 2)), "{reads:?}");
}

#[test]
fn observed_value_binding_is_retired_by_unrecorded_result_writes() {
    let mut failures = Vec::new();
    for by_ref in [false, true] {
        let (mut engine, vertex) = setup_dynamic();
        assert!(
            !engine.graph.value_cache_enabled(),
            "canonical fast return must be tested"
        );
        let routing = engine
            .graph
            .authority_host()
            .observed(vertex)
            .unwrap()
            .to_vec();
        let revision = engine.graph.authority_host().rev_dyn();
        let proof_revision = engine.graph.authority_host().observed_value_revision();
        let value = LiteralValue::Number(999.0);
        if by_ref {
            engine.graph.update_vertex_value_ref(vertex, &value);
        } else {
            engine.graph.update_vertex_value(vertex, value);
        }
        assert_eq!(
            engine.graph.authority_host().observed(vertex),
            Some(routing.as_slice())
        );
        assert_eq!(engine.graph.authority_host().rev_dyn(), revision);
        assert!(engine.graph.authority_host().observed_value_revision() > proof_revision);
        if engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_some()
        {
            failures.push(format!(
                "by_ref={by_ref}: old reads cannot attest a generic replacement"
            ));
        }
    }
    assert!(failures.is_empty(), "{}", failures.join("\n"));
}

#[test]
fn observed_value_binding_failed_outer_request_cannot_retain_attestation() {
    let (mut engine, vertex) = setup_dynamic();
    let cancel = CancelToken::new();
    cancel.cancel();
    let error = engine.evaluate_all_cancellable(cancel).unwrap_err();
    assert_eq!(error.kind, ExcelErrorKind::Cancelled);
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_none(),
        "failed outer request must leave dynamic result trust unknown"
    );
    assert_eq!(
        engine.get_cell_value("Sheet1", 1, 1),
        Some(LiteralValue::Number(10.0))
    );
}

#[test]
fn observed_value_binding_named_result_writes_retire_non_grid_records() {
    use crate::engine::named_range::{NameScope, NamedDefinition};
    for by_ref in [false, true] {
        let (mut engine, _) = setup_dynamic();
        engine
            .define_name(
                "TrackedAlias",
                NamedDefinition::Formula {
                    ast: parse("=INDIRECT(\"C1\")").unwrap(),
                    dependencies: vec![],
                    range_deps: vec![],
                },
                NameScope::Workbook,
            )
            .unwrap();
        engine
            .set_cell_formula("Sheet1", 2, 1, parse("=TrackedAlias").unwrap())
            .unwrap();
        engine.evaluate_all().unwrap();
        let vertex = engine.graph.named_ranges_iter().next().unwrap().1.vertex;
        assert!(
            engine
                .graph
                .authority_host()
                .committed_observed(vertex)
                .is_some()
        );
        let value = LiteralValue::Number(999.0);
        if by_ref {
            engine.graph.update_vertex_value_ref(vertex, &value);
        } else {
            engine.graph.update_vertex_value(vertex, value);
        }
        assert!(
            engine
                .graph
                .authority_host()
                .committed_observed(vertex)
                .is_none(),
            "by_ref={by_ref}"
        );
    }
}

#[test]
fn observed_value_binding_spill_installs_reads_after_final_anchor_write() {
    let (mut engine, _) = setup_dynamic();
    engine
        .set_cell_value("Sheet1", 1, 4, LiteralValue::Number(10.0))
        .unwrap();
    engine
        .set_cell_value("Sheet1", 1, 5, LiteralValue::Number(20.0))
        .unwrap();
    engine
        .set_cell_formula("Sheet1", 4, 1, parse("=INDIRECT(\"C1\")").unwrap())
        .unwrap();
    engine.evaluate_all().unwrap();
    let cell = engine.graph.make_cell_ref("Sheet1", 4, 1);
    let vertex = engine.graph.get_vertex_id_for_address(&cell).unwrap();
    engine
        .set_cell_formula("Sheet1", 4, 1, parse("=INDIRECT(\"D1:E1\")").unwrap())
        .unwrap();
    engine.freshness.armed = true;
    let result = engine
        .freshness_evaluate_recorded(
            vertex,
            "Sheet1",
            cell,
            engine.graph.formula_view(vertex).unwrap(),
        )
        .unwrap()
        .unwrap();
    assert!(matches!(result, LiteralValue::Array(_)));
    let effects = engine.plan_vertex_effects(vertex, result).unwrap();
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_none()
    );
    for effect in effects {
        engine.apply_effect(&effect, None, None).unwrap();
    }
    assert_eq!(
        engine.get_cell_value("Sheet1", 4, 1),
        Some(LiteralValue::Number(10.0))
    );
    assert_eq!(
        engine.get_cell_value("Sheet1", 4, 2),
        Some(LiteralValue::Number(20.0))
    );
    let reads = engine
        .graph
        .authority_host()
        .committed_observed(vertex)
        .unwrap();
    assert!(reads.contains(&(0, 0, 3, 0, 4)), "{reads:?}");
}

#[test]
fn observed_value_binding_current_empty_reads_are_distinct_from_unknown() {
    let (mut engine, _) = setup_dynamic();
    engine
        .set_cell_formula(
            "Sheet1",
            4,
            1,
            parse("=IF(FALSE,INDIRECT(\"D1\"),7)").unwrap(),
        )
        .unwrap();
    engine.evaluate_all().unwrap();
    let vertex = engine
        .graph
        .get_vertex_id_for_address(&engine.graph.make_cell_ref("Sheet1", 4, 1))
        .unwrap();
    assert!(engine.graph.is_dynamic(vertex));
    assert_eq!(
        engine.graph.authority_host().committed_observed(vertex),
        Some([].as_slice())
    );
    assert_eq!(
        engine.get_cell_value("Sheet1", 4, 1),
        Some(LiteralValue::Number(7.0))
    );
}

struct CancelAfterRead {
    cancel: CancelToken,
}
impl crate::function::Function for CancelAfterRead {
    fn name(&self) -> &'static str {
        "CANCEL_AFTER_READ"
    }
    fn min_args(&self) -> usize {
        1
    }
    fn caps(&self) -> crate::function::FnCaps {
        crate::function::FnCaps::VOLATILE
    }
    fn arg_schema(&self) -> &'static [crate::args::ArgSchema] {
        static SCHEMA: std::sync::LazyLock<Vec<crate::args::ArgSchema>> =
            std::sync::LazyLock::new(|| vec![crate::args::ArgSchema::any()]);
        &SCHEMA
    }
    fn eval<'a, 'b, 'c>(
        &self,
        args: &'c [crate::traits::ArgumentHandle<'a, 'b>],
        _: &dyn crate::traits::FunctionContext<'b>,
    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
        let value = args[0].value()?;
        self.cancel.cancel();
        Ok(value)
    }
}
#[test]
fn observed_value_binding_failure_after_partial_writes_discards_attestation() {
    crate::builtins::load_builtins();
    let cancel = CancelToken::new();
    let workbook = TestWorkbook::new().with_function(std::sync::Arc::new(CancelAfterRead {
        cancel: cancel.clone(),
    }));
    let mut engine = Engine::new(
        workbook,
        EvalConfig {
            enable_parallel: false,
            arrow_storage_enabled: true,
            delta_overlay_enabled: true,
            write_formula_overlay_enabled: true,
            ..Default::default()
        },
    );
    engine
        .set_cell_value("Sheet1", 1, 2, LiteralValue::Number(10.0))
        .unwrap();
    engine
        .set_cell_formula("Sheet1", 1, 1, parse("=INDIRECT(\"B1\")").unwrap())
        .unwrap();
    engine.evaluate_all().unwrap();
    let vertex = engine
        .graph
        .get_vertex_id_for_address(&engine.graph.make_cell_ref("Sheet1", 1, 1))
        .unwrap();
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_some()
    );
    engine
        .set_cell_value("Sheet1", 1, 2, LiteralValue::Number(20.0))
        .unwrap();
    engine
        .set_cell_formula("Sheet1", 1, 3, parse("=CANCEL_AFTER_READ(A1)").unwrap())
        .unwrap();
    let error = engine.evaluate_all_cancellable(cancel).unwrap_err();
    assert_eq!(error.kind, ExcelErrorKind::Cancelled);
    assert_eq!(
        engine.get_cell_value("Sheet1", 1, 1),
        Some(LiteralValue::Number(20.0)),
        "the dynamic result must actually commit before this failure"
    );
    assert!(
        engine
            .graph
            .authority_host()
            .committed_observed(vertex)
            .is_none()
    );
    assert!(engine.graph.is_dirty(vertex));
}