• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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