280 lines
6.3 KiB
Go
280 lines
6.3 KiB
Go
|
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},
|
|||
|
{'\u2028', 0, 0},
|
|||
|
{'\u2029', 0, 0},
|
|||
|
}
|
|||
|
|
|||
|
func TestRuneWidth(t *testing.T) {
|
|||
|
c := NewCondition()
|
|||
|
c.EastAsianWidth = false
|
|||
|
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()
|
|||
|
c.EastAsianWidth = false
|
|||
|
for _, tt := range stringwidthtests {
|
|||
|
if out := c.StringWidth(tt.in); out != tt.out {
|
|||
|
t.Errorf("StringWidth(%q) = %d, want %d", 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) = %d, want %d", tt.in, out, tt.eaout)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func TestStringWidthInvalid(t *testing.T) {
|
|||
|
s := "こんにちわ\x00世界"
|
|||
|
if out := StringWidth(s); out != 14 {
|
|||
|
t.Errorf("StringWidth(%q) = %d, want %d", 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)
|
|||
|
}
|
|||
|
}
|