use crate::expression::evaluate_expression_with_context;
use crate::test_helpers::{evaluate_test_expression, evaluate_with_unit_info};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_end_to_end_basic_calculations() {
assert_eq!(evaluate_test_expression("2 + 3"), Some("5".to_string()));
assert_eq!(evaluate_test_expression("10 - 4"), Some("6".to_string()));
assert_eq!(evaluate_test_expression("6 * 7"), Some("42".to_string()));
assert_eq!(evaluate_test_expression("15 / 3"), Some("5".to_string()));
assert_eq!(evaluate_test_expression("2.5 + 1.5"), Some("4".to_string()));
assert_eq!(
evaluate_test_expression("10.2 / 2"),
Some("5.1".to_string())
);
assert_eq!(
evaluate_test_expression("(2 + 3) * 4"),
Some("20".to_string())
);
assert_eq!(
evaluate_test_expression("2 + (3 * 4)"),
Some("14".to_string())
);
}
#[test]
fn test_end_to_end_unit_conversions() {
assert_eq!(
evaluate_test_expression("1 GiB to MiB"),
Some("1,024 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("60 seconds to minutes"),
Some("1 min".to_string())
);
assert_eq!(
evaluate_test_expression("1000 MB to GB"),
Some("1 GB".to_string())
);
assert_eq!(
evaluate_test_expression("2 GiB + 512 MiB to MiB"),
Some("2,560 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("1 hour + 30 minutes to minutes"),
Some("90 min".to_string())
);
assert_eq!(
evaluate_test_expression("24 MiB * 32 in KiB"),
Some("786,432 KiB".to_string())
);
assert_eq!(
evaluate_test_expression("500 GiB / 10 seconds in MiB/s"),
Some("51,200 MiB/s".to_string())
);
}
#[test]
fn test_end_to_end_data_rate_calculations() {
assert_eq!(
evaluate_test_expression("1 hour * 10 GiB/s"),
Some("36,000 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("100 GiB / 10 s"),
Some("10 GiB/s".to_string())
);
assert_eq!(
evaluate_test_expression("50 GiB/s * 2 s"),
Some("100 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("10GiB/s * 30min"),
Some("18,000 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("1000GiB / 10min"),
Some("1.667 GiB/s".to_string())
);
}
#[test]
fn test_end_to_end_qps_calculations() {
assert_eq!(
evaluate_test_expression("100 QPS * 1 hour"),
Some("360,000 query".to_string())
);
assert_eq!(
evaluate_test_expression("25 QPS to req/minute"),
Some("1,500 req/min".to_string())
);
assert_eq!(
evaluate_test_expression("5000 queries / 10 minutes"),
Some("8.333 QPS".to_string())
);
assert_eq!(
evaluate_test_expression("100 QPS + 50 QPS"),
Some("150 QPS".to_string())
);
assert_eq!(
evaluate_test_expression("200 req/min - 80 req/min"),
Some("120 req/min".to_string())
);
assert_eq!(
evaluate_test_expression("100 QPS + 100 req/s"),
Some("200 req/s".to_string())
);
}
#[test]
fn test_end_to_end_large_data_units() {
assert_eq!(
evaluate_test_expression("1000 TB to PB"),
Some("1 PB".to_string())
);
assert_eq!(
evaluate_test_expression("5 PB to TB"),
Some("5,000 TB".to_string())
);
assert_eq!(
evaluate_test_expression("1000 PB to EB"),
Some("1 EB".to_string())
);
assert_eq!(
evaluate_test_expression("1024 TiB to PiB"),
Some("1 PiB".to_string())
);
assert_eq!(
evaluate_test_expression("1024 PiB to EiB"),
Some("1 EiB".to_string())
);
let result = evaluate_with_unit_info("1 PB to PiB");
assert!(result.is_some());
let unit_val = result.unwrap();
assert!((unit_val.value - 0.8881784197).abs() < 0.0001);
}
#[test]
fn test_end_to_end_bit_byte_conversions() {
assert_eq!(
evaluate_test_expression("8 bit to B"),
Some("1 B".to_string())
);
assert_eq!(
evaluate_test_expression("8 Kb to KB"),
Some("1 KB".to_string())
);
assert_eq!(
evaluate_test_expression("8 Mb to MB"),
Some("1 MB".to_string())
);
assert_eq!(
evaluate_test_expression("1 B to bit"),
Some("8 bit".to_string())
);
assert_eq!(
evaluate_test_expression("1 KB to Kb"),
Some("8 Kb".to_string())
);
assert_eq!(
evaluate_test_expression("1 MB to Mb"),
Some("8 Mb".to_string())
);
assert_eq!(
evaluate_test_expression("100 Mbps to MB/s"),
Some("12.5 MB/s".to_string())
);
assert_eq!(
evaluate_test_expression("1 Gbps to MB/s"),
Some("125 MB/s".to_string())
);
}
#[test]
fn test_end_to_end_comma_numbers() {
assert_eq!(
evaluate_test_expression("1,000 + 2,000"),
Some("3,000".to_string())
);
assert_eq!(
evaluate_test_expression("1,000,000 / 1000"),
Some("1,000".to_string())
);
assert_eq!(
evaluate_test_expression("1,234.56 * 2"),
Some("2,469.12".to_string())
);
assert_eq!(
evaluate_test_expression("1,000 GiB to MiB"),
Some("1,024,000 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("1,000,000 bytes to MB"),
Some("1 MB".to_string())
);
}
#[test]
fn test_end_to_end_no_space_units() {
assert_eq!(evaluate_test_expression("5GiB"), Some("5 GiB".to_string()));
assert_eq!(
evaluate_test_expression("100MB to GB"),
Some("0.1 GB".to_string())
);
assert_eq!(
evaluate_test_expression("2.5TiB to GiB"),
Some("2,560 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("5 GiB + 10GiB"),
Some("15 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("1,000GiB + 512 MiB"),
Some("1,024,512 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("10GiB/s * 30min"),
Some("18,000 GiB".to_string())
);
assert_eq!(
evaluate_test_expression("100QPS * 5min"),
Some("30,000 query".to_string())
);
}
#[test]
fn test_end_to_end_complex_expressions() {
assert_eq!(
evaluate_test_expression("((1 TiB + 512 GiB) / 2) * 3 to MiB"),
Some("2,359,296 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("(100 QPS * 1 hour) + (50 req/s * 30 minutes)"),
Some("450,000 req".to_string())
);
assert_eq!(
evaluate_test_expression("(5 PB + 1000 TB) / (10 hours) to GB/s"),
Some("166.667 GB/s".to_string())
);
}
#[test]
fn test_end_to_end_real_world_scenarios() {
assert_eq!(
evaluate_test_expression("Data center: 50 PB + 10 EB"),
Some("10,050 PB".to_string())
);
assert_eq!(
evaluate_test_expression("Total load: 250 QPS + 150 QPS + 100 QPS"),
Some("500 QPS".to_string())
);
assert_eq!(
evaluate_test_expression("Bandwidth used: 1,000 GiB / 1 hour"),
Some("0.278 GiB/s".to_string())
);
assert_eq!(
evaluate_test_expression("Backup rate: 100 TB/s * 8 hours"),
Some("2,880,000 TB".to_string())
);
assert_eq!(
evaluate_test_expression("Network: 10 PB/s to TB/s"),
Some("10,000 TB/s".to_string())
);
}
#[test]
fn test_end_to_end_precision_and_formatting() {
assert_eq!(evaluate_test_expression("1 / 3"), Some("0.333".to_string()));
assert_eq!(
evaluate_test_expression("100 / 3"),
Some("33.333".to_string())
);
assert_eq!(
evaluate_test_expression("1000 / 7"),
Some("142.857".to_string())
);
assert_eq!(
evaluate_test_expression("1000000 + 2000000"),
Some("3,000,000".to_string())
);
assert_eq!(
evaluate_test_expression("1234567.89 + 1"),
Some("1,234,568.89".to_string())
);
assert_eq!(
evaluate_test_expression("0.001 + 0.002"),
Some("0.003".to_string())
);
assert_eq!(
evaluate_test_expression("0.000001 * 1000000"),
Some("1".to_string())
);
}
#[test]
fn test_end_to_end_edge_cases() {
assert_eq!(evaluate_test_expression("0 + 5"), Some("5".to_string()));
assert_eq!(evaluate_test_expression("0 * 100"), Some("0".to_string()));
assert_eq!(
evaluate_test_expression("0 GiB + 5 GiB"),
Some("5 GiB".to_string())
);
assert_eq!(evaluate_test_expression("1 / 0.1"), Some("10".to_string()));
assert_eq!(
evaluate_test_expression("100 / 0.01"),
Some("10,000".to_string())
);
assert_eq!(
evaluate_test_expression("999999 * 999999"),
Some("999,998,000,001".to_string())
);
assert_eq!(
evaluate_test_expression("1 ns to s"),
Some("0 s".to_string())
);
assert_eq!(
evaluate_test_expression("1000000000 ns to s"),
Some("1 s".to_string())
);
}
#[test]
fn test_mixed_text_and_math_expressions() {
assert_eq!(
evaluate_test_expression("The server has 16 GiB of RAM and processes 100 QPS"),
Some("16 GiB".to_string()) );
assert_eq!(
evaluate_test_expression("Download: 1,000 MB at 50 MB/s takes 20 seconds"),
Some("1,000 MB".to_string()) );
assert_eq!(
evaluate_test_expression("Calculate: (5 GiB + 3 GiB) * 2 for total storage"),
Some("16 GiB".to_string()) );
assert_eq!(
evaluate_test_expression("API performance: (100 QPS + 50 req/s) * 1 hour gives total"),
Some("540,000 req".to_string()) );
}
#[test]
fn test_invalid_expression_handling() {
assert_eq!(evaluate_test_expression("invalid expression"), None);
assert_eq!(evaluate_test_expression("(1 + 2"), None);
assert_eq!(evaluate_test_expression("1 + 2)"), None);
assert_eq!(evaluate_test_expression(""), None);
assert_eq!(evaluate_test_expression("5 GiB + 10 seconds"), None);
assert_eq!(evaluate_test_expression("100 QPS - 50 MB"), None);
assert_eq!(
evaluate_test_expression("100 invalidunit"),
Some("100".to_string())
); assert_eq!(
evaluate_test_expression("50 notarealunit to GB"),
Some("50".to_string())
); }
#[test]
fn test_context_with_line_references() {
let lines = vec![Some("10 GiB".to_string()), Some("5 GiB".to_string())];
assert_eq!(
evaluate_expression_with_context("line1 + line2", &lines, 2),
Some("15 GiB".to_string())
);
let lines = vec![Some("1 TiB".to_string()), Some("512 GiB".to_string())];
assert_eq!(
evaluate_expression_with_context("line1 + line2 to MiB", &lines, 2),
Some("1,572,864 MiB".to_string())
);
let lines = vec![Some("100 QPS".to_string()), Some("5 minutes".to_string())];
assert_eq!(
evaluate_expression_with_context("line1 * line2", &lines, 2),
Some("30,000 query".to_string())
);
let lines = vec![Some("10 GiB".to_string())];
assert_eq!(
evaluate_expression_with_context("line1 + line2", &lines, 0),
None );
}
#[test]
fn test_performance_with_large_expressions() {
let nested_expr = "(((((1 + 2) * 3) + 4) * 5) + 6) * 7";
assert_eq!(
evaluate_test_expression(nested_expr),
Some("497".to_string())
);
let long_expr = "1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10";
assert_eq!(evaluate_test_expression(long_expr), Some("55".to_string()));
let unit_expr = "1 GiB + 2 GiB + 3 GiB + 4 GiB + 5 GiB";
assert_eq!(
evaluate_test_expression(unit_expr),
Some("15 GiB".to_string())
);
let conversion_expr = "((1 TiB + 512 GiB) * 2 + 1024 MiB) / 3 to KiB";
let result = evaluate_test_expression(conversion_expr);
assert!(result.is_some());
}
#[test]
fn test_case_insensitivity() {
assert_eq!(
evaluate_test_expression("1 GiB to MiB"),
Some("1,024 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("1 GiB to MiB"),
Some("1,024 MiB".to_string())
);
assert_eq!(
evaluate_test_expression("24 MiB * 32 in KiB"),
Some("786,432 KiB".to_string())
);
assert_eq!(
evaluate_test_expression("1 QPS to req/min"),
Some("60 req/min".to_string())
);
}
#[test]
fn test_file_loading() {
use crate::App;
use std::fs;
use tempfile::NamedTempFile;
let file_content = "5 + 3\n100 GiB to MiB\nx = 42\nx * 2";
let temp_file = NamedTempFile::new().unwrap();
fs::write(temp_file.path(), file_content).unwrap();
let mut app = App::default();
let contents = fs::read_to_string(temp_file.path()).unwrap();
if !contents.trim().is_empty() {
app.text_lines.clear();
app.results.clear();
}
for line in contents.lines() {
app.text_lines.push(line.to_string());
app.results.push(None);
}
app.recalculate_all();
assert_eq!(app.text_lines.len(), 4);
assert_eq!(app.text_lines[0], "5 + 3");
assert_eq!(app.text_lines[1], "100 GiB to MiB");
assert_eq!(app.text_lines[2], "x = 42");
assert_eq!(app.text_lines[3], "x * 2");
assert_eq!(app.results[0], Some("8".to_string()));
assert_eq!(app.results[1], Some("102,400 MiB".to_string()));
assert_eq!(app.results[2], Some("42".to_string()));
assert_eq!(app.results[3], Some("84".to_string()));
}
#[test]
fn test_empty_file_loading() {
use crate::App;
use std::fs;
use tempfile::NamedTempFile;
let temp_file = NamedTempFile::new().unwrap();
fs::write(temp_file.path(), "").unwrap();
let mut app = App::default();
let contents = fs::read_to_string(temp_file.path()).unwrap();
for line in contents.lines() {
app.text_lines.push(line.to_string());
app.results.push(None);
}
if app.text_lines.is_empty() {
app.text_lines.push(String::new());
app.results.push(None);
}
assert_eq!(app.text_lines.len(), 1);
assert_eq!(app.text_lines[0], "");
assert_eq!(app.results[0], None);
}
#[test]
fn test_save_functionality() {
use crate::App;
use std::fs;
use tempfile::NamedTempFile;
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path().to_path_buf();
let mut app = App::default();
app.text_lines = vec![
"5 + 3".to_string(),
"x = 42".to_string(),
"x * 2".to_string(),
];
app.results = vec![None, None, None];
app.set_file_path(Some(temp_path.clone()));
assert!(!app.has_unsaved_changes);
app.cursor_col = 5; app.insert_char('!');
assert!(app.has_unsaved_changes);
app.save().unwrap();
assert!(!app.has_unsaved_changes);
let saved_content = fs::read_to_string(&temp_path).unwrap();
assert_eq!(saved_content, "5 + 3!\nx = 42\nx * 2");
}
#[test]
fn test_save_as_functionality() {
use crate::App;
use std::fs;
use tempfile::NamedTempFile;
let mut app = App::default();
app.text_lines = vec!["test content".to_string()];
app.results = vec![None];
app.has_unsaved_changes = true;
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path().to_path_buf();
app.save_as(temp_path.clone()).unwrap();
assert_eq!(app.file_path, Some(temp_path.clone()));
assert!(!app.has_unsaved_changes);
let saved_content = fs::read_to_string(&temp_path).unwrap();
assert_eq!(saved_content, "test content");
}
#[test]
fn test_unsaved_changes_tracking() {
use crate::App;
let mut app = App::default();
assert!(!app.has_unsaved_changes);
app.insert_char('a');
assert!(app.has_unsaved_changes);
app.has_unsaved_changes = false;
app.delete_char();
assert!(app.has_unsaved_changes);
app.has_unsaved_changes = false;
app.new_line();
assert!(app.has_unsaved_changes);
app.has_unsaved_changes = false;
app.text_lines[0] = "hello world".to_string();
app.cursor_line = 0; app.cursor_col = 11;
app.delete_word();
assert!(app.has_unsaved_changes);
}
#[test]
fn test_loading_non_existent_file() {
use crate::App;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let non_existent_file = temp_dir.path().join("does_not_exist.mathypad");
assert!(!non_existent_file.exists());
let mut app = App::default();
app.set_file_path(Some(non_existent_file.clone()));
app.insert_char('t');
app.insert_char('e');
app.insert_char('s');
app.insert_char('t');
app.save().unwrap();
assert!(non_existent_file.exists());
let content = std::fs::read_to_string(&non_existent_file).unwrap();
assert_eq!(content, "test");
}
#[test]
fn test_non_existent_file_creation() {
use crate::App;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let non_existent_file = temp_dir.path().join("new_file.mathypad");
assert!(!non_existent_file.exists());
let mut app = App::default();
app.set_file_path(Some(non_existent_file.clone()));
app.insert_char('h');
app.insert_char('i');
assert!(!non_existent_file.exists());
app.save().unwrap();
assert!(non_existent_file.exists());
let content = std::fs::read_to_string(&non_existent_file).unwrap();
assert_eq!(content, "hi");
}
#[test]
fn test_unsaved_changes_dialog() {
use crate::App;
let mut app = App::default();
assert!(!app.show_unsaved_dialog);
app.insert_char('t');
app.insert_char('e');
app.insert_char('s');
app.insert_char('t');
assert!(app.has_unsaved_changes);
if app.has_unsaved_changes {
app.show_unsaved_dialog = true;
}
assert!(app.show_unsaved_dialog);
app.show_unsaved_dialog = false;
assert!(!app.show_unsaved_dialog);
app.show_unsaved_dialog = true;
assert!(app.show_unsaved_dialog);
}
#[test]
fn test_save_as_dialog() {
use crate::App;
use tempfile::TempDir;
let mut app = App::default();
assert!(!app.show_save_as_dialog);
assert!(app.save_as_input.is_empty());
app.insert_char('t');
app.insert_char('e');
app.insert_char('s');
app.insert_char('t');
app.show_save_as_dialog(false);
assert!(app.show_save_as_dialog);
assert!(!app.save_as_and_quit);
assert_eq!(app.save_as_input, ".pad");
app.save_as_input = "test_file.pad".to_string();
let temp_dir = TempDir::new().unwrap();
let expected_path = temp_dir.path().join("test_file.pad");
app.save_as_input = expected_path.to_string_lossy().to_string();
let should_quit = app.save_as_from_dialog().unwrap();
assert!(!should_quit); assert!(!app.show_save_as_dialog);
assert!(expected_path.exists());
assert_eq!(app.file_path, Some(expected_path.clone()));
assert!(!app.has_unsaved_changes);
let content = std::fs::read_to_string(&expected_path).unwrap();
assert_eq!(content, "test");
}
#[test]
fn test_save_as_dialog_with_quit() {
use crate::App;
use tempfile::TempDir;
let mut app = App::default();
app.insert_char('h');
app.insert_char('i');
app.show_save_as_dialog(true);
assert!(app.show_save_as_dialog);
assert!(app.save_as_and_quit);
let temp_dir = TempDir::new().unwrap();
let expected_path = temp_dir.path().join("quit_test.pad");
app.save_as_input = expected_path.to_string_lossy().to_string();
let should_quit = app.save_as_from_dialog().unwrap();
assert!(should_quit); assert!(!app.show_save_as_dialog); assert!(!app.save_as_and_quit);
assert!(expected_path.exists());
let content = std::fs::read_to_string(&expected_path).unwrap();
assert_eq!(content, "hi");
}
#[test]
fn test_save_as_dialog_pad_extension() {
use crate::App;
let mut app = App::default();
app.show_save_as_dialog(false);
assert_eq!(app.save_as_input, ".pad");
app.save_as_input = "test.pad".to_string();
assert!(app.save_as_input.ends_with(".pad"));
app.save_as_input = "myfile".to_string();
if !app.save_as_input.ends_with(".pad") && !app.save_as_input.is_empty() {
app.save_as_input.push_str(".pad");
}
assert_eq!(app.save_as_input, "myfile.pad");
}
}