diff --git a/src/bin/mathml2text.rs b/src/bin/mathml2text.rs index ff05b5219..3b55225c3 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 000000000..2f9f418d3 --- /dev/null +++ b/tests/mathml2text.rs @@ -0,0 +1,70 @@ +mod common; + +use std::ffi::OsStr; +use std::path::PathBuf; +use std::process::{Command, Output}; +use std::time::{SystemTime, UNIX_EPOCH}; + +struct Mathml2TextFixture { + input_file: PathBuf, +} + +impl Mathml2TextFixture { + fn new(name: &str, mathml: &str) -> Self { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time should be after UNIX_EPOCH") + .as_nanos(); + let input_file = std::env::temp_dir().join(format!("mathml2text-{name}-{timestamp}.mml")); + std::fs::write(&input_file, mathml).expect("should write temp input file"); + Self { input_file } + } + + fn run_with_rules_dir(&self, rules_dir: impl AsRef) -> Output { + Command::new(env!("CARGO_BIN_EXE_mathml2text")) + .arg("--rules-dir") + .arg(rules_dir) + .arg(&self.input_file) + .output() + .expect("mathml2text should run") + } + + fn missing_rules_dir(&self) -> PathBuf { + self.input_file.with_extension("missing-rules") + } +} + +impl Drop for Mathml2TextFixture { + fn drop(&mut self) { + let _ = std::fs::remove_file(&self.input_file); + } +} + +/// 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 fixture = Mathml2TextFixture::new("explicit-rules", "4"); + let output = fixture.run_with_rules_dir(common::abs_rules_dir_path()); + + assert!( + output.status.success(), + "stderr: {}", + String::from_utf8_lossy(&output.stderr) + ); + assert_eq!("4\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 fixture = Mathml2TextFixture::new("invalid-rules", "5"); + let output = fixture.run_with_rules_dir(fixture.missing_rules_dir()); + + assert!( + !output.status.success(), + "stdout: {}", + String::from_utf8_lossy(&output.stdout) + ); +}