#![allow(clippy::unwrap_used)] use crate::cell::CellValue; use crate::number_format::to_excel_precision_str; use crate::test::util::new_empty_model; #[test] fn test_empty_model() { let model = new_empty_model(); let names = model.workbook.get_worksheet_names(); assert_eq!(names.len(), 1); assert_eq!(names[0], "Sheet1"); } #[test] fn test_model_simple_evaluation() { let mut model = new_empty_model(); model .set_user_input(0, 1, 1, "= 1 + 3".to_string()) .unwrap(); model.evaluate(); let result = model._get_text_at(0, 1, 1); assert_eq!(result, *"4"); let result = model._get_formula("A1"); assert_eq!(result, *"=1+3"); } #[test] fn test_model_simple_evaluation_order() { let mut model = new_empty_model(); model._set("A1", "=1/2/3"); model._set("A2", "=(1/2)/3"); model._set("A3", "=1/(2/3)"); model.evaluate(); assert_eq!(model._get_text("A1"), *"0.166666667"); assert_eq!(model._get_text("A2"), *"0.166666667"); assert_eq!(model._get_text("A3"), *"1.5"); // Unnecessary parenthesis are lost assert_eq!(model._get_formula("A2"), *"=1/2/3"); assert_eq!(model._get_formula("A3"), *"=1/(2/3)"); } #[test] fn test_model_invalid_formula() { let mut model = new_empty_model(); model.set_user_input(0, 1, 1, "= 1 +".to_string()).unwrap(); model.evaluate(); let result = model._get_text_at(0, 1, 1); assert_eq!(result, *"#ERROR!"); let result = model._get_formula("A1"); assert_eq!(result, *"= 1 +"); } #[test] fn test_model_dependencies() { let mut model = new_empty_model(); model.set_user_input(0, 1, 1, "23".to_string()).unwrap(); // A1 model .set_user_input(0, 1, 2, "= A1* 2-4".to_string()) .unwrap(); // B1 model.evaluate(); let result = model._get_text_at(0, 1, 1); assert_eq!(result, *"23"); assert!(!model._has_formula("A1")); let result = model._get_text_at(0, 1, 2); assert_eq!(result, *"42"); let result = model._get_formula("B1"); assert_eq!(result, *"=A1*2-4"); model .set_user_input(0, 2, 1, "=SUM(A1, B1)".to_string()) .unwrap(); // A2 model.evaluate(); let result = model._get_text_at(0, 2, 1); assert_eq!(result, *"65"); } #[test] fn test_model_strings() { let mut model = new_empty_model(); model .set_user_input(0, 1, 1, "Hello World".to_string()) .unwrap(); model.set_user_input(0, 1, 2, "=A1".to_string()).unwrap(); model.evaluate(); let result = model._get_text_at(0, 1, 1); assert_eq!(result, *"Hello World"); let result = model._get_text_at(0, 1, 2); assert_eq!(result, *"Hello World"); } #[test] fn test_get_sheet_index_by_sheet_id() { let mut model = new_empty_model(); model.new_sheet(); assert_eq!(model.get_sheet_index_by_sheet_id(1), Some(0)); assert_eq!(model.get_sheet_index_by_sheet_id(2), Some(1)); assert_eq!(model.get_sheet_index_by_sheet_id(1337), None); } #[test] fn test_set_row_height() { let mut model = new_empty_model(); let worksheet = model.workbook.worksheet_mut(0).unwrap(); worksheet.set_row_height(5, 25.0).unwrap(); let worksheet = model.workbook.worksheet(0).unwrap(); assert!((25.0 - worksheet.row_height(5).unwrap()).abs() < f64::EPSILON); let worksheet = model.workbook.worksheet_mut(0).unwrap(); worksheet.set_row_height(5, 5.0).unwrap(); let worksheet = model.workbook.worksheet(0).unwrap(); assert!((5.0 - worksheet.row_height(5).unwrap()).abs() < f64::EPSILON); } #[test] fn test_to_excel_precision_str() { struct TestCase<'a> { value: f64, str: &'a str, } let test_cases = vec![ TestCase { value: 2e-23, str: "2e-23", }, TestCase { value: 42.0, str: "42", }, TestCase { value: 200.0e-23, str: "2e-21", }, TestCase { value: -200e-23, str: "-2e-21", }, TestCase { value: 10.002, str: "10.002", }, TestCase { value: f64::INFINITY, str: "inf", }, TestCase { value: f64::NAN, str: "NaN", }, ]; for test_case in test_cases { let str = to_excel_precision_str(test_case.value); assert_eq!(str, test_case.str); } } #[test] fn test_booleans() { let mut model = new_empty_model(); model.set_user_input(0, 1, 1, "true".to_string()).unwrap(); model.set_user_input(0, 2, 1, "TRUE".to_string()).unwrap(); model.set_user_input(0, 3, 1, "True".to_string()).unwrap(); model.set_user_input(0, 4, 1, "false".to_string()).unwrap(); model.set_user_input(0, 5, 1, "FALSE".to_string()).unwrap(); model.set_user_input(0, 6, 1, "False".to_string()).unwrap(); model .set_user_input(0, 1, 2, "=ISLOGICAL(A1)".to_string()) .unwrap(); model .set_user_input(0, 2, 2, "=ISLOGICAL(A2)".to_string()) .unwrap(); model .set_user_input(0, 3, 2, "=ISLOGICAL(A3)".to_string()) .unwrap(); model .set_user_input(0, 4, 2, "=ISLOGICAL(A4)".to_string()) .unwrap(); model .set_user_input(0, 5, 2, "=ISLOGICAL(A5)".to_string()) .unwrap(); model .set_user_input(0, 6, 2, "=ISLOGICAL(A6)".to_string()) .unwrap(); model .set_user_input(0, 1, 5, "=IF(false, True, FALSe)".to_string()) .unwrap(); model.evaluate(); assert_eq!(model._get_text_at(0, 1, 1), *"TRUE"); assert_eq!(model._get_text_at(0, 2, 1), *"TRUE"); assert_eq!(model._get_text_at(0, 3, 1), *"TRUE"); assert_eq!(model._get_text_at(0, 4, 1), *"FALSE"); assert_eq!(model._get_text_at(0, 5, 1), *"FALSE"); assert_eq!(model._get_text_at(0, 6, 1), *"FALSE"); assert_eq!(model._get_text_at(0, 1, 2), *"TRUE"); assert_eq!(model._get_text_at(0, 2, 2), *"TRUE"); assert_eq!(model._get_text_at(0, 3, 2), *"TRUE"); assert_eq!(model._get_text_at(0, 4, 2), *"TRUE"); assert_eq!(model._get_text_at(0, 5, 2), *"TRUE"); assert_eq!(model._get_text_at(0, 6, 2), *"TRUE"); assert_eq!(model._get_formula("E1"), *"=IF(FALSE,TRUE,FALSE)"); } #[test] fn test_set_cell_style() { let mut model = new_empty_model(); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); assert!(!style.font.b); style.font.b = true; assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); assert!(style.font.b); style.font.b = false; assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); let style = model.get_style_for_cell(0, 1, 1).unwrap(); assert!(!style.font.b); } #[test] fn test_copy_cell_style() { let mut model = new_empty_model(); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); style.font.b = true; assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); let mut style = model.get_style_for_cell(0, 1, 2).unwrap(); style.font.i = true; assert!(model.set_cell_style(0, 1, 2, &style).is_ok()); assert!(model.copy_cell_style((0, 1, 1), (0, 1, 2)).is_ok()); let style = model.get_style_for_cell(0, 1, 1).unwrap(); assert!(style.font.b); assert!(!style.font.i); let style = model.get_style_for_cell(0, 1, 2).unwrap(); assert!(style.font.b); assert!(!style.font.i); } #[test] fn test_get_cell_style_index() { let mut model = new_empty_model(); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); let style_index = model.get_cell_style_index(0, 1, 1).unwrap(); assert_eq!(style_index, 0); assert!(!style.font.b); style.font.b = true; assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); let style_index = model.get_cell_style_index(0, 1, 1).unwrap(); assert_eq!(style_index, 1); } #[test] fn test_model_set_cells_with_values_styles() { let mut model = new_empty_model(); // Inputs model.set_user_input(0, 1, 1, "21".to_string()).unwrap(); // A1 model.set_user_input(0, 2, 1, "2".to_string()).unwrap(); // A2 let style_index = model.get_cell_style_index(0, 1, 1).unwrap(); assert_eq!(style_index, 0); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); style.font.b = true; assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); assert!(model.set_cell_style(0, 2, 1, &style).is_ok()); let style_index = model.get_cell_style_index(0, 1, 1).unwrap(); assert_eq!(style_index, 1); let style_index = model.get_cell_style_index(0, 2, 1).unwrap(); assert_eq!(style_index, 1); model.update_cell_with_number(0, 1, 2, 1.0).unwrap(); model.update_cell_with_number(0, 2, 1, 2.0).unwrap(); model.evaluate(); // Styles are not modified let style_index = model.get_cell_style_index(0, 1, 1).unwrap(); assert_eq!(style_index, 1); let style_index = model.get_cell_style_index(0, 2, 1).unwrap(); assert_eq!(style_index, 1); } #[test] fn test_style_fmt_id() { let mut model = new_empty_model(); let mut style = model.get_style_for_cell(0, 1, 1).unwrap(); style.num_fmt = "#.##".to_string(); assert!(model.set_cell_style(0, 1, 1, &style).is_ok()); let style = model.get_style_for_cell(0, 1, 1).unwrap(); assert_eq!(style.num_fmt, "#.##"); let mut style = model.get_style_for_cell(0, 10, 1).unwrap(); style.num_fmt = "$$#,##0.0000".to_string(); assert!(model.set_cell_style(0, 10, 1, &style).is_ok()); let style = model.get_style_for_cell(0, 10, 1).unwrap(); assert_eq!(style.num_fmt, "$$#,##0.0000"); // Make sure old style is not touched let style = model.get_style_for_cell(0, 1, 1).unwrap(); assert_eq!(style.num_fmt, "#.##"); } #[test] fn test_set_sheet_color() { let mut model = new_empty_model(); assert_eq!(model.workbook.worksheet(0).unwrap().color, None); assert!(model.set_sheet_color(0, "#FFFAAA").is_ok()); // Test new tab color is properly set assert_eq!( model.workbook.worksheet(0).unwrap().color, Some("#FFFAAA".to_string()) ); // Test we can remove it assert!(model.set_sheet_color(0, "").is_ok()); assert_eq!(model.workbook.worksheet(0).unwrap().color, None); } #[test] fn test_set_sheet_color_invalid_sheet() { let mut model = new_empty_model(); assert_eq!( model.set_sheet_color(10, "#FFFAAA"), Err("Invalid sheet index".to_string()) ); } #[test] fn test_set_sheet_color_invalid() { let mut model = new_empty_model(); // Boundaries assert!(model.set_sheet_color(0, "#FFFFFF").is_ok()); assert!(model.set_sheet_color(0, "#000000").is_ok()); assert_eq!( model.set_sheet_color(0, "#FFF"), Err("Invalid color: #FFF".to_string()) ); assert_eq!( model.set_sheet_color(0, "-#FFF"), Err("Invalid color: -#FFF".to_string()) ); assert_eq!( model.set_sheet_color(0, "#-FFF"), Err("Invalid color: #-FFF".to_string()) ); assert_eq!( model.set_sheet_color(0, "2FFFFFF"), Err("Invalid color: 2FFFFFF".to_string()) ); assert_eq!( model.set_sheet_color(0, "#FFFFFF1"), Err("Invalid color: #FFFFFF1".to_string()) ); } #[test] fn set_input_autocomplete() { let mut model = new_empty_model(); model._set("A1", "1"); model._set("A2", "2"); model .set_user_input(0, 3, 1, "=SUM(A1:A2".to_string()) .unwrap(); // This will fail anyway model .set_user_input(0, 4, 1, "=SUM(A1*".to_string()) .unwrap(); model.evaluate(); assert_eq!(model._get_formula("A3"), "=SUM(A1:A2)"); assert_eq!(model._get_text("A3"), "3"); assert_eq!(model._get_formula("A4"), "=SUM(A1*"); assert_eq!(model._get_text("A4"), "#ERROR!"); } #[test] fn test_get_cell_value_by_ref() { let mut model = new_empty_model(); model._set("A1", "1"); model._set("A2", "2"); model.evaluate(); // Correct assert_eq!( model.get_cell_value_by_ref("Sheet1!A1"), Ok(CellValue::Number(1.0)) ); // You need to specify full reference assert_eq!( model.get_cell_value_by_ref("A1"), Err("Error parsing reference: 'A1'".to_string()) ); // Error, it has a trailing space assert_eq!( model.get_cell_value_by_ref("Sheet1!A1 "), Err("Error parsing reference: 'Sheet1!A1 '".to_string()) ); } #[test] fn test_get_formatted_cell_value() { let mut model = new_empty_model(); model._set("A1", "foobar"); model._set("A2", "true"); model._set("A3", ""); model._set("A4", "123.456"); model._set("A5", "123.456"); // change A5 format let mut style = model.get_style_for_cell(0, 5, 1).unwrap(); style.num_fmt = "$#,##0.00".to_string(); model.set_cell_style(0, 5, 1, &style).unwrap(); model.evaluate(); assert_eq!(model.get_formatted_cell_value(0, 1, 1).unwrap(), "foobar"); assert_eq!(model.get_formatted_cell_value(0, 2, 1).unwrap(), "TRUE"); assert_eq!(model.get_formatted_cell_value(0, 3, 1).unwrap(), ""); assert_eq!(model.get_formatted_cell_value(0, 4, 1).unwrap(), "123.456"); assert_eq!(model.get_formatted_cell_value(0, 5, 1).unwrap(), "$123.46"); } #[test] fn test_cell_formula() { let mut model = new_empty_model(); model._set("A1", "=1+2+3"); model._set("A2", "foobar"); model.evaluate(); assert_eq!( model.get_cell_formula(0, 1, 1), // A1 Ok(Some("=1+2+3".to_string())), ); assert_eq!( model.get_cell_formula(0, 2, 1), // A2 Ok(None), ); assert_eq!( model.get_cell_formula(0, 3, 1), // A3 - empty cell Ok(None), ); assert_eq!( model.get_cell_formula(42, 1, 1), Err("Invalid sheet index".to_string()), ); } #[test] fn test_xlfn() { let mut model = new_empty_model(); model._set("A1", "=_xlfn.SIN(1)"); model._set("A2", "=_xlfn.SINY(1)"); model._set("A3", "=_xlfn.CONCAT(3, 4.0)"); model.evaluate(); // Only modern formulas strip the '_xlfn.' assert_eq!( model.get_cell_formula(0, 1, 1).unwrap(), Some("=_xlfn.SIN(1)".to_string()) ); // unknown formulas keep the '_xlfn.' prefix assert_eq!( model.get_cell_formula(0, 2, 1).unwrap(), Some("=_xlfn.SINY(1)".to_string()) ); assert_eq!( model.get_cell_formula(0, 3, 1).unwrap(), Some("=CONCAT(3,4)".to_string()) ); } #[test] fn test_letter_case() { let mut model = new_empty_model(); model._set("A1", "=sin(1)"); model._set("A2", "=sIn(2)"); model.evaluate(); assert_eq!( model.get_cell_formula(0, 1, 1).unwrap(), Some("=SIN(1)".to_string()) ); assert_eq!( model.get_cell_formula(0, 2, 1).unwrap(), Some("=SIN(2)".to_string()) ); }