|
8 | 8 | config/regexes.py changed, the copies would silently diverge with no |
9 | 9 | CI signal. Tests may legally import both sides (test_layering.py's own |
10 | 10 | convention), so this module is where the promise gets checked. |
| 11 | +
|
| 12 | +Layering is the usual reason for a copy but not the only one, so this |
| 13 | +module's scope is the PROMISE rather than that one pair of packages: |
| 14 | +the comma-set pin below reads _pipeline._state instead of config, and |
| 15 | +the last test reaches outside the package altogether, to a TOML file |
| 16 | +that could not import a Python constant if it wanted to. |
11 | 17 | """ |
12 | 18 | import re |
| 19 | +import tomllib |
| 20 | +from pathlib import Path |
13 | 21 |
|
14 | 22 | import pytest |
15 | 23 |
|
@@ -149,3 +157,42 @@ def test_comma_char_matches_the_pipeline_comma_set() -> None: |
149 | 157 | from nameparser._pipeline._state import COMMA_CHARS |
150 | 158 |
|
151 | 159 | assert set(_render._COMMA_CHAR.pattern.strip("[]")) == set(COMMA_CHARS) |
| 160 | + |
| 161 | + |
| 162 | +def test_differential_cjk_rule_matches_the_script_ranges() -> None: |
| 163 | + """The #271 rule in tools/differential/expected_changes.toml hand- |
| 164 | + copies the CJK spans from _vocab._SCRIPT_RANGES into a character |
| 165 | + class. A TOML file cannot import the constant, so this is the one |
| 166 | + copy with no possible alternative -- and the one whose divergence |
| 167 | + is quietest, because the harness is run by hand rather than in CI. |
| 168 | +
|
| 169 | + Both failure directions matter, which is why this compares sets |
| 170 | + rather than checking coverage. A span MISSING from the class turns |
| 171 | + an intended #271 change into an UNEXPLAINED diff (a release |
| 172 | + blocker for the wrong reason); a span that should not be there |
| 173 | + silently classifies a real regression as intended, which is the |
| 174 | + failure the whole harness exists to prevent. |
| 175 | +
|
| 176 | + Han's astral block is out of scope on both sides. The rule omits |
| 177 | + it deliberately -- no corpus name reaches it, see the comment |
| 178 | + there -- so the comparison runs over the BMP spans only, and a new |
| 179 | + BMP script added to _SCRIPT_RANGES still fails here until the rule |
| 180 | + covers it. |
| 181 | + """ |
| 182 | + toml_path = (Path(__file__).parents[2] / "tools" / "differential" |
| 183 | + / "expected_changes.toml") |
| 184 | + rules = tomllib.loads(toml_path.read_text())["change"] |
| 185 | + matched = [r for r in rules if "#271" in r["issue"]] |
| 186 | + assert len(matched) == 1, ( |
| 187 | + f"expected exactly one #271 rule in {toml_path.name}, " |
| 188 | + f"found {len(matched)}") |
| 189 | + declared = { |
| 190 | + (int(lo, 16), int(hi, 16)) |
| 191 | + for lo, hi in re.findall(r"\\u([0-9A-Fa-f]{4})-\\u([0-9A-Fa-f]{4})", |
| 192 | + matched[0]["name_regex"])} |
| 193 | + expected = {span |
| 194 | + for spans in _vocab._SCRIPT_RANGES.values() |
| 195 | + for span in spans if span[1] <= 0xFFFF} |
| 196 | + assert declared == expected, ( |
| 197 | + f"{toml_path.name}'s #271 name_regex declares {sorted(declared)}; " |
| 198 | + f"_SCRIPT_RANGES' BMP spans are {sorted(expected)}") |
0 commit comments