whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,426 @@
|
||||
// +build !js,!appengine
|
||||
|
||||
package runewidth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var _ sort.Interface = (*table)(nil) // ensure that type "table" does implement sort.Interface
|
||||
|
||||
func init() {
|
||||
os.Setenv("RUNEWIDTH_EASTASIAN", "")
|
||||
handleEnv()
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
type tableInfo struct {
|
||||
tbl table
|
||||
name string
|
||||
wantN int
|
||||
wantSHA string
|
||||
}
|
||||
|
||||
var tables = []tableInfo{
|
||||
{private, "private", 137468, "a4a641206dc8c5de80bd9f03515a54a706a5a4904c7684dc6a33d65c967a51b2"},
|
||||
{nonprint, "nonprint", 2143, "288904683eb225e7c4c0bd3ee481b53e8dace404ec31d443afdbc4d13729fe95"},
|
||||
{combining, "combining", 465, "3cce13deb5e23f9f7327f2b1ef162328285a7dcf277a98302a8f7cdd43971268"},
|
||||
{doublewidth, "doublewidth", 182440, "3d16eda8650dc2c92d6318d32f0b4a74fda5a278db2d4544b1dd65863394823c"},
|
||||
{ambiguous, "ambiguous", 138739, "d05e339a10f296de6547ff3d6c5aee32f627f6555477afebd4a3b7e3cf74c9e3"},
|
||||
{emoji, "emoji", 3535, "9ec17351601d49c535658de8d129c1d0ccda2e620669fc39a2faaee7dedcef6d"},
|
||||
{narrow, "narrow", 111, "fa897699c5e3cd9141c638d539331b0bdd508b874e22996c5e929767d455fc5a"},
|
||||
{neutral, "neutral", 27333, "5455f5e75c307f70b4e9b2384dc5a8bcd91a4c5e2b24b2b185dfad4d860ee5c2"},
|
||||
}
|
||||
|
||||
func TestTableChecksums(t *testing.T) {
|
||||
for _, ti := range tables {
|
||||
gotN := 0
|
||||
buf := make([]byte, utf8.MaxRune+1)
|
||||
for r := rune(0); r <= utf8.MaxRune; r++ {
|
||||
if inTable(r, ti.tbl) {
|
||||
gotN++
|
||||
buf[r] = 1
|
||||
}
|
||||
}
|
||||
gotSHA := fmt.Sprintf("%x", sha256.Sum256(buf))
|
||||
if gotN != ti.wantN || gotSHA != ti.wantSHA {
|
||||
t.Errorf("table = %s,\n\tn = %d want %d,\n\tsha256 = %s want %s", ti.name, gotN, ti.wantN, gotSHA, ti.wantSHA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuneWidthChecksums(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
eastAsianWidth bool
|
||||
wantSHA string
|
||||
}{
|
||||
{"ea-no", false, "4eb632b105d3b2c800dda9141381d0b8a95250a3a5c7f1a5ca2c4d4daaa85234"},
|
||||
{"ea-yes", true, "c2ddc3bdf42d81d4c23050e21eda46eb639b38b15322d35e8eb6c26f3b83ce92"},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
c := NewCondition()
|
||||
c.EastAsianWidth = testcase.eastAsianWidth
|
||||
buf := make([]byte, utf8.MaxRune+1)
|
||||
for r := rune(0); r <= utf8.MaxRune; r++ {
|
||||
buf[r] = byte(c.RuneWidth(r))
|
||||
}
|
||||
gotSHA := fmt.Sprintf("%x", sha256.Sum256(buf))
|
||||
if gotSHA != testcase.wantSHA {
|
||||
t.Errorf("TestRuneWidthChecksums = %s,\n\tsha256 = %s want %s",
|
||||
testcase.name, gotSHA, testcase.wantSHA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkInterval(first, last rune) bool {
|
||||
return first >= 0 && first <= utf8.MaxRune &&
|
||||
last >= 0 && last <= utf8.MaxRune &&
|
||||
first <= last
|
||||
}
|
||||
|
||||
func isCompact(t *testing.T, ti *tableInfo) bool {
|
||||
tbl := ti.tbl
|
||||
for i := range tbl {
|
||||
e := tbl[i]
|
||||
if !checkInterval(e.first, e.last) { // sanity check
|
||||
t.Errorf("table invalid: table = %s index = %d %v", ti.name, i, e)
|
||||
return false
|
||||
}
|
||||
if i+1 < len(tbl) && e.last+1 >= tbl[i+1].first { // can be combined into one entry
|
||||
t.Errorf("table not compact: table = %s index = %d %v %v", ti.name, i, e, tbl[i+1])
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestSorted(t *testing.T) {
|
||||
for _, ti := range tables {
|
||||
if !sort.IsSorted(&ti.tbl) {
|
||||
t.Errorf("table not sorted: %s", ti.name)
|
||||
}
|
||||
if !isCompact(t, &ti) {
|
||||
t.Errorf("table not compact: %s", ti.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var runewidthtests = []struct {
|
||||
in rune
|
||||
out int
|
||||
eaout int
|
||||
nseout int
|
||||
}{
|
||||
{'世', 2, 2, 2},
|
||||
{'界', 2, 2, 2},
|
||||
{'セ', 1, 1, 1},
|
||||
{'カ', 1, 1, 1},
|
||||
{'イ', 1, 1, 1},
|
||||
{'☆', 1, 2, 2}, // double width in ambiguous
|
||||
{'☺', 1, 1, 2},
|
||||
{'☻', 1, 1, 2},
|
||||
{'♥', 1, 2, 2},
|
||||
{'♦', 1, 1, 2},
|
||||
{'♣', 1, 2, 2},
|
||||
{'♠', 1, 2, 2},
|
||||
{'♂', 1, 2, 2},
|
||||
{'♀', 1, 2, 2},
|
||||
{'♪', 1, 2, 2},
|
||||
{'♫', 1, 1, 2},
|
||||
{'☼', 1, 1, 2},
|
||||
{'↕', 1, 2, 2},
|
||||
{'‼', 1, 1, 2},
|
||||
{'↔', 1, 2, 2},
|
||||
{'\x00', 0, 0, 0},
|
||||
{'\x01', 0, 0, 0},
|
||||
{'\u0300', 0, 0, 0},
|
||||
{'\u2028', 0, 0, 0},
|
||||
{'\u2029', 0, 0, 0},
|
||||
{'a', 1, 1, 1}, // ASCII classified as "na" (narrow)
|
||||
{'⟦', 1, 1, 1}, // non-ASCII classified as "na" (narrow)
|
||||
{'👁', 1, 1, 2},
|
||||
}
|
||||
|
||||
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 (EastAsianWidth=false)", 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 (EastAsianWidth=true)", tt.in, out, tt.eaout)
|
||||
}
|
||||
}
|
||||
c.StrictEmojiNeutral = false
|
||||
for _, tt := range runewidthtests {
|
||||
if out := c.RuneWidth(tt.in); out != tt.nseout {
|
||||
t.Errorf("RuneWidth(%q) = %d, want %d (StrictEmojiNeutral=false)", 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 (EA)", 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
old := os.Getenv("RUNEWIDTH_EASTASIAN")
|
||||
defer os.Setenv("RUNEWIDTH_EASTASIAN", old)
|
||||
|
||||
os.Setenv("RUNEWIDTH_EASTASIAN", "0")
|
||||
handleEnv()
|
||||
|
||||
if w := RuneWidth('│'); w != 1 {
|
||||
t.Errorf("RuneWidth('│') = %d, want %d", w, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroWidthJoiner(t *testing.T) {
|
||||
c := NewCondition()
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
want int
|
||||
}{
|
||||
{"👩", 2},
|
||||
{"👩", 2},
|
||||
{"👩🍳", 2},
|
||||
{"🍳", 2},
|
||||
{"👨👨", 2},
|
||||
{"👨👨👧", 2},
|
||||
{"🏳️🌈", 1},
|
||||
{"あ👩🍳い", 6},
|
||||
{"あ🍳い", 6},
|
||||
{"あい", 4},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := c.StringWidth(tt.in); got != tt.want {
|
||||
t.Errorf("StringWidth(%q) = %d, want %d", tt.in, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user