Skip to content

Commit 7f8f0ef

Browse files
Add tests/test_unicode.py
1 parent 1c4870b commit 7f8f0ef

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

tests/test_unicode.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Wide-character / unicode width tests."""
2+
3+
from __future__ import annotations
4+
5+
import pytest
6+
7+
import csvtable
8+
from csvtable._width import char_width, pad, text_width, truncate
9+
10+
11+
def test_text_width_ascii_letters() -> None:
12+
assert text_width("hello") == 5
13+
14+
15+
def test_text_width_wide_cjk() -> None:
16+
# Each CJK character is 2 columns wide.
17+
assert text_width("中国") == 4
18+
19+
20+
def test_text_width_combining_characters() -> None:
21+
# 'a' + combining acute (U+0301)
22+
assert text_width("á") == 1
23+
24+
25+
def test_text_width_empty_string() -> None:
26+
assert text_width("") == 0
27+
28+
29+
def test_char_width_control_chars() -> None:
30+
assert char_width("\x07") == 0
31+
assert char_width("\x00") == 0
32+
assert char_width("\x7f") == 0
33+
34+
35+
def test_char_width_empty_returns_zero() -> None:
36+
assert char_width("") == 0
37+
38+
39+
def test_pad_left_default() -> None:
40+
assert pad("ab", 5) == "ab "
41+
42+
43+
def test_pad_right() -> None:
44+
assert pad("ab", 5, "right") == " ab"
45+
46+
47+
def test_pad_center_balances_extra_on_right() -> None:
48+
assert pad("ab", 5, "center") == " ab "
49+
50+
51+
def test_pad_handles_wide_chars() -> None:
52+
# "中" is width 2; pad to width 4 leaves 2 columns.
53+
assert pad("中", 4) == "中 "
54+
55+
56+
def test_pad_invalid_align_raises() -> None:
57+
with pytest.raises(ValueError):
58+
pad("a", 3, "diagonal")
59+
60+
61+
def test_pad_overflow_raises() -> None:
62+
with pytest.raises(ValueError):
63+
pad("hello", 3)
64+
65+
66+
def test_pad_invalid_fill_raises() -> None:
67+
with pytest.raises(ValueError):
68+
pad("a", 3, fill="ab")
69+
70+
71+
def test_truncate_no_change_when_within_width() -> None:
72+
assert truncate("abc", 10) == "abc"
73+
74+
75+
def test_truncate_zero_width_returns_empty() -> None:
76+
assert truncate("abc", 0) == ""
77+
78+
79+
def test_truncate_negative_width_raises() -> None:
80+
with pytest.raises(ValueError):
81+
truncate("abc", -1)
82+
83+
84+
def test_truncate_invalid_side_raises() -> None:
85+
with pytest.raises(ValueError):
86+
truncate("abcdef", 3, side="diagonal")
87+
88+
89+
def test_truncate_handles_wide_chars() -> None:
90+
# 中国语 is width 6; truncate to width 5 with ellipsis (1 wide).
91+
out = truncate("中国语", 5)
92+
# Keep first wide char (2) + ellipsis (1) = 3 width; we still have
93+
# room for one more wide char (5 - 1 = 4 / 2 = 2 wide chars).
94+
assert text_width(out) <= 5
95+
assert out.endswith("…")
96+
97+
98+
def test_render_with_wide_chars_widths_correct() -> None:
99+
out = csvtable.from_rows(
100+
[["中国", "x"]], headers=["国家", "v"]
101+
)
102+
# "中国" and "国家" are both width 4; column should be width 4.
103+
assert "| 中国 |" in out
104+
assert "| 国家 |" in out
105+
106+
107+
def test_render_combining_marks_dont_inflate_width() -> None:
108+
out = csvtable.from_rows(
109+
[["café", "1"]], headers=["a", "b"]
110+
)
111+
# If width was wrong the headers wouldn't align under the data.
112+
lines = out.split("\n")
113+
assert len(set(len(line) for line in lines)) == 1

0 commit comments

Comments
 (0)