package runewidth import ( "sort" "testing" ) var _ sort.Interface = (*table)(nil) func (t table) Len() int { return len(t) } func (t table) Less(i, j int) bool { return t[i].first < t[j].first } func (t *table) Swap(i, j int) { (*t)[i], (*t)[j] = (*t)[j], (*t)[i] } var tables = []table{ private, nonprint, combining, doublewidth, ambiguous, emoji, notassigned, neutral, } func TestSorted(t *testing.T) { for _, tbl := range tables { if !sort.IsSorted(&tbl) { t.Errorf("not sorted") } } } var runewidthtests = []struct { in rune out int eaout int }{ {'世', 2, 2}, {'界', 2, 2}, {'セ', 1, 1}, {'カ', 1, 1}, {'イ', 1, 1}, {'☆', 1, 2}, // double width in ambiguous {'\x00', 0, 0}, {'\x01', 0, 0}, {'\u0300', 0, 0}, } func TestRuneWidth(t *testing.T) { c := NewCondition() for _, tt := range runewidthtests { if out := c.RuneWidth(tt.in); out != tt.out { t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.out) } } c.EastAsianWidth = true for _, tt := range runewidthtests { if out := c.RuneWidth(tt.in); out != tt.eaout { t.Errorf("RuneWidth(%q) = %d, want %d", tt.in, out, tt.eaout) } } } var isambiguouswidthtests = []struct { in rune out bool }{ {'世', false}, {'■', true}, {'界', false}, {'○', true}, {'㈱', false}, {'①', true}, {'②', true}, {'③', true}, {'④', true}, {'⑤', true}, {'⑥', true}, {'⑦', true}, {'⑧', true}, {'⑨', true}, {'⑩', true}, {'⑪', true}, {'⑫', true}, {'⑬', true}, {'⑭', true}, {'⑮', true}, {'⑯', true}, {'⑰', true}, {'⑱', true}, {'⑲', true}, {'⑳', true}, {'☆', true}, } func TestIsAmbiguousWidth(t *testing.T) { for _, tt := range isambiguouswidthtests { if out := IsAmbiguousWidth(tt.in); out != tt.out { t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out) } } } var stringwidthtests = []struct { in string out int eaout int }{ {"■㈱の世界①", 10, 12}, {"スター☆", 7, 8}, {"つのだ☆HIRO", 11, 12}, } func TestStringWidth(t *testing.T) { c := NewCondition() for _, tt := range stringwidthtests { if out := c.StringWidth(tt.in); out != tt.out { t.Errorf("StringWidth(%q) = %q, want %q", tt.in, out, tt.out) } } c.EastAsianWidth = true for _, tt := range stringwidthtests { if out := c.StringWidth(tt.in); out != tt.eaout { t.Errorf("StringWidth(%q) = %q, want %q", tt.in, out, tt.eaout) } } } func TestStringWidthInvalid(t *testing.T) { s := "こんにちわ\x00世界" if out := StringWidth(s); out != 14 { t.Errorf("StringWidth(%q) = %q, want %q", s, out, 14) } } func TestTruncateSmaller(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := Truncate(s, 10, "..."); out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } } func TestTruncate(t *testing.T) { s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 79 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 79, width) } } func TestTruncateFit(t *testing.T) { s := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 80 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) } } func TestTruncateJustFit(t *testing.T) { s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 80 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) } } func TestWrap(t *testing.T) { s := `東京特許許可局局長はよく柿喰う客だ/東京特許許可局局長はよく柿喰う客だ 123456789012345678901234567890 END` expected := `東京特許許可局局長はよく柿喰う 客だ/東京特許許可局局長はよく 柿喰う客だ 123456789012345678901234567890 END` if out := Wrap(s, 30); out != expected { t.Errorf("Wrap(%q) = %q, want %q", s, out, expected) } } func TestTruncateNoNeeded(t *testing.T) { s := "あいうえおあい" expected := "あいうえおあい" if out := Truncate(s, 80, "..."); out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } } var isneutralwidthtests = []struct { in rune out bool }{ {'→', false}, {'┊', false}, {'┈', false}, {'~', false}, {'└', false}, {'⣀', true}, {'⣀', true}, } func TestIsNeutralWidth(t *testing.T) { for _, tt := range isneutralwidthtests { if out := IsNeutralWidth(tt.in); out != tt.out { t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out) } } } func TestFillLeft(t *testing.T) { s := "あxいうえお" expected := " あxいうえお" if out := FillLeft(s, 15); out != expected { t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) } } func TestFillLeftFit(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := FillLeft(s, 10); out != expected { t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) } } func TestFillRight(t *testing.T) { s := "あxいうえお" expected := "あxいうえお " if out := FillRight(s, 15); out != expected { t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) } } func TestFillRightFit(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := FillRight(s, 10); out != expected { t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) } }