use super::{AffixAnalyzer, AffixStep, Analysis};
use crate::dictionary::Dictionary;
fn workspace(words: &[&str]) -> Dictionary {
let mut dict = Dictionary::new();
for word in words {
dict.add_word(word).expect("no path means no write");
}
dict
}
fn english() -> AffixAnalyzer {
AffixAnalyzer::new("en-US")
}
fn root_of(token: &str) -> Option<String> {
english().analyze(token, None).map(|a| a.root)
}
#[test]
fn a_prefixed_form_resolves_to_its_stem() {
assert_eq!(root_of("subalgebra").as_deref(), Some("algebra"));
assert_eq!(root_of("semicontinuity").as_deref(), Some("continuity"));
assert_eq!(root_of("counit").as_deref(), Some("unit"));
}
#[test]
fn the_hyphenated_spelling_resolves_identically() {
assert_eq!(root_of("sub-algebra"), root_of("subalgebra"));
assert_eq!(root_of("quasi-continuity"), root_of("quasicontinuity"));
}
#[test]
fn a_derivational_suffix_resolves_to_its_stem() {
assert_eq!(root_of("foundedness").as_deref(), Some("founded"));
assert_eq!(root_of("maximality").as_deref(), Some("maximal"));
assert_eq!(root_of("transfinitely").as_deref(), Some("finitely"));
}
#[test]
fn a_prefix_and_a_suffix_compose() {
let analysis = english()
.analyze("subadditivity", None)
.expect("decomposes");
assert_eq!(analysis.root, "additive");
assert_eq!(
analysis.steps,
vec![AffixStep::Prefix("sub"), AffixStep::Suffix("ivity")]
);
}
#[test]
fn a_bare_known_word_is_not_an_analysis() {
assert!(english().analyze("algebra", None).is_none());
assert!(english().analyze("read", None).is_none());
}
#[test]
fn workspace_words_serve_as_roots() {
let dict = workspace(&["hypergraph"]);
let analysis = english()
.analyze("subhypergraph", Some(&dict))
.expect("decomposes onto a user word");
assert_eq!(analysis.root, "hypergraph");
assert!(english().analyze("subhypergraph", None).is_none());
}
#[test]
fn inflectional_endings_are_not_stripped() {
for wrong in ["childs", "mouses", "occured", "begining", "runned"] {
assert!(
english().analyze(wrong, None).is_none(),
"{wrong} must not decompose"
);
}
}
#[test]
fn ly_does_not_restore_a_bare_e() {
assert!(english().analyze("immediatly", None).is_none());
assert_eq!(root_of("subtly").as_deref(), Some("subtle"));
}
#[test]
fn able_keeps_a_soft_e_where_english_does() {
assert_eq!(root_of("movable").as_deref(), Some("move"));
assert!(english().analyze("noticable", None).is_none());
}
#[test]
fn only_one_prefix_is_peeled() {
assert!(english().analyze("recomend", None).is_none());
}
#[test]
fn roots_shorter_than_three_characters_are_refused() {
assert!(
english()
.analyze("subab", Some(&workspace(&["ab"])))
.is_none()
);
assert!(
english()
.analyze("subabc", Some(&workspace(&["abc"])))
.is_some()
);
}
#[test]
fn short_technical_stems_survive() {
assert_eq!(root_of("subset").as_deref(), Some("set"));
assert_eq!(root_of("coset").as_deref(), Some("set"));
}
#[test]
fn non_english_is_not_analysed() {
let german = AffixAnalyzer::new("de-DE");
assert!(german.analyze("subalgebra", None).is_none());
}
#[test]
fn non_ascii_tokens_are_refused() {
assert!(english().analyze("subétale", None).is_none());
assert!(english().analyze("Grüße", None).is_none());
}
#[test]
fn a_decomposition_describes_itself() {
let analysis = Analysis {
root: "algebra".to_string(),
steps: vec![AffixStep::Prefix("sub")],
};
assert_eq!(analysis.describe(), "sub-+algebra");
}
#[test]
fn the_corpus_wins_resolve() {
const WINS: &[&str] = &[
"semicontinuity",
"quasicontinuity",
"subadditivity",
"subderivation",
"subuniverse",
"subformula",
"subterm",
"metavariable",
"preimage",
"codomain",
"coclosure",
"counit",
"multidegree",
"hypergraph",
"bilinearity",
"foundedness",
"definedness",
"closedness",
"algebraicity",
"maximality",
"transfinitely",
"definitionally",
"nonunit",
"preadditive",
"subcollection",
"postcomposition",
"deconstruct",
"polydivision",
"semimodule",
"biconditional",
];
let analyzer = english();
let missed: Vec<&str> = WINS
.iter()
.copied()
.filter(|w| analyzer.analyze(w, None).is_none())
.collect();
assert!(
missed.is_empty(),
"{} of {} corpus words did not decompose: {missed:?}",
missed.len(),
WINS.len()
);
}