Skip to content

Commit 26dd5da

Browse files
committed
test: isolate pipeline-stage branches only hit via the full corpus
Several branches were only reached through tests/v2/test_cases.py's full-pipeline corpus, so a regression scoped to one stage wouldn't be pinpointed by that stage's own test file: assign's SUFFIX_COMMA structure, segment's cosmetic-vs-structural empty-bucket handling (single trailing comma vs. a leading comma), group's extra_suffix_delimiters entry-splitting in a FAMILY_COMMA tail segment, and tokenize's interleaving of two simultaneously active extracted regions (review finding on #288).
1 parent 85bee41 commit 26dd5da

4 files changed

Lines changed: 79 additions & 2 deletions

File tree

tests/v2/pipeline/test_assign.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ def test_suffix_comma_and_extra_segments() -> None:
101101
assert _by_role(out, Role.SUFFIX) == "Jr."
102102

103103

104+
def test_suffix_comma_structure() -> None:
105+
# Structure.SUFFIX_COMMA (segment.py): >1 word before the first
106+
# comma AND every post-first segment is entirely lenient-suffix.
107+
# ('Smith, John, Jr.' above only has ONE word before its first
108+
# comma, so it is FAMILY_COMMA with a trailing suffix segment, not
109+
# this branch.) Previously this assign() branch (tail=1) was only
110+
# reached via the full-corpus test, not its own isolated case.
111+
out = _assigned("John Smith, PhD")
112+
assert _by_role(out, Role.GIVEN) == "John"
113+
assert _by_role(out, Role.FAMILY) == "Smith"
114+
assert _by_role(out, Role.SUFFIX) == "PhD"
115+
116+
104117
def test_trailing_suffix_run_no_comma() -> None:
105118
out = _assigned("John Jack Kennedy PhD MD")
106119
assert _by_role(out, Role.MIDDLE) == "Jack"

tests/v2/pipeline/test_group.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
# real vocabulary overlap; the subset rule only constrains particles.
2323

2424

25-
def _grouped(text: str) -> ParseState:
26-
state = ParseState(original=text, lexicon=_LEX, policy=Policy())
25+
def _grouped(text: str, policy: Policy | None = None,
26+
lexicon: Lexicon | None = None) -> ParseState:
27+
state = ParseState(original=text, lexicon=lexicon or _LEX,
28+
policy=policy or Policy())
2729
return group(classify(segment(tokenize(extract_delimited(state)))))
2830

2931

@@ -128,6 +130,30 @@ def test_initials_do_not_count_as_rootnames_for_conjunction_carveout() -> None:
128130
assert _piece_texts(out) == [["J.", "Ruiz", "y", "Gomez"]]
129131

130132

133+
def test_extra_suffix_delimiter_splits_tail_entries() -> None:
134+
# v1 expand_suffix_delimiter parity (#191): a configured delimiter
135+
# is transparent in a FAMILY_COMMA tail segment -- it separates
136+
# suffix ENTRIES (each rendered independently) and is itself
137+
# dropped, rather than becoming a suffix token or fusing the
138+
# entries on either side into one "joined" run.
139+
lex = Lexicon(suffix_acronyms=frozenset({"phd"}),
140+
suffix_words=frozenset({"jr", "v", "md"}))
141+
out = _grouped("Smith, John, V MD / PhD",
142+
Policy(extra_suffix_delimiters=frozenset({"/"})),
143+
lexicon=lex)
144+
# one tail SEGMENT, split into three suffix pieces: "/" is dropped
145+
# rather than surviving as its own piece
146+
assert _piece_texts(out) == [["Smith"], ["John"], ["V", "MD", "PhD"]]
147+
slash_idx = next(i for i, t in enumerate(out.tokens) if t.text == "/")
148+
assert slash_idx in out.dropped
149+
# "MD" continues the "V MD" entry (joined); "PhD" starts a fresh
150+
# entry across the dropped delimiter, so it does NOT get "joined"
151+
md_tok = next(t for t in out.tokens if t.text == "MD")
152+
phd_tok = next(t for t in out.tokens if t.text == "PhD")
153+
assert "joined" in md_tok.tags
154+
assert "joined" not in phd_tok.tags
155+
156+
131157
def test_suffix_comma_name_segment_gets_no_additional_count() -> None:
132158
# v1 parity: additional_parts_count applies to FAMILY_COMMA parts only;
133159
# ', PhD' must not tip the single-letter-conjunction carve-out

tests/v2/pipeline/test_segment.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,25 @@ def test_comma_only_input_is_no_comma_structure() -> None:
7878
assert out.segments == ()
7979

8080

81+
def test_single_trailing_comma_is_cosmetic_and_dropped() -> None:
82+
# v1 parity: exactly ONE trailing comma is cosmetic and stripped
83+
# (segment.py's "pop the trailing empty bucket" step), so
84+
# 'Smith, John,' segments identically to 'Smith, John' -- unlike a
85+
# genuinely empty INTERIOR bucket ('Doe,, Jr.'), which stays
86+
# structural and keeps its position.
87+
out = _segmented("Smith, John,")
88+
assert out.structure is Structure.FAMILY_COMMA
89+
assert [_texts(out, s) for s in out.segments] == [["Smith"], ["John"]]
90+
91+
92+
def test_leading_comma_yields_empty_first_segment() -> None:
93+
# A leading comma produces a non-trailing empty bucket, which is
94+
# structural (not cosmetic) and keeps its position as segment 0.
95+
out = _segmented(",John Smith")
96+
assert out.structure is Structure.FAMILY_COMMA
97+
assert [_texts(out, s) for s in out.segments] == [[], ["John", "Smith"]]
98+
99+
81100
def test_strict_comma_suffixes_veto_lenient_only_members() -> None:
82101
# lenient_comma_suffixes=False: the post-comma test drops back to
83102
# the strict predicate, so initial-shaped suffix words no longer

tests/v2/pipeline/test_tokenize.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ def test_extracted_regions_are_skipped_and_tokenized_with_role() -> None:
5252
assert starts == sorted(starts)
5353

5454

55+
def test_two_simultaneous_extracted_regions_interleave_correctly() -> None:
56+
# tokenize's masked/extracted loops each run over ALL their regions
57+
# before the final span-sort; every other test here exercises at
58+
# most one masked region at a time. With two active extraction
59+
# types (nickname parens AND a configured maiden bracket) on the
60+
# same string, main-stream and both extracted regions must still
61+
# interleave into the correct overall span order.
62+
pol = Policy(maiden_delimiters=frozenset({("[", "]")}))
63+
out = _tokenized("John (Jack) [Doe] Smith", pol)
64+
assert [(t.text, t.role) for t in out.tokens] == [
65+
("John", None),
66+
("Jack", Role.NICKNAME),
67+
("Doe", Role.MAIDEN),
68+
("Smith", None),
69+
]
70+
starts = [t.span.start for t in out.tokens]
71+
assert starts == sorted(starts)
72+
73+
5574
def test_comma_inside_extracted_region_is_not_an_offset() -> None:
5675
out = _tokenized('John "Jack, Jr" Kim')
5776
assert out.comma_offsets == ()

0 commit comments

Comments
 (0)