Spaces:
Sleeping
Sleeping
File size: 6,820 Bytes
b72bff3 979f3c3 b72bff3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | """Tests Sprint 78 β A.I.5 : Γ©quivalences diplomatiques en curseur fin.
Couvre :
1. Catalogue ``BUILTIN_EQUIVALENCES`` :
- Au moins une règle par profil
- Règles canoniques nommées (longs_s, u_eq_v, etc.)
- Pas de noms doublons
2. ``list_equivalences_by_profile`` :
- Sans filtre β toutes les rΓ¨gles
- Avec filtre β rΓ¨gles du profil seulement
3. ``apply_selected_equivalences`` :
- Application sΓ©lective : seul ``longs_s`` actif β ΕΏβs mais
pas uβv
- Liste vide β pas de changement
- Texte vide / None β ``""``
- Règle inconnue silencieusement ignorée
4. ``compute_cer_with_equivalences`` :
- Sans Γ©quivalences : CER Γ©levΓ©
- Avec les bonnes Γ©quivalences : CER baisse
- Application bilatΓ©rale (GT et hyp)
"""
from __future__ import annotations
from picarones.measurements.equivalence_profile import (
BUILTIN_EQUIVALENCES,
EquivalenceRule,
apply_selected_equivalences,
compute_cer_with_equivalences,
list_equivalences_by_profile,
)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# 1. Catalogue
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestCatalog:
def test_canonical_rules_present(self) -> None:
for name in (
"longs_s", "u_eq_v", "i_eq_j", "y_eq_i", "vv_eq_w",
"ae_ligature", "oe_ligature", "thorn_th", "eth_th",
"yogh_y",
):
assert name in BUILTIN_EQUIVALENCES, (
f"règle canonique manquante : {name}"
)
def test_rule_structure(self) -> None:
for rule in BUILTIN_EQUIVALENCES.values():
assert isinstance(rule, EquivalenceRule)
assert rule.name
assert rule.source
assert rule.target
assert rule.description
assert rule.profile_tag
def test_unique_names(self) -> None:
names = list(BUILTIN_EQUIVALENCES.keys())
assert len(names) == len(set(names))
def test_longs_s_correct(self) -> None:
rule = BUILTIN_EQUIVALENCES["longs_s"]
assert rule.source == "ΕΏ"
assert rule.target == "s"
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# 2. list_equivalences_by_profile
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestListByProfile:
def test_no_filter_returns_all(self) -> None:
all_rules = list_equivalences_by_profile()
assert len(all_rules) == len(BUILTIN_EQUIVALENCES)
def test_filter_by_medieval_french(self) -> None:
rules = list_equivalences_by_profile("medieval_french")
assert all(r.profile_tag == "medieval_french" for r in rules)
assert len(rules) > 0
def test_unknown_profile_returns_empty(self) -> None:
rules = list_equivalences_by_profile("nonexistent")
assert rules == []
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# 3. apply_selected_equivalences
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestApply:
def test_selective_longs_s_only(self) -> None:
result = apply_selected_equivalences("ΕΏeparare", ["longs_s"])
assert result == "separare"
def test_selective_excludes_unselected(self) -> None:
# u_eq_v non sΓ©lectionnΓ© β "u" doit rester
result = apply_selected_equivalences("ΕΏupra", ["longs_s"])
assert result == "supra"
def test_multiple_selected(self) -> None:
# Avec plusieurs règles, toutes appliquées
result = apply_selected_equivalences(
"ΕΏupra", ["longs_s", "u_eq_v"],
)
# ΕΏβs puis uβv β "svpra"
assert "ΕΏ" not in result
def test_empty_selection_unchanged(self) -> None:
assert apply_selected_equivalences("ΕΏeparare", []) == "ΕΏeparare"
def test_empty_text(self) -> None:
assert apply_selected_equivalences("", ["longs_s"]) == ""
assert apply_selected_equivalences(None, ["longs_s"]) == ""
def test_unknown_rule_ignored(self, caplog) -> None:
result = apply_selected_equivalences(
"ΕΏeparare", ["longs_s", "nonexistent_rule"],
)
# longs_s appliqué, règle inconnue ignorée
assert result == "separare"
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# 4. compute_cer_with_equivalences
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class TestComputeCer:
def test_cer_drops_with_equivalences(self) -> None:
gt = "ΕΏeparare"
hyp = "separare"
cer_no_eq = compute_cer_with_equivalences(gt, hyp, [])
cer_with_eq = compute_cer_with_equivalences(gt, hyp, ["longs_s"])
assert cer_no_eq > 0
assert cer_with_eq == 0.0
def test_bilateral_application(self) -> None:
# Les deux cΓ΄tΓ©s sont normalisΓ©s : si gt et hyp se
# neutralisent par la règle, CER = 0
gt = "ΕΏupra" # avec ΕΏ
hyp = "ΕΏupra" # avec ΕΏ aussi
cer = compute_cer_with_equivalences(gt, hyp, ["longs_s"])
assert cer == 0.0
def test_unrelated_diff_remains(self) -> None:
# DiffΓ©rence indΓ©pendante des Γ©quivalences sΓ©lectionnΓ©es
gt = "ΕΏalpha"
hyp = "ΕΏbeta"
cer = compute_cer_with_equivalences(gt, hyp, ["longs_s"])
# ΕΏ β s appliquΓ© aux deux : "salpha" vs "sbeta" β CER > 0
assert cer > 0
def test_empty_inputs(self) -> None:
assert compute_cer_with_equivalences("", "", ["longs_s"]) == 0.0
|