Files
IronCalc/xlsx/tests/test.rs
varuntumbe e7858f7aa9 adding merge cell logic processing
formatting commit

addressing testcase failures

adding one more scenario to case

adding one more scenario to case

Adding update and unmerge functions for merge cell handling

adding one more case to testcase

adding testcases to base code

adding testcase for import/export

adding documentation to some of the PUB function

fixing warnings and test warnings

adding merge cell part cell update restriction to public sytle set fns

addressing reviwers comment : Changed Mergedcell structure and its side effercts

reverting it back to non pub.

renaming update_merge_cells to just merge_cells in model

renaming *unmerge_merged_cell* to *unmerge_cells*

addressing other reviewer's comment + cosmetica naming adjustments

cosmetic changes
2024-11-29 23:13:10 +01:00

567 lines
18 KiB
Rust

#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
use std::io::Read;
use std::{env, fs, io};
use uuid::Uuid;
use ironcalc::compare::{test_file, test_load_and_saving};
use ironcalc::export::save_to_xlsx;
use ironcalc::import::{load_from_icalc, load_from_xlsx, load_from_xlsx_bytes};
use ironcalc_base::types::{HorizontalAlignment, VerticalAlignment};
use ironcalc_base::Model;
// This is a functional test.
// We check that the output of example.xlsx is what we expect.
#[test]
fn test_example() {
let model = load_from_xlsx("tests/example.xlsx", "en", "UTC").unwrap();
// We should use the API once it is in place
let workbook = model.workbook;
let ws = &workbook.worksheets;
let expected_names = vec![
"Sheet1".to_string(),
"Second".to_string(),
"Sheet4".to_string(),
"shared".to_string(),
"Table".to_string(),
"Sheet2".to_string(),
"Created fourth".to_string(),
"Frozen".to_string(),
"Split".to_string(),
"Hidden".to_string(),
];
let names: Vec<String> = ws.iter().map(|s| s.name.clone()).collect();
// One is not not imported and one is hidden
assert_eq!(expected_names, names);
assert_eq!(workbook.views[&0].sheet, 7);
// Test selection:
// First sheet (Sheet1)
// E13 and E13:N20
assert_eq!(ws[0].frozen_rows, 0);
assert_eq!(ws[0].frozen_columns, 0);
assert_eq!(ws[0].views[&0].row, 13);
assert_eq!(ws[0].views[&0].column, 5);
assert_eq!(ws[0].views[&0].range, [13, 5, 20, 14]);
let model2 = load_from_icalc("tests/example.ic").unwrap();
let s = bitcode::encode(&model2.workbook);
assert_eq!(workbook, model2.workbook, "{:?}", s);
}
#[test]
fn test_load_from_xlsx_bytes() {
let file_path = std::path::Path::new("tests/example.xlsx");
let mut file = fs::File::open(file_path).unwrap();
let mut bytes = Vec::new();
file.read_to_end(&mut bytes).unwrap();
let workbook = load_from_xlsx_bytes(&bytes, "home", "en", "UTC").unwrap();
assert_eq!(workbook.views[&0].sheet, 7);
}
#[test]
fn no_grid() {
let model = load_from_xlsx("tests/NoGrid.xlsx", "en", "UTC").unwrap();
{
let workbook = &model.workbook;
let ws = &workbook.worksheets;
// NoGrid does not show grid lines
let no_grid_sheet = &ws[0];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
assert!(!no_grid_sheet.show_grid_lines);
let sheet2 = &ws[1];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
assert!(sheet2.show_grid_lines);
let no_grid_no_headers_sheet = &ws[2];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
// There is also no headers
assert!(!no_grid_no_headers_sheet.show_grid_lines);
}
{
// save it and check again
let temp_file_name = "temp_file_no_grid.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let workbook = &model.workbook;
let ws = &workbook.worksheets;
// NoGrid does not show grid lines
let no_grid_sheet = &ws[0];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
assert!(!no_grid_sheet.show_grid_lines);
let sheet2 = &ws[1];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
assert!(sheet2.show_grid_lines);
let no_grid_no_headers_sheet = &ws[2];
assert_eq!(no_grid_sheet.name, "NoGrid".to_string());
// There is also no headers
assert!(!no_grid_no_headers_sheet.show_grid_lines);
fs::remove_file(temp_file_name).unwrap();
}
}
#[test]
fn test_save_to_xlsx() {
let mut model = load_from_xlsx("tests/example.xlsx", "en", "UTC").unwrap();
model.evaluate();
let temp_file_name = "temp_file_example.xlsx";
// test can safe
save_to_xlsx(&model, temp_file_name).unwrap();
// test can open
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let metadata = &model.workbook.metadata;
assert_eq!(metadata.application, "IronCalc Sheets");
// FIXME: This will need to be updated once we fix versioning
assert_eq!(metadata.app_version, "10.0000");
let workbook = model.workbook;
let ws = &workbook.worksheets;
assert_eq!(workbook.views[&0].sheet, 7);
// Test selection:
// First sheet (Sheet1)
// E13 and E13:N20
assert_eq!(ws[0].frozen_rows, 0);
assert_eq!(ws[0].frozen_columns, 0);
assert_eq!(ws[0].views[&0].row, 13);
assert_eq!(ws[0].views[&0].column, 5);
assert_eq!(ws[0].views[&0].range, [13, 5, 20, 14]);
// TODO: can we show it is the 'same' model?
fs::remove_file(temp_file_name).unwrap();
}
#[test]
fn test_freeze() {
// freeze has 3 frozen columns and 2 frozen rows
let model = load_from_xlsx("tests/freeze.xlsx", "en", "UTC")
.unwrap()
.workbook;
assert_eq!(model.worksheets[0].frozen_rows, 2);
assert_eq!(model.worksheets[0].frozen_columns, 3);
}
#[test]
fn test_split() {
// We test that a workbook with split panes do not produce frozen rows and columns
let model = load_from_xlsx("tests/split.xlsx", "en", "UTC")
.unwrap()
.workbook;
assert_eq!(model.worksheets[0].frozen_rows, 0);
assert_eq!(model.worksheets[0].frozen_columns, 0);
}
fn test_model_has_correct_styles(model: &Model) {
// A1 is bold
let style_a1 = model.get_style_for_cell(0, 1, 1).unwrap();
assert!(style_a1.font.b);
assert!(!style_a1.font.i);
assert!(!style_a1.font.u);
// B1 is Italics
let style_b1 = model.get_style_for_cell(0, 1, 2).unwrap();
assert!(style_b1.font.i);
assert!(!style_b1.font.b);
assert!(!style_b1.font.u);
// C1 Underlined
let style_c1 = model.get_style_for_cell(0, 1, 3).unwrap();
assert!(style_c1.font.u);
assert!(!style_c1.font.b);
assert!(!style_c1.font.i);
// D1 Bold and Italics
let style_d1 = model.get_style_for_cell(0, 1, 4).unwrap();
assert!(style_d1.font.b);
assert!(style_d1.font.i);
assert!(!style_d1.font.u);
// E1 Bold, italics and underlined
let style_e1 = model.get_style_for_cell(0, 1, 5).unwrap();
assert!(style_e1.font.b);
assert!(style_e1.font.i);
assert!(style_e1.font.u);
assert!(!style_e1.font.strike);
// F1 strikethrough
let style_f1 = model.get_style_for_cell(0, 1, 6).unwrap();
assert!(style_f1.font.strike);
// G1 Double underlined just get simple underlined
let style_g1 = model.get_style_for_cell(0, 1, 7).unwrap();
assert!(style_g1.font.u);
let height_row_3 = model.workbook.worksheet(0).unwrap().row_height(3).unwrap();
assert_eq!(height_row_3, 136.0);
let height_row_5 = model.workbook.worksheet(0).unwrap().row_height(5).unwrap();
assert_eq!(height_row_5, 62.0);
// Second sheet has alignment
// Horizontal
let alignment = model.get_style_for_cell(1, 2, 1).unwrap().alignment;
assert_eq!(alignment, None);
let alignment = model
.get_style_for_cell(1, 3, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Left);
let alignment = model
.get_style_for_cell(1, 4, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Distributed);
let alignment = model
.get_style_for_cell(1, 5, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Right);
let alignment = model
.get_style_for_cell(1, 6, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Center);
let alignment = model
.get_style_for_cell(1, 7, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Fill);
let alignment = model
.get_style_for_cell(1, 8, 1)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.horizontal, HorizontalAlignment::Justify);
// Vertical
let alignment = model.get_style_for_cell(1, 2, 2).unwrap().alignment;
assert_eq!(alignment, None);
let alignment = model.get_style_for_cell(1, 3, 2).unwrap().alignment;
assert_eq!(alignment, None);
let alignment = model
.get_style_for_cell(1, 4, 2)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.vertical, VerticalAlignment::Top);
let alignment = model
.get_style_for_cell(1, 5, 2)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.vertical, VerticalAlignment::Center);
let alignment = model
.get_style_for_cell(1, 6, 2)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.vertical, VerticalAlignment::Justify);
let alignment = model
.get_style_for_cell(1, 7, 2)
.unwrap()
.alignment
.unwrap();
assert_eq!(alignment.vertical, VerticalAlignment::Distributed);
}
#[test]
fn test_simple_text() {
let model = load_from_xlsx("tests/basic_text.xlsx", "en", "UTC").unwrap();
test_model_has_correct_styles(&model);
let temp_file_name = "temp_file_test_named_styles.xlsx";
save_to_xlsx(&model, temp_file_name).unwrap();
let model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
fs::remove_file(temp_file_name).unwrap();
test_model_has_correct_styles(&model);
}
#[test]
fn test_defined_names_casing() {
let test_file_path = "tests/calc_tests/defined_names_for_unit_test.xlsx";
let loaded_workbook = load_from_xlsx(test_file_path, "en", "UTC")
.unwrap()
.workbook;
let mut model = Model::from_bytes(&bitcode::encode(&loaded_workbook)).unwrap();
let (row, column) = (2, 13); // B13
let test_cases = [
("=named1", "11"),
("=NAMED1", "11"),
("=NaMeD1", "11"),
("=named2", "22"),
("=NAMED2", "22"),
("=NaMeD2", "22"),
("=named3", "33"),
("=NAMED3", "33"),
("=NaMeD3", "33"),
];
for (formula, expected_value) in test_cases {
model
.set_user_input(0, row, column, formula.to_string())
.unwrap();
model.evaluate();
assert_eq!(
model.get_formatted_cell_value(0, row, column).unwrap(),
expected_value
);
}
}
#[test]
fn test_xlsx() {
let mut entries = fs::read_dir("tests/calc_tests/")
.unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, io::Error>>()
.unwrap();
entries.sort();
let temp_folder = env::temp_dir();
let path = format!("{}", Uuid::new_v4());
let dir = temp_folder.join(path);
fs::create_dir(&dir).unwrap();
for file_path in entries {
let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
let file_path_str = file_path.to_str().unwrap();
println!("Testing file: {}", file_path_str);
if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
if let Err(message) = test_file(file_path_str) {
println!("{}", message);
panic!("Model was evaluated inconsistently with XLSX data.")
}
assert!(test_load_and_saving(file_path_str, &dir).is_ok());
} else {
println!("skipping");
}
}
fs::remove_dir_all(&dir).unwrap();
}
#[test]
fn no_export() {
let mut entries = fs::read_dir("tests/calc_test_no_export/")
.unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, io::Error>>()
.unwrap();
entries.sort();
let temp_folder = env::temp_dir();
let path = format!("{}", Uuid::new_v4());
let dir = temp_folder.join(path);
fs::create_dir(&dir).unwrap();
for file_path in entries {
let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
let file_path_str = file_path.to_str().unwrap();
println!("Testing file: {}", file_path_str);
if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
if let Err(message) = test_file(file_path_str) {
println!("{}", message);
panic!("Model was evaluated inconsistently with XLSX data.")
}
} else {
println!("skipping");
}
}
fs::remove_dir_all(&dir).unwrap();
}
// This test verifies whether exporting the merged cells functionality is happening properly or not.
// It first loads the Excel having the merged cell and exports it to another xlsx and verifies whether merged
// cell node is same in both of the xlsx file or not.
#[test]
fn test_exporting_merged_cells() {
let temp_file_name = "temp_file_test_export_merged_cells.xlsx";
let expected_merge_cell_ref = {
// loading the xlsx file containing merged cells
let example_file_name = "tests/example.xlsx";
let mut model = load_from_xlsx(example_file_name, "en", "UTC").unwrap();
let expected_merge_cell_ref = model
.workbook
.worksheets
.first()
.unwrap()
.merged_cells_list
.clone();
// exporting and saving it in another xlsx
model.evaluate();
save_to_xlsx(&model, temp_file_name).unwrap();
expected_merge_cell_ref
};
{
let mut temp_model = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
{
// loading the previous file back and verifying whether
// merged cells got exported properly or not
let got_merge_cell_ref = &temp_model
.workbook
.worksheets
.first()
.unwrap()
.merged_cells_list
.clone();
assert_eq!(expected_merge_cell_ref, *got_merge_cell_ref);
fs::remove_file(temp_file_name).unwrap();
}
{
// this block is to verify that if there are no
// merged cells, exported xml should not have the
// <mergeCells/> xml node
temp_model
.workbook
.worksheets
.get_mut(0)
.unwrap()
.merged_cells_list
.clear();
save_to_xlsx(&temp_model, temp_file_name).unwrap();
let temp_model2 = load_from_xlsx(temp_file_name, "en", "UTC").unwrap();
let got_merge_cell_ref_cnt = &temp_model2
.workbook
.worksheets
.first()
.unwrap()
.merged_cells_list
.len();
assert!(*got_merge_cell_ref_cnt == 0);
}
}
fs::remove_file(temp_file_name).unwrap();
}
#[test]
fn test_documentation_xlsx() {
let mut entries = fs::read_dir("tests/docs/")
.unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, io::Error>>()
.unwrap();
entries.sort();
// We can't test volatiles
let skip = ["DATE.xlsx", "DAY.xlsx", "MONTH.xlsx", "YEAR.xlsx"];
let skip = skip.map(|s| format!("tests/docs/{s}"));
println!("{:?}", skip);
// dumb counter to make sure we are actually testing the files
assert_eq!(entries.len(), 7);
let temp_folder = env::temp_dir();
let path = format!("{}", Uuid::new_v4());
let dir = temp_folder.join(path);
fs::create_dir(&dir).unwrap();
for file_path in entries {
let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
let file_path_str = file_path.to_str().unwrap();
if skip.contains(&file_path_str.to_string()) {
println!("Skipping file: {}", file_path_str);
continue;
}
println!("Testing file: {}", file_path_str);
if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
if let Err(message) = test_file(file_path_str) {
println!("{}", message);
panic!("Model was evaluated inconsistently with XLSX data.")
}
assert!(test_load_and_saving(file_path_str, &dir).is_ok());
} else {
println!("skipping");
}
}
fs::remove_dir_all(&dir).unwrap();
}
#[test]
fn test_merge_cell_import_export_behaviors() {
// loading the xlsx file containing merged cells
let example_file_name = "tests/Merged_cells.xlsx";
let mut model = load_from_xlsx(example_file_name, "en", "UTC").unwrap();
// Case1 : To check whether Merge cells structures got imported properly or not
let imported_merge_cell_vec = model.workbook.worksheet(0).unwrap().get_merged_cells_list();
assert_eq!(imported_merge_cell_vec.len(), 5);
let range_refs_of_merge_cell: Vec<String> = imported_merge_cell_vec
.iter()
.map(|cell| cell.get_merged_cells_str_ref().unwrap())
.collect();
assert_eq!(
range_refs_of_merge_cell,
[
"C1:D3".to_string(),
"A1:B4".to_string(),
"G1:H7".to_string(),
"D8:E9".to_string(),
"D4:F6".to_string()
]
);
// Create one More Merge cell which Overlaps with 3 More
model.merge_cells(0, "A1:D5").unwrap();
model
.set_user_input(0, 1, 1, "New overlapped Merge cell".to_string())
.unwrap();
let mut style = model.get_style_for_cell(0, 1, 1).unwrap();
style.font.b = true;
assert_eq!(
model.workbook.styles.create_named_style("bold", &style),
Ok(())
);
model.set_cell_style_by_name(0, 1, 1, "bold").unwrap();
// Lets export to different Excell
let exported_merge_cell_xlsx = "temporary_exported_mergecells.xlsx";
save_to_xlsx(&model, exported_merge_cell_xlsx).unwrap();
{
let temp_model = load_from_xlsx(exported_merge_cell_xlsx, "en", "UTC").unwrap();
// Loading the exported sheet back and verifying whether it got exported properly or not
let imported_merge_cell_vec = temp_model
.workbook
.worksheet(0)
.unwrap()
.get_merged_cells_list();
assert_eq!(imported_merge_cell_vec.len(), 3);
let range_refs_of_merge_cell: Vec<String> = imported_merge_cell_vec
.iter()
.map(|cell| cell.get_merged_cells_str_ref().unwrap())
.collect();
assert_eq!(
range_refs_of_merge_cell,
[
"G1:H7".to_string(),
"D8:E9".to_string(),
"A1:D5".to_string()
]
);
}
fs::remove_file(exported_merge_cell_xlsx).unwrap();
}