use std::hint::black_box;
use std::time::{Duration, Instant};
use truecalc_core::Engine;
use truecalc_workbook::{
Address, CellInput, EngineFlavor, RecalcContext, Value, Workbook, Worksheet,
};
fn registry_build_cost() -> Duration {
const N: u32 = 40;
black_box(Engine::sheets());
let start = Instant::now();
for _ in 0..N {
black_box(Engine::sheets());
}
start.elapsed() / N
}
fn literal_grid() -> Workbook {
let mut wb = Workbook::new(EngineFlavor::Sheets);
wb.add_sheet(Worksheet::new("Sheet1")).unwrap();
for row in 1..=5u32 {
wb.set(
"Sheet1",
Address::new(row, 1).unwrap(),
CellInput::Literal(Value::Number(f64::from(row))),
)
.unwrap();
}
wb
}
#[test]
fn writing_formula_cells_does_not_build_a_registry_per_cell() {
const CELLS: u32 = 200;
let unit = registry_build_cost();
let mut warm = literal_grid();
warm.set(
"Sheet1",
Address::new(1, 2).unwrap(),
CellInput::Formula("=A1+1".to_string()),
)
.unwrap();
let mut wb = literal_grid();
let start = Instant::now();
for row in 1..=CELLS {
black_box(
wb.set(
"Sheet1",
Address::new(row, 2).unwrap(),
CellInput::Formula(format!("=A1+{row}")),
)
.unwrap(),
);
}
let elapsed = start.elapsed();
let units = elapsed.as_secs_f64() / unit.as_secs_f64();
let budget = f64::from(CELLS) / 3.0;
assert!(
units <= budget,
"writing {CELLS} formula cells cost {units:.1} registry builds \
({elapsed:?} at {unit:?} per build); budget is {budget:.1}. \
A registry is being built to validate each formula (issue #900)."
);
}
#[test]
fn resolving_named_ranges_does_not_build_a_registry_per_reference() {
const CELLS: u32 = 200;
let unit = registry_build_cost();
let ctx = RecalcContext::new(0, "UTC", 0).unwrap();
let mut template = literal_grid();
template.define_name("MYNAME", "Sheet1!A1").unwrap();
for row in 1..=CELLS {
template
.set(
"Sheet1",
Address::new(row, 2).unwrap(),
CellInput::Formula("=MYNAME+1".to_string()),
)
.unwrap();
}
let mut warm = template.clone();
warm.recalc(&ctx);
let mut wb = template.clone();
let start = Instant::now();
black_box(wb.recalc(&ctx));
let elapsed = start.elapsed();
assert_eq!(
wb.get("Sheet1", Address::new(1, 2).unwrap())
.unwrap()
.value(),
&Value::Number(2.0),
"named-range resolution changed value"
);
let units = elapsed.as_secs_f64() / unit.as_secs_f64();
let budget = f64::from(CELLS) / 3.0;
assert!(
units <= budget,
"recalc over {CELLS} named-range references cost {units:.1} registry \
builds ({elapsed:?} at {unit:?} per build); budget is {budget:.1}. \
A registry is being built per named-range reference (issue #900)."
);
}
#[test]
fn set_still_validates_formula_syntax() {
for flavor in [EngineFlavor::Sheets, EngineFlavor::Excel] {
let mut wb = Workbook::new(flavor);
wb.add_sheet(Worksheet::new("Sheet1")).unwrap();
let bad = wb.set(
"Sheet1",
Address::new(1, 1).unwrap(),
CellInput::Formula("=SUM(".to_string()),
);
assert!(bad.is_err(), "{flavor:?}: invalid formula was accepted");
assert!(
format!("{}", bad.unwrap_err()).contains("is invalid"),
"{flavor:?}: unexpected error message for an invalid formula"
);
wb.set(
"Sheet1",
Address::new(1, 1).unwrap(),
CellInput::Formula("=SUM(A1:A5)".to_string()),
)
.expect("valid formula must still be accepted");
}
}