diff --git a/src/bin/mathml2text.rs b/src/bin/mathml2text.rs index ff05b521..3b55225c 100644 --- a/src/bin/mathml2text.rs +++ b/src/bin/mathml2text.rs @@ -18,6 +18,10 @@ fn get_rules_dir() -> String { return rules_path.as_os_str().to_str().unwrap().to_string(); } +fn resolve_rules_dir(rules_dir: Option) -> PathBuf { + return rules_dir.unwrap_or_else(|| PathBuf::from(get_rules_dir())); +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] enum OutputType { Text, @@ -60,14 +64,15 @@ fn main() -> Result<()> { "#.to_string() }; - if let Err(e) = set_rules_dir(get_rules_dir()) { + let rules_dir = resolve_rules_dir(cli.rules_dir); + if let Err(e) = set_rules_dir(rules_dir.to_string_lossy()) { panic!("Error: exiting -- {}", errors_to_string(&e)); } debug!("Languages: {}", libmathcat::interface::get_supported_languages()?.join(", ")); #[cfg(feature = "include-zip")] info!("***********include-zip is present**********"); - info!("Version = '{}' using Rules dir {}", get_version(), get_rules_dir()); + info!("Version = '{}' using Rules dir {}", get_version(), rules_dir.display()); set_preference("Language", cli.language)?; set_preference("DecimalSeparator", "Auto").unwrap(); diff --git a/tests/mathml2text.rs b/tests/mathml2text.rs new file mode 100644 index 00000000..80193a8b --- /dev/null +++ b/tests/mathml2text.rs @@ -0,0 +1,97 @@ +mod common; + +use std::path::PathBuf; +use std::process::Command; +use std::time::{SystemTime, UNIX_EPOCH}; + +fn mathml2text_command() -> Command { Command::new(env!("CARGO_BIN_EXE_mathml2text")) } + +fn rules_dir() -> String { common::abs_rules_dir_path() } + +fn unique_temp_file(name: &str) -> PathBuf { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time should be after UNIX_EPOCH") + .as_nanos(); + std::env::temp_dir().join(format!("mathml2text-{name}-{timestamp}.mml")) +} + +/// Verifies that `mathml2text` uses an explicitly provided `--rules-dir`. +/// This protects the CLI override path instead of silently falling back to the default rules location. +#[test] +fn accepts_explicit_rules_dir() { + let input_file = unique_temp_file("explicit-rules"); + std::fs::write( + &input_file, + "4", + ) + .expect("should write temp input file"); + + let output = mathml2text_command() + .args([ + "--rules-dir", + rules_dir().as_str(), + ]) + .arg(&input_file) + .output() + .expect("mathml2text should run"); + + let _ = std::fs::remove_file(&input_file); + + assert!(output.status.success(), "stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert_eq!("4\n", String::from_utf8_lossy(&output.stdout)); +} + +/// Verifies that the positional input-file argument still works on its own. +/// This keeps the default CLI file-input path covered while testing the rules-dir changes separately. +#[test] +fn still_accepts_input_file() { + let input_file = unique_temp_file("input-file"); + std::fs::write( + &input_file, + "2", + ) + .expect("should write temp input file"); + + let output = mathml2text_command() + .arg(&input_file) + .output() + .expect("mathml2text should run"); + + let _ = std::fs::remove_file(&input_file); + + assert!(output.status.success(), "stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert_eq!("2\n", String::from_utf8_lossy(&output.stdout)); +} + +/// Verifies that an invalid explicit `--rules-dir` causes the CLI to fail. +/// This protects against regressions where the flag is parsed but then ignored at runtime. +#[test] +fn rejects_invalid_explicit_rules_dir() { + let missing_rules_dir = std::env::temp_dir().join(format!( + "mathml2text-missing-rules-{}", + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time should be after UNIX_EPOCH") + .as_nanos() + )); + let input_file = unique_temp_file("invalid-rules"); + std::fs::write( + &input_file, + "5", + ) + .expect("should write temp input file"); + + let output = mathml2text_command() + .args([ + "--rules-dir", + missing_rules_dir.to_str().expect("temp path should be valid UTF-8"), + ]) + .arg(&input_file) + .output() + .expect("mathml2text should run"); + + let _ = std::fs::remove_file(&input_file); + + assert!(!output.status.success(), "stdout: {}", String::from_utf8_lossy(&output.stdout)); +}