a020ac669f
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
102 lines
3.6 KiB
Rust
102 lines
3.6 KiB
Rust
//! `Incremental` against `Eager` over random edit sequences.
|
|
//! Evidence: `context/test-evidence/improvise-incremental.md`.
|
|
|
|
mod support;
|
|
|
|
use improvise_core::formula::ast::AggFunc;
|
|
use improvise_core::model::Model;
|
|
use improvise_core::model::cell::{CellKey, CellValue};
|
|
use improvise_core::view::View;
|
|
use improvise_incremental::{Edit, Incremental, differential_in};
|
|
use proptest::prelude::*;
|
|
use support::{
|
|
grouped_model, hand_model, hidden_counted_workbook, hidden_workbook, none_of, ratio_workbook,
|
|
};
|
|
|
|
fn pool() -> impl Strategy<Value = CellValue> {
|
|
prop_oneof![
|
|
(-3i32..4).prop_map(|n| CellValue::Number(f64::from(n))),
|
|
Just(CellValue::Number(-0.0)),
|
|
Just(CellValue::Number(f64::NAN)),
|
|
Just(CellValue::Number(-f64::NAN)),
|
|
Just(CellValue::Number(f64::INFINITY)),
|
|
prop::sample::select(vec![0.1, 1e16, -1e16, f64::MAX, 5e-324]).prop_map(CellValue::Number),
|
|
prop::sample::select(vec!["n", "x"]).prop_map(|t| CellValue::Text(t.into())),
|
|
Just(CellValue::Error("e".into())),
|
|
]
|
|
}
|
|
|
|
/// Edits built from a document's own cells: overwrite, a new record in a made-up item,
|
|
/// clear, and a new aggregation for one of its measures.
|
|
fn edits_over(model: &Model) -> BoxedStrategy<Edit> {
|
|
let keys: Vec<CellKey> = model.data.iter_cells().map(|(k, _)| k).take(24).collect();
|
|
let measures: Vec<String> = model.measure_item_names();
|
|
let funcs = vec![
|
|
AggFunc::Sum,
|
|
AggFunc::Avg,
|
|
AggFunc::Min,
|
|
AggFunc::Max,
|
|
AggFunc::Count,
|
|
];
|
|
let existing = prop::sample::select(keys.clone());
|
|
let fresh = (prop::sample::select(keys), 0usize..3).prop_map(|(k, which)| {
|
|
let (cat, _) = k.0[which % k.0.len()].clone();
|
|
k.with(cat, "Zz-new")
|
|
});
|
|
prop_oneof![
|
|
(existing.clone(), pool()).prop_map(|(k, v)| Edit::SetCell(k, v)),
|
|
(fresh, pool()).prop_map(|(k, v)| Edit::SetCell(k, v)),
|
|
existing.prop_map(Edit::ClearCell),
|
|
(prop::sample::select(measures), prop::sample::select(funcs))
|
|
.prop_map(|(m, func)| Edit::SetMeasureAgg { measure: m, func }),
|
|
]
|
|
.boxed()
|
|
}
|
|
|
|
fn small_documents() -> Vec<(String, Model, Vec<String>, Vec<View>)> {
|
|
let hidden = hidden_workbook();
|
|
let ratio = ratio_workbook();
|
|
let counted = hidden_counted_workbook();
|
|
let views = |wb: &improvise_core::workbook::Workbook| wb.views.values().cloned().collect();
|
|
vec![
|
|
("hand".into(), hand_model(), vec!["Region".into()], vec![]),
|
|
("grouped".into(), grouped_model(), vec![], vec![]),
|
|
(
|
|
"hidden".into(),
|
|
hidden.model.clone(),
|
|
none_of(&hidden),
|
|
views(&hidden),
|
|
),
|
|
(
|
|
"ratio".into(),
|
|
ratio.model.clone(),
|
|
none_of(&ratio),
|
|
views(&ratio),
|
|
),
|
|
(
|
|
"counted".into(),
|
|
counted.model.clone(),
|
|
none_of(&counted),
|
|
views(&counted),
|
|
),
|
|
]
|
|
}
|
|
|
|
fn any_document_edits() -> impl Strategy<Value = (usize, Vec<Edit>)> {
|
|
let docs = small_documents();
|
|
(0..docs.len()).prop_flat_map(move |i| {
|
|
let edits = prop::collection::vec(edits_over(&docs[i].1), 0..5);
|
|
(Just(i), edits)
|
|
})
|
|
}
|
|
|
|
proptest! {
|
|
#[test]
|
|
fn incremental_equals_eager_over_random_edit_sequences((i, edits) in any_document_edits()) {
|
|
let (name, model, none, views) = small_documents().swap_remove(i);
|
|
let candidate = Incremental::new(model.clone(), none.clone());
|
|
let ran = differential_in(&model, &views, &none, candidate, &edits);
|
|
prop_assert!(ran.is_ok(), "{name}: {}", ran.unwrap_err());
|
|
}
|
|
}
|