1package text
2
3import (
4 "strings"
5 "testing"
6)
7
8
9func TestWrap(t *testing.T) {
10 cases := []struct {
11 Input, Output string
12 Lim int
13 }{
14 // A simple word passes through.
15 {
16 "foo",
17 "foo",
18 4,
19 },
20 // Word breaking
21 {
22 "foobarbaz",
23 "foob\narba\nz",
24 4,
25 },
26 // Lines are broken at whitespace.
27 {
28 "foo bar baz",
29 "foo\nbar\nbaz",
30 4,
31 },
32 // Word breaking
33 {
34 "foo bars bazzes",
35 "foo\nbars\nbazz\nes",
36 4,
37 },
38 // A word that would run beyond the width is wrapped.
39 {
40 "fo sop",
41 "fo\nsop",
42 4,
43 },
44 // A tab counts as 4 characters.
45 {
46 "foo\nb\t r\n baz",
47 "foo\nb\nr\n baz",
48 4,
49 },
50 // Trailing whitespace is removed after used for wrapping.
51 // Runs of whitespace on which a line is broken are removed.
52 {
53 "foo \nb ar ",
54 "foo\n\nb\nar\n",
55 4,
56 },
57 // An explicit line break at the end of the input is preserved.
58 {
59 "foo bar baz\n",
60 "foo\nbar\nbaz\n",
61 4,
62 },
63 // Explicit break are always preserved.
64 {
65 "\nfoo bar\n\n\nbaz\n",
66 "\nfoo\nbar\n\n\nbaz\n",
67 4,
68 },
69 // Ignore complete words with terminal color sequence
70 {
71 "foo \x1b[31mbar\x1b[0m baz",
72 "foo\n\x1b[31mbar\x1b[0m\nbaz",
73 4,
74 },
75 // Handle words with colors sequence inside the word
76 {
77 "foo b\x1b[31mbar\x1b[0mr baz",
78 "foo\nb\x1b[31mbar\n\x1b[0mr\nbaz",
79 4,
80 },
81 // Break words with colors sequence inside the word
82 {
83 "foo bb\x1b[31mbar\x1b[0mr baz",
84 "foo\nbb\x1b[31mba\nr\x1b[0mr\nbaz",
85 4,
86 },
87 // Complete example:
88 {
89 " This is a list: \n\n\t* foo\n\t* bar\n\n\n\t* baz \nBAM ",
90 " This\nis a\nlist:\n\n *\nfoo\n *\nbar\n\n\n *\nbaz\nBAM\n",
91 6,
92 },
93 // Handle chinese (wide characters)
94 {
95 "一只敏捷的狐狸跳过了一只懒狗。",
96 "一只敏捷的狐\n狸跳过了一只\n懒狗。",
97 12,
98 },
99 // Handle chinese with colors
100 {
101 "一只敏捷的\x1b[31m狐狸跳过\x1b[0m了一只懒狗。",
102 "一只敏捷的\x1b[31m狐\n狸跳过\x1b[0m了一只\n懒狗。",
103 12,
104 },
105 }
106
107 for i, tc := range cases {
108 actual, lines := Wrap(tc.Input, tc.Lim)
109 if actual != tc.Output {
110 t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s`\n\nActual Output:\n\n`%s`",
111 i, tc.Input, tc.Output, actual)
112 }
113
114 expected := len(strings.Split(tc.Output, "\n"))
115 if expected != lines {
116 t.Fatalf("Case %d Nb lines mismatch\nExpected:%d\nActual:%d",
117 i, expected, lines)
118 }
119 }
120}
121
122func TestWrapLeftPadded(t *testing.T) {
123 cases := []struct {
124 input, output string
125 lim, pad int
126 }{
127 {
128 "The Lorem ipsum text is typically composed of pseudo-Latin words. It is commonly used as placeholder text to examine or demonstrate the visual effects of various graphic design.",
129 ` The Lorem ipsum text is typically composed of
130 pseudo-Latin words. It is commonly used as placeholder
131 text to examine or demonstrate the visual effects of
132 various graphic design.`,
133 59, 4,
134 },
135 // Handle Chinese
136 {
137 "婞一枳郲逴靲屮蜧曀殳,掫乇峔掮傎溒兀緉冘仜。郼牪艽螗媷錵朸一詅掜豗怙刉笀丌,楀棶乇矹迡搦囷圣亍昄漚粁仈祂。覂一洳袶揙楱亍滻瘯毌,掗屮柅軡菵腩乜榵毌夯。勼哻怌婇怤灟葠雺奷朾恦扰衪岨坋誁乇芚誙腞。冇笉妺悆浂鱦賌廌灱灱觓坋佫呬耴跣兀枔蓔輈。嵅咍犴膰痭瘰机一靬涽捊矷尒玶乇,煚塈丌岰陊鉖怞戉兀甿跾觓夬侄。棩岧汌橩僁螗玎一逭舴圂衪扐衲兀,嵲媕亍衩衿溽昃夯丌侄蒰扂丱呤。毰侘妅錣廇螉仴一暀淖蚗佶庂咺丌,輀鈁乇彽洢溦洰氶乇构碨洐巿阹。",
138 ` 婞一枳郲逴靲屮蜧曀殳,掫乇峔掮傎溒兀緉冘仜。郼牪艽螗媷
139 錵朸一詅掜豗怙刉笀丌,楀棶乇矹迡搦囷圣亍昄漚粁仈祂。覂
140 一洳袶揙楱亍滻瘯毌,掗屮柅軡菵腩乜榵毌夯。勼哻怌婇怤灟
141 葠雺奷朾恦扰衪岨坋誁乇芚誙腞。冇笉妺悆浂鱦賌廌灱灱觓坋
142 佫呬耴跣兀枔蓔輈。嵅咍犴膰痭瘰机一靬涽捊矷尒玶乇,煚塈
143 丌岰陊鉖怞戉兀甿跾觓夬侄。棩岧汌橩僁螗玎一逭舴圂衪扐衲
144 兀,嵲媕亍衩衿溽昃夯丌侄蒰扂丱呤。毰侘妅錣廇螉仴一暀淖
145 蚗佶庂咺丌,輀鈁乇彽洢溦洰氶乇构碨洐巿阹。`,
146 59, 4,
147 },
148 }
149
150 for i, tc := range cases {
151 actual, lines := WrapLeftPadded(tc.input, tc.lim, tc.pad)
152 if actual != tc.output {
153 t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n`\n%s`\n\nActual Output:\n`\n%s\n%s`",
154 i, tc.input, tc.output,
155 "|"+strings.Repeat("-", tc.lim-2)+"|",
156 actual)
157 }
158
159 expected := len(strings.Split(tc.output, "\n"))
160 if expected != lines {
161 t.Fatalf("Case %d Nb lines mismatch\nExpected:%d\nActual:%d",
162 i, expected, lines)
163 }
164 }
165}
166
167func TestWordLen(t *testing.T) {
168 cases := []struct {
169 Input string
170 Length int
171 }{
172 // A simple word
173 {
174 "foo",
175 3,
176 },
177 // A simple word with colors
178 {
179 "\x1b[31mbar\x1b[0m",
180 3,
181 },
182 // Handle prefix and suffix properly
183 {
184 "foo\x1b[31mfoobarHoy\x1b[0mbaaar",
185 17,
186 },
187 // Handle chinese
188 {
189 "快檢什麼望對",
190 12,
191 },
192 // Handle chinese with colors
193 {
194 "快\x1b[31m檢什麼\x1b[0m望對",
195 12,
196 },
197 }
198
199 for i, tc := range cases {
200 l := wordLen(tc.Input)
201 if l != tc.Length {
202 t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%d`\n\nActual Output:\n\n`%d`",
203 i, tc.Input, tc.Length, l)
204 }
205 }
206}
207
208func TestSplitWord(t *testing.T) {
209 cases := []struct {
210 Input string
211 Length int
212 Result, Leftover string
213 }{
214 // A simple word passes through.
215 {
216 "foo",
217 4,
218 "foo", "",
219 },
220 // Cut at the right place
221 {
222 "foobarHoy",
223 4,
224 "foob", "arHoy",
225 },
226 // A simple word passes through with colors
227 {
228 "\x1b[31mbar\x1b[0m",
229 4,
230 "\x1b[31mbar\x1b[0m", "",
231 },
232 // Cut at the right place with colors
233 {
234 "\x1b[31mfoobarHoy\x1b[0m",
235 4,
236 "\x1b[31mfoob", "arHoy\x1b[0m",
237 },
238 // Handle prefix and suffix properly
239 {
240 "foo\x1b[31mfoobarHoy\x1b[0mbaaar",
241 4,
242 "foo\x1b[31mf", "oobarHoy\x1b[0mbaaar",
243 },
244 // Cut properly with length = 0
245 {
246 "foo",
247 0,
248 "", "foo",
249 },
250 // Handle chinese
251 {
252 "快檢什麼望對",
253 4,
254 "快檢", "什麼望對",
255 },
256 {
257 "快檢什麼望對",
258 5,
259 "快檢", "什麼望對",
260 },
261 // Handle chinese with colors
262 {
263 "快\x1b[31m檢什麼\x1b[0m望對",
264 4,
265 "快\x1b[31m檢", "什麼\x1b[0m望對",
266 },
267 }
268
269 for i, tc := range cases {
270 result, leftover := splitWord(tc.Input, tc.Length)
271 if result != tc.Result || leftover != tc.Leftover {
272 t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s` - `%s`\n\nActual Output:\n\n`%s` - `%s`",
273 i, tc.Input, tc.Result, tc.Leftover, result, leftover)
274 }
275 }
276}