1// +build !windows,!js
2
3package runewidth
4
5import (
6	"os"
7	"sort"
8	"testing"
9)
10
11var _ sort.Interface = (*table)(nil)
12
13func init() {
14	os.Setenv("RUNEWIDTH_EASTASIAN", "")
15}
16
17func (t table) Len() int {
18	return len(t)
19}
20
21func (t table) Less(i, j int) bool {
22	return t[i].first < t[j].first
23}
24
25func (t *table) Swap(i, j int) {
26	(*t)[i], (*t)[j] = (*t)[j], (*t)[i]
27}
28
29var tables = []table{
30	private,
31	nonprint,
32	combining,
33	doublewidth,
34	ambiguous,
35	emoji,
36	notassigned,
37	neutral,
38}
39
40func TestSorted(t *testing.T) {
41	for _, tbl := range tables {
42		if !sort.IsSorted(&tbl) {
43			t.Errorf("not sorted")
44		}
45	}
46}
47
48var runewidthtests = []struct {
49	in    rune
50	out   int
51	eaout int
52}{
53	{'世', 2, 2},
54	{'界', 2, 2},
55	{'セ', 1, 1},
56	{'カ', 1, 1},
57	{'イ', 1, 1},
58	{'☆', 1, 2}, // double width in ambiguous
59	{'\x00', 0, 0},
60	{'\x01', 0, 0},
61	{'\u0300', 0, 0},
62	{'\u2028', 0, 0},
63	{'\u2029', 0, 0},
64}
65
66func TestRuneWidth(t *testing.T) {
67	c := NewCondition()
68	c.EastAsianWidth = false
69	for _, tt := range runewidthtests {
70		if out := c.RuneWidth(tt.in); out != tt.out {
71			t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.out)
72		}
73	}
74	c.EastAsianWidth = true
75	for _, tt := range runewidthtests {
76		if out := c.RuneWidth(tt.in); out != tt.eaout {
77			t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.eaout)
78		}
79	}
80}
81
82var isambiguouswidthtests = []struct {
83	in  rune
84	out bool
85}{
86	{'世', false},
87	{'■', true},
88	{'界', false},
89	{'○', true},
90	{'㈱', false},
91	{'①', true},
92	{'②', true},
93	{'③', true},
94	{'④', true},
95	{'⑤', true},
96	{'⑥', true},
97	{'⑦', true},
98	{'⑧', true},
99	{'⑨', true},
100	{'⑩', true},
101	{'⑪', true},
102	{'⑫', true},
103	{'⑬', true},
104	{'⑭', true},
105	{'⑮', true},
106	{'⑯', true},
107	{'⑰', true},
108	{'⑱', true},
109	{'⑲', true},
110	{'⑳', true},
111	{'☆', true},
112}
113
114func TestIsAmbiguousWidth(t *testing.T) {
115	for _, tt := range isambiguouswidthtests {
116		if out := IsAmbiguousWidth(tt.in); out != tt.out {
117			t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out)
118		}
119	}
120}
121
122var stringwidthtests = []struct {
123	in    string
124	out   int
125	eaout int
126}{
127	{"■㈱の世界①", 10, 12},
128	{"スター☆", 7, 8},
129	{"つのだ☆HIRO", 11, 12},
130}
131
132func TestStringWidth(t *testing.T) {
133	c := NewCondition()
134	c.EastAsianWidth = false
135	for _, tt := range stringwidthtests {
136		if out := c.StringWidth(tt.in); out != tt.out {
137			t.Errorf("StringWidth(%q) = %d, want %d", tt.in, out, tt.out)
138		}
139	}
140	c.EastAsianWidth = true
141	for _, tt := range stringwidthtests {
142		if out := c.StringWidth(tt.in); out != tt.eaout {
143			t.Errorf("StringWidth(%q) = %d, want %d", tt.in, out, tt.eaout)
144		}
145	}
146}
147
148func TestStringWidthInvalid(t *testing.T) {
149	s := "こんにちわ\x00世界"
150	if out := StringWidth(s); out != 14 {
151		t.Errorf("StringWidth(%q) = %d, want %d", s, out, 14)
152	}
153}
154
155func TestTruncateSmaller(t *testing.T) {
156	s := "あいうえお"
157	expected := "あいうえお"
158
159	if out := Truncate(s, 10, "..."); out != expected {
160		t.Errorf("Truncate(%q) = %q, want %q", s, out, expected)
161	}
162}
163
164func TestTruncate(t *testing.T) {
165	s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお"
166	expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..."
167	out := Truncate(s, 80, "...")
168	if out != expected {
169		t.Errorf("Truncate(%q) = %q, want %q", s, out, expected)
170	}
171	width := StringWidth(out)
172	if width != 79 {
173		t.Errorf("width of Truncate(%q) should be %d, but %d", s, 79, width)
174	}
175}
176
177func TestTruncateFit(t *testing.T) {
178	s := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお"
179	expected := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..."
180
181	out := Truncate(s, 80, "...")
182	if out != expected {
183		t.Errorf("Truncate(%q) = %q, want %q", s, out, expected)
184	}
185	width := StringWidth(out)
186	if width != 80 {
187		t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width)
188	}
189}
190
191func TestTruncateJustFit(t *testing.T) {
192	s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお"
193	expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお"
194
195	out := Truncate(s, 80, "...")
196	if out != expected {
197		t.Errorf("Truncate(%q) = %q, want %q", s, out, expected)
198	}
199	width := StringWidth(out)
200	if width != 80 {
201		t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width)
202	}
203}
204
205func TestWrap(t *testing.T) {
206	s := `東京特許許可局局長はよく柿喰う客だ/東京特許許可局局長はよく柿喰う客だ
207123456789012345678901234567890
208
209END`
210	expected := `東京特許許可局局長はよく柿喰う
211客だ/東京特許許可局局長はよく
212柿喰う客だ
213123456789012345678901234567890
214
215END`
216
217	if out := Wrap(s, 30); out != expected {
218		t.Errorf("Wrap(%q) = %q, want %q", s, out, expected)
219	}
220}
221
222func TestTruncateNoNeeded(t *testing.T) {
223	s := "あいうえおあい"
224	expected := "あいうえおあい"
225
226	if out := Truncate(s, 80, "..."); out != expected {
227		t.Errorf("Truncate(%q) = %q, want %q", s, out, expected)
228	}
229}
230
231var isneutralwidthtests = []struct {
232	in  rune
233	out bool
234}{
235	{'→', false},
236	{'┊', false},
237	{'┈', false},
238	{'~', false},
239	{'└', false},
240	{'⣀', true},
241	{'⣀', true},
242}
243
244func TestIsNeutralWidth(t *testing.T) {
245	for _, tt := range isneutralwidthtests {
246		if out := IsNeutralWidth(tt.in); out != tt.out {
247			t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out)
248		}
249	}
250}
251
252func TestFillLeft(t *testing.T) {
253	s := "あxいうえお"
254	expected := "    あxいうえお"
255
256	if out := FillLeft(s, 15); out != expected {
257		t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected)
258	}
259}
260
261func TestFillLeftFit(t *testing.T) {
262	s := "あいうえお"
263	expected := "あいうえお"
264
265	if out := FillLeft(s, 10); out != expected {
266		t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected)
267	}
268}
269
270func TestFillRight(t *testing.T) {
271	s := "あxいうえお"
272	expected := "あxいうえお    "
273
274	if out := FillRight(s, 15); out != expected {
275		t.Errorf("FillRight(%q) = %q, want %q", s, out, expected)
276	}
277}
278
279func TestFillRightFit(t *testing.T) {
280	s := "あいうえお"
281	expected := "あいうえお"
282
283	if out := FillRight(s, 10); out != expected {
284		t.Errorf("FillRight(%q) = %q, want %q", s, out, expected)
285	}
286}
287