use proptest::prelude::*;
use truecalc_core::{evaluate, Value};
use std::collections::HashMap;
const CASES: u32 = 500;
fn make_single_row_db(val: f64) -> Value {
Value::Array(vec![
Value::Array(vec![Value::Text("x".to_string())]),
Value::Array(vec![Value::Number(val)]),
])
}
fn make_exact_criteria(val: f64) -> Value {
Value::Array(vec![
Value::Array(vec![Value::Text("x".to_string())]),
Value::Array(vec![Value::Number(val)]),
])
}
fn run_db(formula: &str, db: Value, crit: Value) -> Value {
let mut vars = HashMap::new();
vars.insert("db".to_string(), db);
vars.insert("crit".to_string(), crit);
evaluate(formula, &vars)
}
fn positive_f64() -> impl Strategy<Value = f64> {
1.0f64..=10000.0f64
}
#[test]
fn dsum_single_row_exact_match() {
proptest!(ProptestConfig::with_cases(CASES), |(val in positive_f64())| {
let result = run_db("=DSUM(db, 1, crit)",
make_single_row_db(val), make_exact_criteria(val));
prop_assert_eq!(result, Value::Number(val));
});
eprintln!("proptest: {CASES} cases (val ∈ [1, 10000])");
}
#[test]
fn daverage_single_row_exact_match() {
proptest!(ProptestConfig::with_cases(CASES), |(val in positive_f64())| {
let result = run_db("=DAVERAGE(db, 1, crit)",
make_single_row_db(val), make_exact_criteria(val));
prop_assert_eq!(result, Value::Number(val));
});
eprintln!("proptest: {CASES} cases (val ∈ [1, 10000])");
}
#[test]
fn dmax_single_row_exact_match() {
proptest!(ProptestConfig::with_cases(CASES), |(val in positive_f64())| {
let result = run_db("=DMAX(db, 1, crit)",
make_single_row_db(val), make_exact_criteria(val));
prop_assert_eq!(result, Value::Number(val));
});
eprintln!("proptest: {CASES} cases (val ∈ [1, 10000])");
}
#[test]
fn dmin_single_row_exact_match() {
proptest!(ProptestConfig::with_cases(CASES), |(val in positive_f64())| {
let result = run_db("=DMIN(db, 1, crit)",
make_single_row_db(val), make_exact_criteria(val));
prop_assert_eq!(result, Value::Number(val));
});
eprintln!("proptest: {CASES} cases (val ∈ [1, 10000])");
}
#[test]
fn dcount_single_row_returns_one() {
proptest!(ProptestConfig::with_cases(CASES), |(val in positive_f64())| {
let result = run_db("=DCOUNT(db, 1, crit)",
make_single_row_db(val), make_exact_criteria(val));
prop_assert_eq!(result, Value::Number(1.0));
});
eprintln!("proptest: {CASES} cases (val ∈ [1, 10000])");
}
#[test]
fn dmax_gte_dmin_two_rows() {
proptest!(ProptestConfig::with_cases(CASES), |(a in 1.0f64..=5000.0f64, b in 1.0f64..=5000.0f64)| {
let db = Value::Array(vec![
Value::Array(vec![Value::Text("x".to_string())]),
Value::Array(vec![Value::Number(a)]),
Value::Array(vec![Value::Number(b)]),
]);
let crit = Value::Array(vec![
Value::Array(vec![Value::Text("x".to_string())]),
Value::Array(vec![Value::Text(">0".to_string())]),
]);
let dmax = {
let mut vars = HashMap::new();
vars.insert("db".to_string(), db.clone());
vars.insert("crit".to_string(), crit.clone());
evaluate("=DMAX(db, 1, crit)", &vars)
};
let dmin = {
let mut vars = HashMap::new();
vars.insert("db".to_string(), db);
vars.insert("crit".to_string(), crit);
evaluate("=DMIN(db, 1, crit)", &vars)
};
if let (Value::Number(mx), Value::Number(mn)) = (dmax, dmin) {
prop_assert!(mx >= mn - 1e-12, "DMAX={} < DMIN={}", mx, mn);
}
});
eprintln!("proptest: {CASES} cases (a,b ∈ [1, 5000])");
}
#[test]
fn sanity_dsum() {
let db = Value::Array(vec![
Value::Array(vec![Value::Text("val".to_string())]),
Value::Array(vec![Value::Number(10.0)]),
Value::Array(vec![Value::Number(20.0)]),
]);
let crit = Value::Array(vec![
Value::Array(vec![Value::Text("val".to_string())]),
Value::Array(vec![Value::Text(">0".to_string())]),
]);
let mut vars = HashMap::new();
vars.insert("db".to_string(), db);
vars.insert("crit".to_string(), crit);
assert_eq!(evaluate("=DSUM(db, 1, crit)", &vars), Value::Number(30.0));
}