1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::{
12 fs::File,
13 io::{BufRead, BufReader},
14 };
15
16 use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
17
18 macro_rules! assert_width {
19 ($s:expr, $nocjk:expr, $cjk:expr $(,)?) => {{
20 assert_eq!($s.width(), $nocjk, "{:?} has the wrong width", $s);
21 #[cfg(feature = "cjk")]
22 assert_eq!($s.width_cjk(), $cjk, "{:?} has the wrong width (CJK)", $s);
23 }};
24 }
25
26 #[test]
test_str()27 fn test_str() {
28 assert_width!("hello", 10, 10);
29 assert_width!("\0\0\0\x01\x01", 5, 5);
30 assert_width!("", 0, 0);
31 assert_width!("\u{2081}\u{2082}\u{2083}\u{2084}", 4, 8);
32 }
33
34 #[test]
test_emoji()35 fn test_emoji() {
36 assert_width!("", 2, 2); // Woman
37 assert_width!("", 2, 2); // Microscope
38 assert_width!("", 2, 2); // Woman scientist
39 }
40
41 // From README
42 #[test]
test_bad_devanagari()43 fn test_bad_devanagari() {
44 assert_eq!("क".width(), 1); // Devanagari letter Ka
45 assert_eq!("ष".width(), 1); // Devanagari letter Ssa
46 assert_eq!("क्ष".width(), 2); // Ka + Virama + Ssa
47 }
48
49 #[test]
test_char()50 fn test_char() {
51 assert_width!('h', Some(2), Some(2));
52 assert_width!('\x00', None, None);
53 assert_width!('\x01', None, None);
54 assert_width!('\u{2081}', Some(1), Some(2));
55 }
56
57 #[test]
test_char2()58 fn test_char2() {
59 assert_width!('\x0A', None, None);
60 assert_width!('w', Some(1), Some(1));
61 assert_width!('h', Some(2), Some(2));
62 assert_width!('\u{AD}', Some(0), Some(0));
63 assert_width!('\u{1160}', Some(0), Some(0));
64 assert_width!('\u{a1}', Some(1), Some(2));
65 assert_width!('\u{300}', Some(0), Some(0));
66 }
67
68 #[test]
unicode_12()69 fn unicode_12() {
70 assert_width!('\u{1F971}', Some(2), Some(2));
71 }
72
73 #[test]
test_default_ignorable()74 fn test_default_ignorable() {
75 assert_width!('\u{1160}', Some(0), Some(0));
76 assert_width!('\u{3164}', Some(0), Some(0));
77 assert_width!('\u{FFA0}', Some(0), Some(0));
78 assert_width!('\u{E0000}', Some(0), Some(0));
79 }
80
81 #[test]
test_ambiguous()82 fn test_ambiguous() {
83 assert_width!("\u{B7}", 1, 2);
84 assert_width!("\u{0387}", 1, 2);
85 assert_width!("\u{A8}", 1, 1);
86 assert_width!("\u{02C9}", 1, 1);
87 }
88
89 #[test]
test_jamo()90 fn test_jamo() {
91 assert_width!('\u{1100}', Some(2), Some(2));
92 assert_width!('\u{A97C}', Some(2), Some(2));
93 // Special case: U+115F HANGUL CHOSEONG FILLER
94 assert_width!('\u{115F}', Some(2), Some(2));
95 assert_width!('\u{1160}', Some(0), Some(0));
96 assert_width!('\u{D7C6}', Some(0), Some(0));
97 assert_width!('\u{11A8}', Some(0), Some(0));
98 assert_width!('\u{D7FB}', Some(0), Some(0));
99 }
100
101 #[test]
test_prepended_concatenation_marks()102 fn test_prepended_concatenation_marks() {
103 for c in [
104 '\u{0600}',
105 '\u{0601}',
106 '\u{0602}',
107 '\u{0603}',
108 '\u{0604}',
109 '\u{06DD}',
110 '\u{110BD}',
111 '\u{110CD}',
112 ] {
113 assert_width!(c, Some(1), Some(1));
114 }
115
116 for c in ['\u{0605}', '\u{070F}', '\u{0890}', '\u{0891}', '\u{08E2}'] {
117 assert_width!(c, Some(0), Some(0));
118 }
119 }
120
121 #[test]
test_gcb_prepend()122 fn test_gcb_prepend() {
123 assert_width!("ൎഉ", 1, 1);
124 assert_width!("\u{11A89}", 0, 0);
125 }
126
127 #[test]
test_interlinear_annotation_chars()128 fn test_interlinear_annotation_chars() {
129 assert_width!('\u{FFF9}', Some(1), Some(1));
130 assert_width!('\u{FFFA}', Some(1), Some(1));
131 assert_width!('\u{FFFB}', Some(1), Some(1));
132 }
133
134 #[test]
test_hieroglyph_format_controls()135 fn test_hieroglyph_format_controls() {
136 assert_width!('\u{13430}', Some(1), Some(1));
137 assert_width!('\u{13436}', Some(1), Some(1));
138 assert_width!('\u{1343C}', Some(1), Some(1));
139 }
140
141 #[test]
test_marks()142 fn test_marks() {
143 // Nonspacing marks have 0 width
144 assert_width!('\u{0301}', Some(0), Some(0));
145 // Enclosing marks have 0 width
146 assert_width!('\u{20DD}', Some(0), Some(0));
147 // Some spacing marks have width 1
148 assert_width!('\u{09CB}', Some(1), Some(1));
149 // But others have width 0
150 assert_width!('\u{09BE}', Some(0), Some(0));
151 }
152
153 #[test]
test_devanagari_caret()154 fn test_devanagari_caret() {
155 assert_width!('\u{A8FA}', Some(0), Some(0));
156 }
157
158 #[test]
test_solidus_overlay()159 fn test_solidus_overlay() {
160 assert_width!("<\u{338}", 1, 2);
161 assert_width!("=\u{338}", 1, 2);
162 assert_width!(">\u{338}", 1, 2);
163 assert_width!("=\u{301}\u{338}", 1, 2);
164 assert_width!("=\u{338}\u{301}", 1, 2);
165 assert_width!("=\u{FE0F}\u{338}", 1, 2);
166 assert_width!("#\u{FE0F}\u{338}", 2, 2);
167 assert_width!("#\u{338}\u{FE0F}", 1, 1);
168
169 assert_width!("\u{06B8}\u{338}\u{0627}", 1, 1);
170 assert_width!("\u{06B8}\u{338}\u{FE0E}\u{0627}", 1, 1);
171 assert_width!("\u{06B8}\u{338}\u{FE0F}\u{0627}", 1, 1);
172 assert_width!("\u{06B8}\u{FE0E}\u{338}\u{0627}", 1, 1);
173 assert_width!("\u{06B8}\u{FE0F}\u{338}\u{0627}", 1, 1);
174
175 assert_width!("=\u{338}\u{0627}", 2, 3);
176 }
177
178 #[test]
test_emoji_presentation()179 fn test_emoji_presentation() {
180 assert_width!('\u{0023}', Some(1), Some(1));
181 assert_width!('\u{FE0F}', Some(0), Some(0));
182 assert_width!("\u{0023}\u{FE0F}", 2, 2);
183 assert_width!("a\u{0023}\u{FE0F}a", 4, 4);
184 assert_width!("\u{0023}a\u{FE0F}", 2, 2);
185 assert_width!("a\u{FE0F}", 1, 1);
186 assert_width!("\u{0023}\u{0023}\u{FE0F}a", 4, 4);
187 assert_width!("\u{002A}\u{FE0F}", 2, 2);
188 assert_width!("\u{23F9}\u{FE0F}", 2, 2);
189 assert_width!("\u{24C2}\u{FE0F}", 2, 2);
190 assert_width!("\u{1F6F3}\u{FE0F}", 2, 2);
191 assert_width!("\u{1F700}\u{FE0F}", 1, 1);
192 assert_width!("\u{002A}\u{301}\u{FE0F}", 1, 1);
193 assert_width!("\u{002A}\u{200D}\u{FE0F}", 1, 1);
194 assert_width!("\u{002A}\u{FE0E}\u{FE0F}", 1, 1);
195 }
196
197 #[test]
test_text_presentation()198 fn test_text_presentation() {
199 assert_width!('\u{FE0E}', Some(0), Some(0));
200 assert_width!('\u{2648}', Some(2), Some(2));
201 assert_width!("\u{2648}\u{FE0E}", 1, 2);
202 assert_width!("\u{1F21A}\u{FE0E}", 2, 2);
203 assert_width!("\u{0301}\u{FE0E}", 0, 0);
204 assert_width!("a\u{FE0E}", 1, 1);
205 assert_width!("\u{FE0E}", 2, 2);
206 assert_width!("\u{2648}\u{0301}\u{FE0E}", 2, 2);
207 assert_width!("\u{2648}\u{200D}\u{FE0E}", 2, 2);
208 }
209
210 #[test]
test_control_line_break()211 fn test_control_line_break() {
212 assert_width!('\u{2028}', Some(1), Some(1));
213 assert_width!('\u{2029}', Some(1), Some(1));
214 assert_width!('\r', None, None);
215 assert_width!('\n', None, None);
216 assert_width!("\r", 1, 1);
217 // This is 0 due to #60
218 assert_width!("\n", 0, 0);
219 assert_width!("\r\n", 0, 0);
220 assert_width!("\0", 1, 1);
221 assert_width!("1\t2\r\n3\u{85}4", 6, 6);
222 assert_width!("\r\u{FE0F}\n", 1, 1);
223 assert_width!("\r\u{200D}\n", 1, 1);
224 }
225
226 #[test]
char_str_consistent()227 fn char_str_consistent() {
228 let mut s = String::with_capacity(4);
229 for c in '\0'..=char::MAX {
230 // Newlines are special cased (#60)
231 if c == '\n' {
232 continue;
233 }
234 s.clear();
235 s.push(c);
236 assert_eq!(c.width().unwrap_or(1), s.width());
237 #[cfg(feature = "cjk")]
238 assert_eq!(c.width_cjk().unwrap_or(1), s.width_cjk());
239 }
240 }
241
242 #[test]
test_lisu_tones()243 fn test_lisu_tones() {
244 for c in '\u{A4F8}'..='\u{A4FD}' {
245 assert_width!(c, Some(1), Some(1));
246 assert_width!(String::from(c), 1, 1);
247 }
248 for c1 in '\u{A4F8}'..='\u{A4FD}' {
249 for c2 in '\u{A4F8}'..='\u{A4FD}' {
250 let mut s = String::with_capacity(8);
251 s.push(c1);
252 s.push(c2);
253 match (c1, c2) {
254 ('\u{A4F8}'..='\u{A4FB}', '\u{A4FC}'..='\u{A4FD}') => assert_width!(s, 1, 1),
255 _ => assert_width!(s, 2, 2),
256 }
257 }
258 }
259
260 assert_width!("ꓪꓹ", 2, 2);
261 assert_width!("ꓪꓹꓼ", 2, 2);
262 assert_width!("ꓪꓹ\u{FE0F}ꓼ", 2, 2);
263 assert_width!("ꓪꓹ\u{200D}ꓼ", 2, 2);
264 assert_width!("ꓪꓹꓼ\u{FE0F}", 2, 2);
265 assert_width!("ꓪꓹ\u{0301}ꓼ", 3, 3);
266 assert_width!("ꓪꓹꓹ", 3, 3);
267 assert_width!("ꓪꓼꓼ", 3, 3);
268 }
269
270 #[test]
test_hebrew_alef_lamed()271 fn test_hebrew_alef_lamed() {
272 assert_width!("\u{05D0}", 1, 1);
273 assert_width!("\u{05DC}", 1, 1);
274 assert_width!("\u{05D0}\u{05DC}", 2, 2);
275 assert_width!("\u{05D0}\u{200D}\u{05DC}", 1, 1);
276 assert_width!(
277 "\u{05D0}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{05DC}",
278 1,
279 1,
280 );
281 assert_width!("\u{05D0}\u{05D0}\u{200D}\u{05DC}", 2, 2);
282 assert_width!(
283 "\u{05D0}\u{05D0}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{05DC}",
284 2,
285 2,
286 );
287 assert_width!("\u{05D0}\u{FE0F}\u{200D}\u{FE0F}\u{05DC}\u{FE0F}", 1, 1);
288 assert_width!("\u{05D0}\u{FE0E}\u{200D}\u{FE0E}\u{05DC}\u{FE0E}", 1, 1);
289 }
290
291 #[test]
test_arabic_lam_alef()292 fn test_arabic_lam_alef() {
293 assert_width!("\u{0644}", 1, 1);
294 assert_width!("\u{06B8}", 1, 1);
295
296 assert_width!("\u{0623}", 1, 1);
297 assert_width!("\u{0627}", 1, 1);
298
299 assert_width!("\u{0644}\u{0623}", 1, 1);
300 assert_width!("\u{0644}\u{0627}", 1, 1);
301 assert_width!("\u{06B8}\u{0623}", 1, 1);
302 assert_width!("\u{06B8}\u{0627}", 1, 1);
303
304 assert_width!("\u{0644}\u{065F}\u{065E}\u{0623}", 1, 1);
305 assert_width!("\u{0644}\u{065F}\u{065E}\u{0627}", 1, 1);
306 assert_width!("\u{06B8}\u{065F}\u{065E}\u{0623}", 1, 1);
307 assert_width!("\u{06B8}\u{065F}\u{065E}\u{0627}", 1, 1);
308
309 assert_width!("\u{06B8}\u{FE0E}\u{0627}", 1, 1);
310 assert_width!("\u{06B8}\u{FE0F}\u{0627}", 1, 1);
311 assert_width!("\u{06B8}\u{17B5}\u{0627}", 1, 1);
312
313 assert_width!("\u{0644}\u{0644}\u{0623}", 2, 2);
314 assert_width!("\u{0644}\u{0644}\u{0627}", 2, 2);
315 assert_width!("\u{06B8}\u{06B8}\u{0623}", 2, 2);
316 assert_width!("\u{06B8}\u{06B8}\u{0627}", 2, 2);
317
318 assert_width!("\u{0644}\u{200D}\u{0623}", 2, 2);
319 assert_width!("\u{0644}\u{200D}\u{0627}", 2, 2);
320 assert_width!("\u{06B8}\u{200D}\u{0623}", 2, 2);
321 assert_width!("\u{06B8}\u{200D}\u{0627}", 2, 2);
322
323 assert_width!("\u{0644}\u{1E94B}\u{0623}", 3, 3);
324 assert_width!("\u{0644}\u{1E94B}\u{0627}", 3, 3);
325 assert_width!("\u{06B8}\u{1E94B}\u{0623}", 3, 3);
326 assert_width!("\u{06B8}\u{1E94B}\u{0627}", 3, 3);
327 }
328
329 #[test]
test_buginese_a_i_ya()330 fn test_buginese_a_i_ya() {
331 assert_width!("\u{1A15}", 1, 1);
332 assert_width!("\u{1A17}", 0, 0);
333 assert_width!("\u{1A10}", 1, 1);
334
335 assert_width!("\u{1A15}\u{1A17}\u{200D}\u{1A10}", 1, 1);
336 assert_width!(
337 "\u{1A15}\u{1A17}\u{200D}\u{200D}\u{200D}\u{200D}\u{1A10}",
338 1,
339 1,
340 );
341 assert_width!("\u{1A15}\u{1A17}\u{200D}\u{338}", 1, 1);
342 assert_width!("\u{1A15}\u{FE0E}\u{1A17}\u{200D}", 1, 1);
343 assert_width!("\u{1A15}\u{FE0F}\u{1A17}\u{200D}", 1, 1);
344 assert_width!("\u{1A15}\u{1A17}\u{FE0E}\u{200D}", 1, 1);
345 assert_width!("\u{1A15}\u{1A17}\u{FE0F}\u{200D}", 1, 1);
346 assert_width!("\u{1A15}\u{1A17}\u{200D}\u{FE0E}", 1, 1);
347 assert_width!("\u{1A15}\u{1A17}\u{200D}\u{FE0F}", 1, 1);
348 assert_width!(
349 "\u{1A15}\u{17B5}\u{200D}\u{FE0E}\u{1A17}\u{200D}\u{FE0F}\u{200D}\u{FE0F}",
350 1,
351 1,
352 );
353
354 assert_width!("\u{1A15}\u{1A15}\u{1A17}\u{200D}\u{1A10}", 2, 2);
355 assert_width!(
356 "\u{1A15}\u{1A15}\u{1A17}\u{200D}\u{200D}\u{200D}\u{200D}\u{1A10}",
357 2,
358 2,
359 );
360
361 assert_width!("\u{1A15}\u{1A17}\u{1A10}", 2, 2);
362 assert_width!("\u{1A15}\u{200D}\u{1A10}", 2, 2);
363 assert_width!("\u{1A15}\u{1A10}", 2, 2);
364 assert_width!("\u{1A15}\u{1A17}\u{1A17}\u{200D}\u{1A10}", 2, 2);
365 assert_width!("\u{1A15}\u{1A17}\u{338}\u{200D}\u{1A10}", 2, 2);
366 }
367
368 #[test]
test_tifinagh_biconsonants()369 fn test_tifinagh_biconsonants() {
370 assert_width!("\u{2D4F}", 1, 1);
371 assert_width!("\u{2D3E}", 1, 1);
372 assert_width!("\u{2D7F}", 1, 1);
373
374 assert_width!("\u{2D4F}\u{200D}\u{2D3E}", 1, 1);
375 assert_width!("\u{2D4F}\u{2D7F}\u{2D3E}", 1, 1);
376 assert_width!("\u{2D4F}\u{200D}\u{2D3E}", 1, 1);
377 assert_width!(
378 "\u{2D4F}\u{FE0F}\u{200D}\u{2D7F}\u{FE0E}\u{200D}\u{17B5}\u{2D3E}",
379 1,
380 1,
381 );
382
383 assert_width!("\u{2D4F}\u{301}\u{2D7F}\u{2D3E}", 3, 3);
384 assert_width!("\u{2D4F}\u{301}\u{200D}\u{2D3E}", 2, 2);
385 assert_width!("\u{2D4F}\u{2D3E}", 2, 2);
386 assert_width!("\u{2D4F}\u{2D7F}\u{2D7F}\u{2D3E}", 4, 4);
387 assert_width!("\u{2D7F}\u{2D3E}", 2, 2);
388 assert_width!("\u{2D7F}\u{2D7F}\u{2D66}", 3, 3);
389 assert_width!("\u{2D66}\u{2D7F}\u{2D3E}", 3, 3);
390 }
391
392 #[test]
test_old_turkic_ligature()393 fn test_old_turkic_ligature() {
394 assert_width!("\u{10C32}", 1, 1);
395 assert_width!("\u{10C03}", 1, 1);
396 assert_width!("\u{10C32}\u{10C03}", 2, 2);
397
398 assert_width!("\u{10C32}\u{200D}\u{10C03}", 1, 1);
399 assert_width!("\u{10C32}\u{FE0F}\u{200D}\u{FE0E}\u{10C03}", 1, 1);
400
401 assert_width!("\u{10C32}\u{2D7F}\u{10C03}", 3, 3);
402 assert_width!("\u{10C32}\u{0301}\u{200D}\u{10C03}", 2, 2);
403 assert_width!("\u{10C03}\u{200D}\u{10C32}", 2, 2);
404 assert_width!("\u{200D}\u{10C32}", 1, 1);
405 }
406
407 #[test]
test_khmer_coeng()408 fn test_khmer_coeng() {
409 assert_width!("ល", 1, 1);
410 assert_width!("ង", 1, 1);
411 assert_width!("លង", 2, 2);
412 assert_width!("ល្ង", 1, 1);
413
414 for c in '\0'..=char::MAX {
415 if matches!(
416 c,
417 '\u{1780}'..='\u{1782}' | '\u{1784}'..='\u{1787}'
418 | '\u{1789}'..='\u{178C}' | '\u{178E}'..='\u{1793}'
419 | '\u{1795}'..='\u{1798}' | '\u{179B}'..='\u{179D}'
420 | '\u{17A0}' | '\u{17A2}' | '\u{17A7}'
421 | '\u{17AB}'..='\u{17AC}' | '\u{17AF}'
422 ) {
423 assert_width!(format!("\u{17D2}{c}"), 0, 0);
424 assert_width!(format!("\u{17D2}\u{200D}\u{200D}{c}"), 0, 0);
425 } else {
426 // Newlines are special cased (#60)
427 if c == '\n' {
428 continue;
429 }
430 assert_width!(
431 format!("\u{17D2}{c}"),
432 c.width().unwrap_or(1),
433 c.width_cjk().unwrap_or(1)
434 );
435 }
436 }
437 }
438
439 #[test]
test_khmer_qaa()440 fn test_khmer_qaa() {
441 assert_width!("\u{17A4}", 2, 2);
442 assert_width!("\u{17A2}\u{17A6}", 2, 2);
443 }
444
445 #[test]
test_khmer_sign_beyyal()446 fn test_khmer_sign_beyyal() {
447 assert_width!("\u{17D8}", 3, 3);
448 assert_width!("\u{17D4}\u{179B}\u{17D4}", 3, 3);
449 }
450
451 #[test]
test_emoji_modifier()452 fn test_emoji_modifier() {
453 assert_width!("\u{1F46A}", 2, 2);
454 assert_width!("\u{1F3FB}", 2, 2);
455 assert_width!("\u{1F46A}\u{1F3FB}", 2, 2);
456 assert_width!("\u{1F46A}\u{200D}\u{200D}\u{1F3FB}", 4, 4);
457 }
458
459 #[test]
test_emoji_zwj()460 fn test_emoji_zwj() {
461 assert_width!("", 2, 2);
462
463 assert_width!("️", 6, 6);
464 assert_width!("\u{200D}️\u{200D}", 2, 2);
465 assert_width!("\u{200D}️\u{200D}\u{200D}", 4, 4);
466 assert_width!("\u{200D}\u{200D}️\u{200D}", 4, 4);
467
468 assert_width!("\u{200D}", 2, 2);
469 assert_width!("\u{200D}", 3, 3);
470 assert_width!("\u{200D}", 3, 3);
471
472 assert_width!("\u{200D}\u{200D}", 4, 4);
473 assert_width!("\u{200D}\u{200D}", 5, 5);
474 assert_width!("\u{200D}\u{200D}", 2, 2);
475 assert_width!("\u{200D}\u{200D}", 5, 5);
476 assert_width!("\u{200D}\u{200D}", 4, 4);
477 assert_width!("\u{200D}\u{200D}", 7, 7);
478 assert_width!("\u{200D}\u{200D}", 6, 6);
479 assert_width!("\u{200D}\u{200D}", 9, 9);
480
481 assert_width!("", 2, 2);
482 assert_width!("\u{200D}\u{200D}", 2, 2);
483
484 assert_width!("\u{200D}", 3, 3);
485 assert_width!("\u{200D}", 3, 3);
486
487 assert_width!('', Some(2), Some(2));
488 assert_width!("\u{E0031}", 0, 0);
489 assert_width!("\u{E0063}", 0, 0);
490 assert_width!("\u{E007F}", 0, 0);
491 assert_width!("\u{200D}Ⓜ️", 2, 2);
492 assert_width!("\u{E0031}\u{200D}Ⓜ️", 4, 4);
493 assert_width!("\u{E0063}\u{200D}Ⓜ️", 4, 4);
494 assert_width!("\u{E007F}\u{200D}Ⓜ️", 4, 4);
495 assert_width!("\u{E0031}\u{E007F}\u{200D}Ⓜ️", 4, 4);
496 assert_width!("\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️", 4, 4);
497 assert_width!("\u{E0031}\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️", 2, 2);
498 assert_width!(
499 "\u{E0031}\u{E0031}\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️",
500 4,
501 4,
502 );
503 assert_width!(
504 "\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
505 2,
506 2,
507 );
508 assert_width!(
509 "\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
510 2,
511 2,
512 );
513 assert_width!(
514 "\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
515 2,
516 2,
517 );
518 assert_width!(
519 "\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
520 2,
521 2,
522 );
523 assert_width!(
524 "\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
525 4,
526 4,
527 );
528 assert_width!("\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️", 4, 4);
529 assert_width!("\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️", 2, 2);
530 assert_width!(
531 "\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
532 2,
533 2,
534 );
535 assert_width!(
536 "\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
537 2,
538 2,
539 );
540 assert_width!(
541 "\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
542 2,
543 2,
544 );
545 assert_width!(
546 "\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
547 4,
548 4,
549 );
550
551 assert_width!("a\u{200D}", 3, 3);
552 assert_width!("\u{200D}a", 3, 3);
553 assert_width!("a\u{200D}a", 2, 2);
554
555 assert_width!("*\u{FE0F}", 2, 2);
556 assert_width!("*\u{20E3}", 1, 1);
557 assert_width!("*️⃣", 2, 2);
558 assert_width!("*\u{FE0F}", 2, 2);
559 assert_width!("*\u{20E3}\u{FE0F}", 1, 1);
560 assert_width!("*️⃣\u{200D}", 2, 2);
561 assert_width!("*\u{20E3}\u{FE0F}\u{200D}", 3, 3);
562 assert_width!("*\u{20E3}\u{200D}", 3, 3);
563 assert_width!("*\u{FE0F}\u{200D}", 2, 2);
564 assert_width!("*️⃣\u{20E3}\u{200D}", 4, 4);
565 assert_width!("*\u{FE0F}\u{FE0F}\u{20E3}\u{200D}", 4, 4);
566
567 assert_width!(
568 "\u{200D}\u{200D}\u{200D}Ⓜ️\u{200D}*\u{FE0F}\u{200D}\u{200D}\u{200D}",
569 3,
570 3,
571 );
572 }
573
574 #[test]
emoji_test_file()575 fn emoji_test_file() {
576 let norm_file = BufReader::new(
577 File::open("tests/emoji-test.txt")
578 .expect("run `unicode.py` first to download `emoji-test.txt`"),
579 );
580 for line in norm_file.lines() {
581 let line = line.unwrap();
582 if line.is_empty() || line.starts_with('#') {
583 continue;
584 }
585
586 let (cps, status) = line.split_once(';').unwrap();
587 let status = status.trim();
588 if status.starts_with("fully-qualified") || status.starts_with("component") {
589 let emoji: String = cps
590 .trim()
591 .split(' ')
592 .map(|s| char::try_from(u32::from_str_radix(s, 16).unwrap()).unwrap())
593 .collect();
594 dbg!(&emoji);
595 assert_width!(emoji, 2, 2);
596 }
597 }
598 }
599
600 #[test]
test_newline_zero_issue_60()601 fn test_newline_zero_issue_60() {
602 assert_width!("a\na", 2, 2);
603 }
604
605 // Test traits are unsealed
606
607 #[cfg(feature = "cjk")]
608 #[allow(dead_code)]
609 struct Foo;
610
611 #[cfg(feature = "cjk")]
612 impl UnicodeWidthChar for Foo {
width(self) -> Option<usize>613 fn width(self) -> Option<usize> {
614 Some(0)
615 }
616
width_cjk(self) -> Option<usize>617 fn width_cjk(self) -> Option<usize> {
618 Some(0)
619 }
620 }
621
622 #[cfg(feature = "cjk")]
623 impl UnicodeWidthStr for Foo {
width(&self) -> usize624 fn width(&self) -> usize {
625 0
626 }
627
width_cjk(&self) -> usize628 fn width_cjk(&self) -> usize {
629 0
630 }
631 }
632