use super::super::*;
use crate::eval::{Context, EvalCtx, Registry};
use crate::parser::ast::{Expr, Span};
use crate::types::Value;
use std::collections::HashMap;
fn run(formula: &str, vars: HashMap<String, Value>) -> Value {
crate::Engine::sheets().evaluate(formula, &vars)
}
fn span() -> Span { Span::new(0, 1) }
fn run_counta_lazy(args: Vec<Expr>) -> Value {
let reg = Registry::new();
let mut ctx = EvalCtx::new(Context::empty(), ®);
counta_lazy_fn(&args, &mut ctx)
}
#[test]
fn counta_lazy_array_arg_counts_elements() {
let args = vec![Expr::Array(
vec![
Expr::Number(1.0, span()),
Expr::Number(2.0, span()),
Expr::Number(3.0, span()),
],
span(),
)];
assert_eq!(run_counta_lazy(args), Value::Number(3.0));
}
#[test]
fn counta_lazy_array_counts_text_skips_empty() {
let args = vec![Expr::Array(
vec![
Expr::Text("a".to_string(), span()),
Expr::Text("b".to_string(), span()),
],
span(),
)];
assert_eq!(run_counta_lazy(args), Value::Number(2.0));
}
#[test]
fn count_no_args_returns_zero() {
assert_eq!(count_fn(&[]), Value::Number(0.0));
}
#[test]
fn counta_no_args_returns_zero() {
assert_eq!(counta_fn(&[]), Value::Number(0.0));
}
#[test]
fn count_mixed_ignores_non_numeric() {
assert_eq!(
count_fn(&[
Value::Number(1.0),
Value::Text("text".to_string()),
Value::Bool(true),
Value::Number(3.0)
]),
Value::Number(2.0)
);
}
#[test]
fn counta_mixed_counts_all_non_empty() {
assert_eq!(
counta_fn(&[
Value::Number(1.0),
Value::Text("text".to_string()),
Value::Bool(true),
Value::Number(3.0)
]),
Value::Number(4.0)
);
}
#[test]
fn counta_empty_values_not_counted() {
assert_eq!(
counta_fn(&[Value::Empty, Value::Number(1.0), Value::Empty]),
Value::Number(1.0)
);
}
#[test]
fn count_array_variable_counts_numbers() {
let vars: HashMap<_, _> = [(
"A".to_string(),
Value::Array(vec![
Value::Number(1.0),
Value::Number(2.0),
Value::Number(3.0),
]),
)]
.into();
assert_eq!(run("=COUNT(A)", vars), Value::Number(3.0));
}
#[test]
fn count_array_variable_skips_non_numeric() {
let vars: HashMap<_, _> = [(
"A".to_string(),
Value::Array(vec![
Value::Number(1.0),
Value::Text("hello".to_string()),
Value::Bool(true),
Value::Number(2.0),
Value::Empty,
]),
)]
.into();
assert_eq!(run("=COUNT(A)", vars), Value::Number(2.0));
}
#[test]
fn count_range_variable_skips_bool_and_text() {
let vars: HashMap<_, _> = [(
"R".to_string(),
Value::Array(vec![
Value::Number(10.0),
Value::Number(5.0),
Value::Text("hello".to_string()),
Value::Bool(true),
Value::Number(20.0),
Value::Number(20.0),
]),
)]
.into();
assert_eq!(run("=COUNT(R)", vars), Value::Number(4.0));
}
#[test]
fn count_counts_dates() {
assert_eq!(run("=COUNT(DATE(2020,1,1))", HashMap::new()), Value::Number(1.0));
assert_eq!(
run("=COUNT(DATE(2020,1,1),DATE(2021,1,1))", HashMap::new()),
Value::Number(2.0)
);
assert_eq!(
run("=COUNT({DATE(2020,1,1),DATE(2021,1,1)})", HashMap::new()),
Value::Number(2.0)
);
assert_eq!(
run("=COUNT({DATE(2020,1,1),5})", HashMap::new()),
Value::Number(2.0)
);
assert_eq!(count_fn(&[Value::Date(43831.0)]), Value::Number(1.0));
assert_eq!(
count_fn(&[Value::Date(43831.0), Value::Number(5.0)]),
Value::Number(2.0)
);
}
#[test]
fn count_counts_dates_arriving_through_a_range() {
let vars: HashMap<_, _> = [(
"R".to_string(),
Value::Array(vec![
Value::Array(vec![Value::Date(43831.0)]),
Value::Array(vec![Value::Date(44197.0)]),
Value::Array(vec![Value::Number(5.0)]),
]),
)]
.into();
assert_eq!(run("=COUNT(R)", vars.clone()), Value::Number(3.0));
assert_eq!(run("=COUNT(MAX(R))", vars.clone()), Value::Number(1.0));
assert_eq!(run("=COUNT(MIN(R))", vars.clone()), Value::Number(1.0));
assert_eq!(run("=COUNT(MAXA(R))", vars.clone()), Value::Number(1.0));
assert_eq!(run("=COUNT(MINA(R))", vars), Value::Number(1.0));
assert_eq!(
run("=COUNT(MAX({DATE(2020,1,1),5}))", HashMap::new()),
Value::Number(1.0)
);
}
#[test]
fn counting_a_date_does_not_move_bools_or_text() {
assert_eq!(
run("=COUNT(DATE(2020,1,1),TRUE)", HashMap::new()),
Value::Number(2.0)
);
assert_eq!(
run("=COUNT({DATE(2020,1,1),TRUE})", HashMap::new()),
Value::Number(1.0)
);
assert_eq!(
run("=COUNT({DATE(2020,1,1),\"a\"})", HashMap::new()),
Value::Number(1.0)
);
}
#[test]
fn counta_array_variable_counts_non_empty() {
let vars: HashMap<_, _> = [(
"A".to_string(),
Value::Array(vec![
Value::Number(1.0),
Value::Text("hello".to_string()),
Value::Empty,
Value::Bool(false),
]),
)]
.into();
assert_eq!(run("=COUNTA(A)", vars), Value::Number(3.0));
}
#[test]
fn direct_date_and_time_text_is_counted() {
for formula in [
"=COUNT(\"2020-01-01\")",
"=COUNT(\"1/1/2020\")",
"=COUNT(\"1-Jan-2020\")",
"=COUNT(\"13:45:00\")",
"=COUNT(\"13:45\")",
"=COUNT(\"1:45 PM\")",
"=COUNT(\"2020-01-01 13:45\")",
"=COUNT(\" 2020-01-01 \")",
"=COUNT(\"5\")",
"=COUNT(5)",
] {
assert_eq!(run(formula, HashMap::new()), Value::Number(1.0), "{formula}");
}
assert_eq!(
run("=COUNT(\"2020-01-01\",5)", HashMap::new()),
Value::Number(2.0)
);
}
#[test]
fn direct_text_that_is_not_a_number_or_date_is_not_counted() {
for formula in ["=COUNT(\"2020-13-01\")", "=COUNT(\"abc\")", "=COUNT(\"\")"] {
assert_eq!(run(formula, HashMap::new()), Value::Number(0.0), "{formula}");
}
}
#[test]
fn array_context_still_skips_date_text() {
assert_eq!(
run("=COUNT({\"2020-01-01\"})", HashMap::new()),
Value::Number(0.0)
);
assert_eq!(
run("=COUNT({\"2020-01-01\",5})", HashMap::new()),
Value::Number(1.0)
);
}