#![allow(clippy::needless_return)]
use regex::Regex;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::LazyLock;
pub use libmathcat::interface::*;
use anyhow::Result;
#[allow(dead_code)]
pub fn init_logger() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug"))
.is_test(true)
.format_timestamp(None)
.format_module_path(false)
.format_indent(None)
.format_level(false)
.init();
}
pub fn abs_rules_dir_path() -> String {
cfg_if::cfg_if! {
if #[cfg(feature = "include-zip")] {
"Rules".to_string()
} else {
return std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("Rules")
.to_str()
.expect("CARGO_MANIFEST_DIR and Rules path must be UTF-8")
.to_string();
}
}
}
#[allow(dead_code)] fn strip_spaces(str: &str) -> String {
static SPACES: LazyLock<Regex> = LazyLock::new(|| Regex::new(r" +").unwrap());
String::from(SPACES.replace_all(str, " "))
}
#[allow(dead_code)] fn check_answer(test: &str, target: &str, failure_message: &str) {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
if let Err(e) = set_mathml(test) {
panic!("{}", errors_to_string(&e));
};
match get_spoken_text() {
Ok(speech) => assert_eq!(target, strip_spaces(&speech), "\ntest with {} failed", failure_message),
Err(e) => panic!("{}", errors_to_string(&e)),
};
Ok(())
}));
if let Err(e) = report_any_panic(result) {
panic!("{}", e);
}
}
fn set_default_speech_prefs() {
set_rules_dir(abs_rules_dir_path()).unwrap();
libmathcat::speech::SPEECH_RULES.with(|rules| {
let rules = rules.borrow_mut();
let mut prefs = rules.pref_manager.borrow_mut();
prefs.set_user_prefs("DecimalSeparator", "Auto").unwrap();
prefs.set_user_prefs("SpeechOverrides_CapitalLetters", "").unwrap(); prefs.set_user_prefs("MathRate", "100").unwrap(); prefs.set_user_prefs("PauseFactor", "100").unwrap(); prefs.set_user_prefs("Verbosity", "Medium").unwrap();
prefs.set_user_prefs("Impairment", "Blindness").unwrap();
});
}
#[allow(dead_code)] pub fn test(language: &str, style: &str, mathml: &str, speech: &str) -> Result<()> {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_default_speech_prefs();
set_preference("Language", language).unwrap();
set_preference("SpeechStyle", style).unwrap();
check_answer(mathml, speech, &format!("{}/{}", language, style));
Ok(())
}));
report_any_panic(result)
}
#[allow(dead_code)] #[allow(non_snake_case)]
pub fn test_prefs(language: &str, speech_style: &str, test_prefs: Vec<(&str, &str)>, mathml: &str, speech: &str) -> Result<()> {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_default_speech_prefs();
set_preference("Language", language).unwrap();
set_preference("SpeechStyle", speech_style).unwrap();
for &(pref_name, pref_value) in &test_prefs {
set_preference(pref_name, pref_value).unwrap();
};
check_answer(mathml, speech, &format!("{}/{} with prefs {:#?}", language, speech_style, test_prefs));
Ok(())
}));
report_any_panic(result)
}
#[allow(dead_code)] #[allow(non_snake_case)]
pub fn test_ClearSpeak(language: &str, pref_name: &str, pref_value: &str, mathml: &str, speech: &str) -> Result<()> {
let prefs = vec![(pref_name, pref_value)];
test_prefs(language, "ClearSpeak", prefs, mathml, speech)
}
#[allow(dead_code)] #[allow(non_snake_case)]
pub fn test_ClearSpeak_prefs(language: &str, prefs: Vec<(&str, &str)>, mathml: &str, speech: &str) -> Result<()> {
test_prefs(language, "ClearSpeak", prefs, mathml, speech)
}
#[allow(dead_code)] #[allow(non_snake_case)]
pub fn test_braille(code: &str, mathml: &str, braille: &str) -> Result<()> {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_rules_dir(abs_rules_dir_path()).unwrap();
set_preference("DecimalSeparator", "Auto").unwrap();
set_preference("BrailleNavHighlight", "Off").unwrap();
set_preference("BrailleNavHighlight", "Off").unwrap();
set_preference("BrailleCode", code).unwrap();
set_preference("LaTeX_UseShortName", "false").unwrap();
match code {
"Vietnam" => set_preference("Language", "vi").unwrap(),
"CMU" => set_preference("Language", "es").unwrap(),
_ => set_preference("Language", "en").unwrap(),
}
if let Err(e) = set_mathml(mathml) {
panic!("{}", errors_to_string(&e));
};
match get_braille("") {
Ok(result) => assert_eq!(braille, &result),
Err(e) => panic!("{}", errors_to_string(&e)),
};
Ok(())
}));
report_any_panic(result)
}
#[allow(dead_code)] pub fn test_braille_prefs(code: &str, test_prefs: Vec<(&str, &str)>, mathml: &str, braille: &str) -> Result<()> {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_rules_dir(abs_rules_dir_path()).unwrap();
set_preference("DecimalSeparator", "Auto").unwrap();
set_preference("BrailleCode", code).unwrap();
match code {
"Vietnam" => set_preference("Language", "vi").unwrap(),
"CMU" => set_preference("Language", "es").unwrap(),
_ => set_preference("Language", "en").unwrap(),
}
set_preference("UseSpacesAroundAllOperators", "false").unwrap(); for &(pref_name, pref_value) in &test_prefs {
set_preference(pref_name, pref_value).unwrap();
};
if let Err(e) = set_mathml(mathml) {
panic!("{}", errors_to_string(&e));
};
match get_braille("") {
Ok(result) => assert_eq!(braille, &result),
Err(e) => panic!("{}", errors_to_string(&e)),
};
Ok(())
}));
report_any_panic(result)
}
#[allow(dead_code)]
pub fn test_intent(mathml: &str, target: &str, test_prefs: Vec<(&str, &str)>) -> Result<()> {
use sxd_document::{dom::Element, parser};
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_rules_dir(abs_rules_dir_path()).unwrap();
libmathcat::speech::SPEECH_RULES.with(|rules| {
let rules = rules.borrow_mut();
let mut prefs = rules.pref_manager.borrow_mut();
prefs.set_user_prefs("DecimalSeparators", ".").unwrap();
prefs.set_user_prefs("BlockSeparators", ", ").unwrap();
});
set_preference("IntentErrorRecovery", "Error").unwrap();
set_preference("SpeechStyle", "SimpleSpeak").unwrap(); for &(pref_name, pref_value) in &test_prefs {
set_preference(pref_name, pref_value).unwrap();
};
let package = &parser::parse(target).expect("Failed to parse target input");
let target_elem = get_element(package);
trim_element(target_elem, true);
let new_package = parser::parse(mathml);
if let Err(e) = new_package {
panic!("Invalid MathML:\n{}\nError is: {}", &mathml, &e.to_string());
}
let new_package = new_package.unwrap();
let mathml_elem = get_element(&new_package);
let computed_intent = match libmathcat::get_intent(mathml_elem, new_package.as_document()) {
Ok(e) => e,
Err(e) => panic!("in intent_from_mathml: {}", libmathcat::errors_to_string(&e)),
};
clean_attrs(computed_intent);
match is_same_element(computed_intent, target_elem, &[]) {
Ok(_) => Ok(()),
Err(e) => {
println!("target:\n{}", libmathcat::pretty_print::mml_to_string(target_elem));
println!("computed intent:\n{}", libmathcat::pretty_print::mml_to_string(computed_intent));
panic!("{}", e)
},
}
}));
fn clean_attrs<'a>(mathml: Element<'a>) -> Element<'a> {
mathml.remove_attribute("id");
mathml.remove_attribute("data-id-added");
let children = mathml.children();
if children.is_empty() || (children.len() == 1 && children[0].element().is_none()) {
return mathml;
}
for child in children {
clean_attrs(child.element().unwrap());
}
mathml
}
report_any_panic(result)
}
#[allow(dead_code)] #[allow(non_snake_case)]
pub fn test_from_braille(code: &str, mathml: &str, braille: &str) -> Result<()> {
init_panic_handler();
let result = catch_unwind(AssertUnwindSafe(|| {
set_rules_dir(abs_rules_dir_path()).unwrap();
set_preference("DecimalSeparator", "Auto").unwrap();
set_preference("BrailleNavHighlight", "Off").unwrap();
set_preference("BrailleNavHighlight", "Off").unwrap();
set_preference("BrailleCode", code).unwrap();
set_preference("LaTeX_UseShortName", "false").unwrap();
match code {
"Vietnam" => set_preference("Language", "vi").unwrap(),
"CMU" => set_preference("Language", "es").unwrap(),
_ => set_preference("Language", "en").unwrap(),
}
if let Err(e) = set_mathml(mathml) {
panic!("{}", errors_to_string(&e));
};
assert!(libmathcat::are_strs_canonically_equal(mathml, braille, &["data-changed", "data-id-added"]));
Ok(())
}));
report_any_panic(result)
}