601 lines
21 KiB
Rust
601 lines
21 KiB
Rust
#![allow(clippy::unwrap_used)]
|
|
|
|
/// Here we add tests that cannot be done in Excel
|
|
/// Either because Excel does not have that feature (i.e. wrong number of arguments)
|
|
/// or because we differ from Excel throwing #NUM! on invalid dates
|
|
/// We can also enter examples that illustrate/document a part of the function
|
|
use crate::{cell::CellValue, test::util::new_empty_model};
|
|
|
|
// Excel uses a serial date system where Jan 1, 1900 = 1 (though it treats 1900 as a leap year)
|
|
// Most test dates are documented inline, but we define boundary values here:
|
|
const EXCEL_MAX_DATE: f64 = 2958465.0; // Dec 31, 9999 - used in boundary tests
|
|
const EXCEL_INVALID_DATE: f64 = 2958466.0; // One day past max - used in error tests
|
|
|
|
#[test]
|
|
fn test_fn_date_arguments() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Wrong number of arguments produce #ERROR!
|
|
// NB: Excel does not have this error, but does not let you enter wrong number of arguments in the UI
|
|
model._set("A1", "=DATE()");
|
|
model._set("A2", "=DATE(1975)");
|
|
model._set("A3", "=DATE(1975, 2)");
|
|
model._set("A4", "=DATE(1975, 2, 10, 3)");
|
|
|
|
// Arguments are out of rage. This is against Excel
|
|
// Excel will actually compute a date by continuing to the next month, year...
|
|
// We throw #NUM!
|
|
model._set("A5", "=DATE(1975, -2, 10)");
|
|
model._set("A6", "=DATE(1975, 2, -10)");
|
|
model._set("A7", "=DATE(1975, 14, 10)");
|
|
// February doesn't have 30 days
|
|
model._set("A8", "=DATE(1975, 2, 30)");
|
|
|
|
// 1975, a great year, wasn't a leap year
|
|
model._set("A9", "=DATE(1975, 2, 29)");
|
|
// 1976 was
|
|
model._set("A10", "=DATE(1976, 2, 29)");
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A2"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A4"), *"#ERROR!");
|
|
|
|
assert_eq!(model._get_text("A5"), *"10/10/1974");
|
|
assert_eq!(model._get_text("A6"), *"21/01/1975");
|
|
assert_eq!(model._get_text("A7"), *"10/02/1976");
|
|
assert_eq!(model._get_text("A8"), *"02/03/1975");
|
|
|
|
assert_eq!(model._get_text("A9"), *"01/03/1975");
|
|
assert_eq!(model._get_text("A10"), *"29/02/1976");
|
|
assert_eq!(
|
|
model.get_cell_value_by_ref("Sheet1!A10"),
|
|
Ok(CellValue::Number(27819.0))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_date_out_of_range() {
|
|
let mut model = new_empty_model();
|
|
|
|
// month
|
|
model._set("A1", "=DATE(2022, 0, 10)");
|
|
model._set("A2", "=DATE(2022, 13, 10)");
|
|
|
|
// day
|
|
model._set("B1", "=DATE(2042, 5, 0)");
|
|
model._set("B2", "=DATE(2025, 5, 32)");
|
|
|
|
// year (actually years < 1900 don't really make sense)
|
|
model._set("C1", "=DATE(-1, 5, 5)");
|
|
// excel is not compatible with years past 9999
|
|
model._set("C2", "=DATE(10000, 5, 5)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"10/12/2021");
|
|
assert_eq!(model._get_text("A2"), *"10/01/2023");
|
|
assert_eq!(model._get_text("B1"), *"30/04/2042");
|
|
assert_eq!(model._get_text("B2"), *"01/06/2025");
|
|
|
|
assert_eq!(model._get_text("C1"), *"#NUM!");
|
|
assert_eq!(model._get_text("C2"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_year_arguments() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=YEAR()");
|
|
model._set("A2", "=YEAR(27819)");
|
|
model._set("A3", "=YEAR(27819, 3)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A2"), *"1976");
|
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_month_arguments() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=MONTH()");
|
|
model._set("A2", "=MONTH(27819)");
|
|
model._set("A3", "=MONTH(27819, 3)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A2"), *"2");
|
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_day_arguments() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=DAY()");
|
|
model._set("A2", "=DAY(27819)");
|
|
model._set("A3", "=DAY(27819, 3)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A2"), *"29");
|
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_day_small_serial() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=DAY(-1)");
|
|
model._set("A2", "=DAY(0)");
|
|
model._set("A3", "=DAY(60)");
|
|
|
|
model._set("A4", "=DAY(61)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#NUM!");
|
|
assert_eq!(model._get_text("A2"), *"#NUM!");
|
|
// Excel thinks is Feb 29, 1900
|
|
assert_eq!(model._get_text("A3"), *"28");
|
|
|
|
// From now on everyone agrees
|
|
assert_eq!(model._get_text("A4"), *"1");
|
|
}
|
|
|
|
#[test]
|
|
fn test_month_small_serial() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=MONTH(-1)");
|
|
model._set("A2", "=MONTH(0)");
|
|
model._set("A3", "=MONTH(60)");
|
|
|
|
model._set("A4", "=MONTH(61)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#NUM!");
|
|
assert_eq!(model._get_text("A2"), *"#NUM!");
|
|
// We agree with Excel here (We are both in Feb)
|
|
assert_eq!(model._get_text("A3"), *"2");
|
|
|
|
// Same as Excel
|
|
assert_eq!(model._get_text("A4"), *"3");
|
|
}
|
|
|
|
#[test]
|
|
fn test_year_small_serial() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=YEAR(-1)");
|
|
model._set("A2", "=YEAR(0)");
|
|
model._set("A3", "=YEAR(60)");
|
|
|
|
model._set("A4", "=YEAR(61)");
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"#NUM!");
|
|
assert_eq!(model._get_text("A2"), *"#NUM!");
|
|
|
|
assert_eq!(model._get_text("A3"), *"1900");
|
|
|
|
// Same as Excel
|
|
assert_eq!(model._get_text("A4"), *"1900");
|
|
}
|
|
|
|
#[test]
|
|
fn test_date_early_dates() {
|
|
let mut model = new_empty_model();
|
|
model._set("A1", "=DATE(1900, 1, 1)");
|
|
model._set("A2", "=DATE(1900, 2, 28)");
|
|
model._set("B2", "=DATE(1900, 2, 29)");
|
|
model._set("A3", "=DATE(1900, 3, 1)");
|
|
|
|
model.evaluate();
|
|
|
|
// This is 1 in Excel, we agree with Google Docs
|
|
assert_eq!(model._get_text("A1"), *"01/01/1900");
|
|
assert_eq!(
|
|
model.get_cell_value_by_ref("Sheet1!A1"),
|
|
Ok(CellValue::Number(2.0))
|
|
);
|
|
|
|
// 1900 was not a leap year, this is a bug in EXCEL
|
|
// This would be 60 in Excel
|
|
assert_eq!(model._get_text("A2"), *"28/02/1900");
|
|
assert_eq!(
|
|
model.get_cell_value_by_ref("Sheet1!A2"),
|
|
Ok(CellValue::Number(60.0))
|
|
);
|
|
|
|
// This does not agree with Excel, instead of mistakenly allowing
|
|
// for Feb 29, it will auto-wrap to the next day after Feb 28.
|
|
assert_eq!(model._get_text("B2"), *"01/03/1900");
|
|
|
|
// This agrees with Excel from he onward
|
|
assert_eq!(model._get_text("A3"), *"01/03/1900");
|
|
assert_eq!(
|
|
model.get_cell_value_by_ref("Sheet1!A3"),
|
|
Ok(CellValue::Number(61.0))
|
|
);
|
|
}
|
|
#[test]
|
|
fn test_days_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Basic functionality
|
|
model._set("A1", "=DAYS(44570,44561)");
|
|
model._set("A2", "=DAYS(44561,44570)"); // Reversed order
|
|
model._set("A3", "=DAYS(44561,44561)");
|
|
|
|
// Edge cases
|
|
model._set("A4", "=DAYS(1,2)"); // Early dates
|
|
model._set(
|
|
"A5",
|
|
&format!("=DAYS({},{})", EXCEL_MAX_DATE, EXCEL_MAX_DATE - 1.0),
|
|
); // Near max date
|
|
|
|
// Error cases - wrong argument count
|
|
model._set("A6", "=DAYS()");
|
|
model._set("A7", "=DAYS(44561)");
|
|
model._set("A8", "=DAYS(44561,44570,1)");
|
|
|
|
// Error cases - invalid dates
|
|
model._set("A9", "=DAYS(-1,44561)");
|
|
model._set("A10", &format!("=DAYS(44561,{EXCEL_INVALID_DATE})"));
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"9");
|
|
assert_eq!(model._get_text("A2"), *"-9");
|
|
assert_eq!(model._get_text("A3"), *"0");
|
|
assert_eq!(model._get_text("A4"), *"-1"); // DAYS(1,2) = 1-2 = -1
|
|
assert_eq!(model._get_text("A5"), *"1");
|
|
assert_eq!(model._get_text("A6"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A7"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A8"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A9"), *"#NUM!");
|
|
assert_eq!(model._get_text("A10"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_days360_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Basic functionality with different basis values
|
|
model._set("A1", "=DAYS360(44196,44560)"); // Default basis (US 30/360)
|
|
model._set("A2", "=DAYS360(44196,44560,FALSE)"); // US 30/360 explicitly
|
|
model._set("A3", "=DAYS360(44196,44560,TRUE)"); // European 30/360
|
|
|
|
// Same date
|
|
model._set("A4", "=DAYS360(44561,44561)");
|
|
model._set("A5", "=DAYS360(44561,44561,TRUE)");
|
|
|
|
// Reverse order (negative result)
|
|
model._set("A6", "=DAYS360(44560,44196)");
|
|
model._set("A7", "=DAYS360(44560,44196,TRUE)");
|
|
|
|
// Edge cases
|
|
model._set("A8", "=DAYS360(1,2)");
|
|
model._set("A9", "=DAYS360(1,2,FALSE)");
|
|
|
|
// Error cases - wrong argument count
|
|
model._set("A10", "=DAYS360()");
|
|
model._set("A11", "=DAYS360(44561)");
|
|
model._set("A12", "=DAYS360(44561,44570,TRUE,1)");
|
|
|
|
// Error cases - invalid dates
|
|
model._set("A13", "=DAYS360(-1,44561)");
|
|
model._set("A14", &format!("=DAYS360(44561,{EXCEL_INVALID_DATE})"));
|
|
|
|
model.evaluate();
|
|
|
|
assert_eq!(model._get_text("A1"), *"360");
|
|
assert_eq!(model._get_text("A2"), *"360");
|
|
assert_eq!(model._get_text("A3"), *"360");
|
|
assert_eq!(model._get_text("A4"), *"0");
|
|
assert_eq!(model._get_text("A5"), *"0");
|
|
assert_eq!(model._get_text("A6"), *"-360");
|
|
assert_eq!(model._get_text("A7"), *"-360");
|
|
assert_eq!(model._get_text("A8"), *"1");
|
|
assert_eq!(model._get_text("A9"), *"1");
|
|
assert_eq!(model._get_text("A10"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A11"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A12"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A13"), *"#NUM!");
|
|
assert_eq!(model._get_text("A14"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_weekday_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Test return_type parameter variations with one known date (Friday 44561)
|
|
model._set("A1", "=WEEKDAY(44561)"); // Default: Sun=1, Fri=6
|
|
model._set("A2", "=WEEKDAY(44561,2)"); // Mon=1, Fri=5
|
|
model._set("A3", "=WEEKDAY(44561,3)"); // Mon=0, Fri=4
|
|
|
|
// Test boundary days (Sun/Mon) to verify return_type logic
|
|
model._set("A4", "=WEEKDAY(44556,1)"); // Sunday: should be 1
|
|
model._set("A5", "=WEEKDAY(44556,2)"); // Sunday: should be 7
|
|
model._set("A6", "=WEEKDAY(44557,2)"); // Monday: should be 1
|
|
|
|
// Error cases
|
|
model._set("A7", "=WEEKDAY()"); // Wrong arg count
|
|
model._set("A8", "=WEEKDAY(44561,0)"); // Invalid return_type
|
|
model._set("A9", "=WEEKDAY(-1)"); // Invalid date
|
|
|
|
model.evaluate();
|
|
|
|
// Core functionality
|
|
assert_eq!(model._get_text("A1"), *"6"); // Friday default
|
|
assert_eq!(model._get_text("A2"), *"5"); // Friday Mon=1
|
|
assert_eq!(model._get_text("A3"), *"4"); // Friday Mon=0
|
|
|
|
// Boundary verification
|
|
assert_eq!(model._get_text("A4"), *"1"); // Sunday Sun=1
|
|
assert_eq!(model._get_text("A5"), *"7"); // Sunday Mon=1
|
|
assert_eq!(model._get_text("A6"), *"1"); // Monday Mon=1
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A7"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A8"), *"#VALUE!");
|
|
assert_eq!(model._get_text("A9"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_weeknum_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Test different return_type values (1=week starts Sunday, 2=week starts Monday)
|
|
model._set("A1", "=WEEKNUM(44561)"); // Default return_type=1
|
|
model._set("A2", "=WEEKNUM(44561,1)"); // Sunday start
|
|
model._set("A3", "=WEEKNUM(44561,2)"); // Monday start
|
|
|
|
// Test year boundaries
|
|
model._set("A4", "=WEEKNUM(43831,1)"); // Jan 1, 2020 (Wednesday)
|
|
model._set("A5", "=WEEKNUM(43831,2)"); // Jan 1, 2020 (Wednesday)
|
|
model._set("A6", "=WEEKNUM(44196,1)"); // Dec 31, 2020 (Thursday)
|
|
model._set("A7", "=WEEKNUM(44196,2)"); // Dec 31, 2020 (Thursday)
|
|
|
|
// Test first and last weeks of year
|
|
model._set("A8", "=WEEKNUM(44197,1)"); // Jan 1, 2021 (Friday)
|
|
model._set("A9", "=WEEKNUM(44197,2)"); // Jan 1, 2021 (Friday)
|
|
model._set("A10", "=WEEKNUM(44561,1)"); // Dec 31, 2021 (Friday)
|
|
model._set("A11", "=WEEKNUM(44561,2)"); // Dec 31, 2021 (Friday)
|
|
|
|
// Error cases - wrong argument count
|
|
model._set("A12", "=WEEKNUM()");
|
|
model._set("A13", "=WEEKNUM(44561,1,1)");
|
|
|
|
// Error cases - invalid return_type
|
|
model._set("A14", "=WEEKNUM(44561,0)");
|
|
model._set("A15", "=WEEKNUM(44561,3)");
|
|
model._set("A16", "=WEEKNUM(44561,-1)");
|
|
|
|
// Error cases - invalid dates
|
|
model._set("A17", "=WEEKNUM(-1)");
|
|
model._set("A18", &format!("=WEEKNUM({EXCEL_INVALID_DATE})"));
|
|
|
|
model.evaluate();
|
|
|
|
// Basic functionality
|
|
assert_eq!(model._get_text("A1"), *"53"); // Week 53
|
|
assert_eq!(model._get_text("A2"), *"53"); // Week 53 (Sunday start)
|
|
assert_eq!(model._get_text("A3"), *"53"); // Week 53 (Monday start)
|
|
|
|
// Year boundary tests
|
|
assert_eq!(model._get_text("A4"), *"1"); // Jan 1, 2020 (Sunday start)
|
|
assert_eq!(model._get_text("A5"), *"1"); // Jan 1, 2020 (Monday start)
|
|
assert_eq!(model._get_text("A6"), *"53"); // Dec 31, 2020 (Sunday start)
|
|
assert_eq!(model._get_text("A7"), *"53"); // Dec 31, 2020 (Monday start)
|
|
|
|
// 2021 tests
|
|
assert_eq!(model._get_text("A8"), *"1"); // Jan 1, 2021 (Sunday start)
|
|
assert_eq!(model._get_text("A9"), *"1"); // Jan 1, 2021 (Monday start)
|
|
assert_eq!(model._get_text("A10"), *"53"); // Dec 31, 2021 (Sunday start)
|
|
assert_eq!(model._get_text("A11"), *"53"); // Dec 31, 2021 (Monday start)
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A12"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A13"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A14"), *"#VALUE!");
|
|
assert_eq!(model._get_text("A15"), *"#VALUE!");
|
|
assert_eq!(model._get_text("A16"), *"#VALUE!");
|
|
assert_eq!(model._get_text("A17"), *"#NUM!");
|
|
assert_eq!(model._get_text("A18"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_workday_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Basic functionality
|
|
model._set("A1", "=WORKDAY(44560,1)");
|
|
model._set("A2", "=WORKDAY(44561,-1)");
|
|
model._set("A3", "=WORKDAY(44561,0)");
|
|
model._set("A4", "=WORKDAY(44560,5)");
|
|
|
|
// Test with holidays
|
|
model._set("B1", "44561");
|
|
model._set("A5", "=WORKDAY(44560,1,B1)"); // Should skip the holiday
|
|
model._set("B2", "44562");
|
|
model._set("B3", "44563");
|
|
model._set("A6", "=WORKDAY(44560,3,B1:B3)"); // Multiple holidays
|
|
|
|
// Test starting on weekend
|
|
model._set("A7", "=WORKDAY(44562,1)"); // Saturday start
|
|
model._set("A8", "=WORKDAY(44563,1)"); // Sunday start
|
|
|
|
// Test negative workdays
|
|
model._set("A9", "=WORKDAY(44565,-3)"); // Go backwards 3 days
|
|
model._set("A10", "=WORKDAY(44565,-5,B1:B3)"); // Backwards with holidays
|
|
|
|
// Edge cases
|
|
model._set("A11", "=WORKDAY(1,1)"); // Early date
|
|
model._set("A12", "=WORKDAY(100000,10)"); // Large numbers
|
|
|
|
// Error cases - wrong argument count
|
|
model._set("A13", "=WORKDAY()");
|
|
model._set("A14", "=WORKDAY(44560)");
|
|
model._set("A15", "=WORKDAY(44560,1,B1,B2)");
|
|
|
|
// Error cases - invalid dates
|
|
model._set("A16", "=WORKDAY(-1,1)");
|
|
model._set("A17", &format!("=WORKDAY({EXCEL_INVALID_DATE},1)"));
|
|
|
|
// Error cases - invalid holiday dates
|
|
model._set("B4", "-1");
|
|
model._set("A18", "=WORKDAY(44560,1,B4)");
|
|
|
|
model.evaluate();
|
|
|
|
// Basic functionality
|
|
assert_eq!(model._get_text("A1"), *"44561"); // 1 day forward
|
|
assert_eq!(model._get_text("A2"), *"44560"); // 1 day backward
|
|
assert_eq!(model._get_text("A3"), *"44561"); // 0 days
|
|
assert_eq!(model._get_text("A4"), *"44567"); // 5 days forward
|
|
|
|
// With holidays
|
|
assert_eq!(model._get_text("A5"), *"44564"); // Skip holiday, go to Monday
|
|
assert_eq!(model._get_text("A6"), *"44566"); // Skip multiple holidays
|
|
|
|
// Weekend starts
|
|
assert_eq!(model._get_text("A7"), *"44564"); // From Saturday
|
|
assert_eq!(model._get_text("A8"), *"44564"); // From Sunday
|
|
|
|
// Negative workdays
|
|
assert_eq!(model._get_text("A9"), *"44560"); // 3 days back
|
|
assert_eq!(model._get_text("A10"), *"44557"); // 5 days back with holidays
|
|
|
|
// Edge cases
|
|
assert_eq!(model._get_text("A11"), *"2"); // Early date
|
|
assert_eq!(model._get_text("A12"), *"100014"); // Large numbers
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A13"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A14"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A15"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A16"), *"#NUM!");
|
|
assert_eq!(model._get_text("A17"), *"#NUM!");
|
|
assert_eq!(model._get_text("A18"), *"#NUM!"); // Invalid holiday
|
|
}
|
|
|
|
#[test]
|
|
fn test_workday_intl_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Test key weekend mask types
|
|
model._set("A1", "=WORKDAY.INTL(44560,1,1)"); // Numeric: standard (Sat-Sun)
|
|
model._set("A2", "=WORKDAY.INTL(44560,1,2)"); // Numeric: Sun-Mon
|
|
model._set("A3", "=WORKDAY.INTL(44560,1,\"0000001\")"); // String: Sunday only
|
|
model._set("A4", "=WORKDAY.INTL(44560,1,\"1100000\")"); // String: Mon-Tue
|
|
|
|
// Test with holidays
|
|
model._set("B1", "44561");
|
|
model._set("A5", "=WORKDAY.INTL(44560,2,1,B1)"); // Standard + holiday
|
|
model._set("A6", "=WORKDAY.INTL(44560,2,7,B1)"); // Fri-Sat + holiday
|
|
|
|
// Basic edge cases
|
|
model._set("A7", "=WORKDAY.INTL(44561,0,1)"); // Zero days
|
|
model._set("A8", "=WORKDAY.INTL(44565,-1,1)"); // Negative days
|
|
|
|
// Error cases
|
|
model._set("A9", "=WORKDAY.INTL()"); // Wrong arg count
|
|
model._set("A10", "=WORKDAY.INTL(44560,1,0)"); // Invalid weekend mask
|
|
model._set("A11", "=WORKDAY.INTL(44560,1,\"123\")"); // Invalid string mask
|
|
model._set("A12", "=WORKDAY.INTL(-1,1,1)"); // Invalid date
|
|
|
|
model.evaluate();
|
|
|
|
// Weekend mask functionality
|
|
assert_eq!(model._get_text("A1"), *"44561"); // Standard weekend
|
|
assert_eq!(model._get_text("A2"), *"44561"); // Sun-Mon weekend
|
|
assert_eq!(model._get_text("A3"), *"44561"); // Sunday only
|
|
assert_eq!(model._get_text("A4"), *"44561"); // Mon-Tue weekend
|
|
|
|
// With holidays
|
|
assert_eq!(model._get_text("A5"), *"44565"); // Skip holiday + standard weekend
|
|
assert_eq!(model._get_text("A6"), *"44564"); // Skip holiday + Fri-Sat weekend
|
|
|
|
// Edge cases
|
|
assert_eq!(model._get_text("A7"), *"44561"); // Zero days
|
|
assert_eq!(model._get_text("A8"), *"44564"); // Negative days
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A9"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A10"), *"#NUM!");
|
|
assert_eq!(model._get_text("A11"), *"#VALUE!");
|
|
assert_eq!(model._get_text("A12"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_yearfrac_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Test key basis values (not exhaustive - just verify parameter works)
|
|
model._set("A1", "=YEARFRAC(44561,44926)"); // Default (30/360)
|
|
model._set("A2", "=YEARFRAC(44561,44926,1)"); // Actual/actual
|
|
model._set("A3", "=YEARFRAC(44561,44926,4)"); // European 30/360
|
|
|
|
// Edge cases
|
|
model._set("A4", "=YEARFRAC(44561,44561,1)"); // Same date = 0
|
|
model._set("A6", "=YEARFRAC(44197,44562,1)"); // Exact year (2021)
|
|
|
|
// Error cases
|
|
model._set("A7", "=YEARFRAC()"); // Wrong arg count
|
|
model._set("A8", "=YEARFRAC(44561,44926,5)"); // Invalid basis
|
|
model._set("A9", "=YEARFRAC(-1,44926,1)"); // Invalid date
|
|
|
|
model.evaluate();
|
|
|
|
// Basic functionality (approximate values expected)
|
|
assert_eq!(model._get_text("A1"), *"1"); // About 1 year
|
|
assert_eq!(model._get_text("A2"), *"1"); // About 1 year
|
|
assert_eq!(model._get_text("A3"), *"1"); // About 1 year
|
|
|
|
// Edge cases
|
|
assert_eq!(model._get_text("A4"), *"0"); // Same date
|
|
assert_eq!(model._get_text("A6"), *"1"); // Exact year
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A7"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A8"), *"#NUM!"); // Invalid basis should return #NUM!
|
|
assert_eq!(model._get_text("A9"), *"#NUM!");
|
|
}
|
|
|
|
#[test]
|
|
fn test_isoweeknum_function() {
|
|
let mut model = new_empty_model();
|
|
|
|
// Basic functionality
|
|
model._set("A1", "=ISOWEEKNUM(44563)"); // Mid-week date
|
|
model._set("A2", "=ISOWEEKNUM(44561)"); // Year-end date
|
|
|
|
// Key ISO week boundaries (just critical cases)
|
|
model._set("A3", "=ISOWEEKNUM(44197)"); // Jan 1, 2021 (Fri) -> Week 53 of 2020
|
|
model._set("A4", "=ISOWEEKNUM(44200)"); // Jan 4, 2021 (Mon) -> Week 1 of 2021
|
|
model._set("A5", "=ISOWEEKNUM(44564)"); // Jan 3, 2022 (Mon) -> Week 1 of 2022
|
|
|
|
// Error cases
|
|
model._set("A6", "=ISOWEEKNUM()"); // Wrong arg count
|
|
model._set("A7", "=ISOWEEKNUM(-1)"); // Invalid date
|
|
|
|
model.evaluate();
|
|
|
|
// Basic functionality
|
|
assert_eq!(model._get_text("A1"), *"52");
|
|
assert_eq!(model._get_text("A2"), *"52");
|
|
|
|
// ISO week boundaries
|
|
assert_eq!(model._get_text("A3"), *"53"); // Week 53 of previous year
|
|
assert_eq!(model._get_text("A4"), *"1"); // Week 1 of current year
|
|
assert_eq!(model._get_text("A5"), *"1"); // Week 1 of next year
|
|
|
|
// Error cases
|
|
assert_eq!(model._get_text("A6"), *"#ERROR!");
|
|
assert_eq!(model._get_text("A7"), *"#NUM!");
|
|
}
|