github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xstring/xstring_test.go (about)

     1  package xstring
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/Aoi-hosizora/ahlib/xtesting"
     6  	"net/url"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  	"unicode"
    11  )
    12  
    13  func TestCapitalize(t *testing.T) {
    14  	for _, tc := range []struct {
    15  		give string
    16  		want string
    17  	}{
    18  		{"", ""},
    19  		{" ", " "},
    20  		{"abc", "Abc"},
    21  		{"Abc", "Abc"},
    22  		{"abc def", "Abc def"},
    23  		{"测试", "测试"},
    24  		{"テス", "テス"},
    25  		{"тест", "Тест"},
    26  	} {
    27  		xtesting.Equal(t, Capitalize(tc.give), tc.want)
    28  	}
    29  }
    30  
    31  func TestUncapitalize(t *testing.T) {
    32  	for _, tc := range []struct {
    33  		give string
    34  		want string
    35  	}{
    36  		{"", ""},
    37  		{" ", " "},
    38  		{"Abc", "abc"},
    39  		{"abc", "abc"},
    40  		{"Abc Def", "abc Def"},
    41  		{"测试", "测试"},
    42  		{"テス", "テス"},
    43  		{"Тест", "тест"},
    44  	} {
    45  		xtesting.Equal(t, Uncapitalize(tc.give), tc.want)
    46  	}
    47  }
    48  
    49  func TestCapitalizeAll(t *testing.T) {
    50  	for _, tc := range []struct {
    51  		give string
    52  		want string
    53  	}{
    54  		{"", ""},
    55  		{" ", " "},
    56  		{"abc", "Abc"},
    57  		{"abc def", "Abc Def"},
    58  		{"abc\tDef ", "Abc\tDef "},
    59  		{" abc\ndef\r\n ghi\v", " Abc\nDef\r\n Ghi\v"},
    60  		{"测试 测试", "测试 测试"},
    61  		{"テス テス", "テス テス"},
    62  		{"тест тест", "Тест Тест"},
    63  	} {
    64  		xtesting.Equal(t, CapitalizeAll(tc.give), tc.want)
    65  	}
    66  }
    67  
    68  func TestUncapitalizeAll(t *testing.T) {
    69  	for _, tc := range []struct {
    70  		give string
    71  		want string
    72  	}{
    73  		{"", ""},
    74  		{" ", " "},
    75  		{"Abc", "abc"},
    76  		{"Abc Def", "abc def"},
    77  		{"abc\tDef ", "abc\tdef "},
    78  		{" abc\nDef\r\n Ghi\v", " abc\ndef\r\n ghi\v"},
    79  		{"测试 测试", "测试 测试"},
    80  		{"テス テス", "テス テス"},
    81  		{"Тест Тест", "тест тест"},
    82  	} {
    83  		xtesting.Equal(t, UncapitalizeAll(tc.give), tc.want)
    84  	}
    85  }
    86  
    87  func TestIsArabicNumber(t *testing.T) {
    88  	xtesting.True(t, unicode.IsNumber('0'), true)
    89  	xtesting.True(t, unicode.IsNumber('5'), true)
    90  	for _, tc := range []struct {
    91  		give rune
    92  		want bool
    93  	}{
    94  		{' ', false},
    95  		{'0', true},
    96  		{'1', true},
    97  		{'2', true},
    98  		{'3', true},
    99  		{'4', true},
   100  		{'5', true},
   101  		{'6', true},
   102  		{'7', true},
   103  		{'8', true},
   104  		{'9', true},
   105  		{'0', false}, // <- true for unicode.IsNumber
   106  		{'1', false},
   107  		{'2', false},
   108  		{'3', false},
   109  		{'4', false},
   110  		{'5', false},
   111  		{'6', false},
   112  		{'7', false},
   113  		{'8', false},
   114  		{'9', false},
   115  	} {
   116  		t.Run(string(tc.give), func(t *testing.T) {
   117  			xtesting.Equal(t, IsArabicNumber(tc.give), tc.want)
   118  		})
   119  	}
   120  }
   121  
   122  func TestIsBlank(t *testing.T) {
   123  	for _, tc := range []struct {
   124  		give rune
   125  		want bool
   126  	}{
   127  		{' ', true},
   128  		{'\t', true},
   129  		{'\n', true},
   130  		{'\r', true},
   131  		{'\v', true},
   132  		{'\f', true},
   133  		{' ', true},
   134  		{'\x85', true},
   135  		{'\xA0', true},
   136  		{0x2000, true},
   137  		{0x2001, true},
   138  		{0x2002, true},
   139  		{0x2003, true},
   140  		{0x2004, true},
   141  		{0x2005, true},
   142  		{0x2006, true},
   143  		{0x2007, true},
   144  		{0x2008, true},
   145  		{0x2009, true},
   146  		{0x200A, true},
   147  		{0x200B, false},
   148  		{0x200F, false},
   149  		{0x2028, true},
   150  		{0x2029, true},
   151  		{0x202F, true},
   152  		{0x205F, true},
   153  		{'a', false},
   154  		{'0', false},
   155  		{'测', false},
   156  	} {
   157  		xtesting.Equal(t, IsBlank(tc.give), tc.want)
   158  	}
   159  }
   160  
   161  func TestTrimBlanks(t *testing.T) {
   162  	for _, tc := range []struct {
   163  		give string
   164  		want string
   165  	}{
   166  		{"", ""},
   167  		{" ", ""},
   168  		{"\t", ""},
   169  		{"\n|\r", "|"},
   170  		{"\t\t\r\r\n\n", ""},
   171  		{" a b\tc\r\nd\v\f", "a b\tc\r\nd"},
   172  	} {
   173  		t.Run(tc.give, func(t *testing.T) {
   174  			xtesting.Equal(t, TrimBlanks(tc.give), tc.want)
   175  		})
   176  	}
   177  }
   178  
   179  func TestReplaceAndRemoveExtraBlanks(t *testing.T) {
   180  	for _, tc := range []struct {
   181  		give  string
   182  		want1 string
   183  		want2 string
   184  	}{
   185  		{"", "", ""},
   186  		{" ", "", ""},
   187  		{" \n ", "", ""},
   188  		{"\t\v ", "", ""},
   189  		{"a b", "a|b", "a b"},
   190  		{"a  b", "a|b", "a b"},
   191  		{" a b\tc\r\nd", "a|b|c|d", "a b c d"},
   192  		{"   ab cd  ef\n\tgh\n", "ab|cd|ef|gh", "ab cd ef gh"},
   193  		{"\t\t\r\r\n\n", "", ""},
   194  	} {
   195  		t.Run(tc.give, func(t *testing.T) {
   196  			xtesting.Equal(t, ReplaceExtraBlanks(tc.give, "|"), tc.want1)
   197  			xtesting.Equal(t, RemoveExtraBlanks(tc.give), tc.want2)
   198  		})
   199  	}
   200  }
   201  
   202  func TestIsBreakLine(t *testing.T) {
   203  	for _, tc := range []struct {
   204  		give rune
   205  		want bool
   206  	}{
   207  		{' ', false},
   208  		{'\t', false},
   209  		{'\n', true},
   210  		{'\r', true},
   211  		{'\v', false},
   212  		{'\f', false},
   213  		{'a', false},
   214  		{'0', false},
   215  		{'测', false},
   216  	} {
   217  		xtesting.Equal(t, IsBreakLine(tc.give), tc.want)
   218  	}
   219  }
   220  
   221  func TestTrimBreakLines(t *testing.T) {
   222  	for _, tc := range []struct {
   223  		give string
   224  		want string
   225  	}{
   226  		{"", ""},
   227  		{" ", " "},
   228  		{"\n", ""},
   229  		{"\n|\r", "|"},
   230  		{"\n\r\t\r\t\n", "\t\r\t"},
   231  		{" a b\tc\r\nd\r\n", " a b\tc\r\nd"},
   232  	} {
   233  		t.Run(tc.give, func(t *testing.T) {
   234  			xtesting.Equal(t, TrimBreakLines(tc.give), tc.want)
   235  		})
   236  	}
   237  }
   238  
   239  func TestReplaceAndRemoveExtraBreakLines(t *testing.T) {
   240  	for _, tc := range []struct {
   241  		give  string
   242  		want1 string
   243  		want2 string
   244  	}{
   245  		{"", "", ""},
   246  		{" ", " ", " "},
   247  		{" \n\n ", " | ", " \n "},
   248  		{"\t\r\n", "\t", "\t"},
   249  		{"a\nb", "a|b", "a\nb"},
   250  		{"a\n\rb", "a|b", "a\nb"},
   251  		{" a\rb\tc\n\nd", " a|b\tc|d", " a\nb\tc\nd"},
   252  		{"  \n ab\t cd  ef\n\rgh\n", "  | ab\t cd  ef|gh", "  \n ab\t cd  ef\ngh"},
   253  		{"\n\r\t\r\t\n", "\t|\t", "\t\n\t"},
   254  	} {
   255  		t.Run(tc.give, func(t *testing.T) {
   256  			xtesting.Equal(t, ReplaceExtraBreakLines(tc.give, "|"), tc.want1)
   257  			xtesting.Equal(t, RemoveExtraBreakLines(tc.give), tc.want2)
   258  		})
   259  	}
   260  }
   261  
   262  func TestSplitToWords(t *testing.T) {
   263  	for _, tc := range []struct {
   264  		giveString string
   265  		giveSeps   []string
   266  		want       []string
   267  	}{
   268  		{"", []string{}, []string{}},
   269  		{"a", []string{}, []string{"a"}},
   270  		{"ABC", []string{}, []string{"ABC"}},
   271  
   272  		{"a", []string{"a"}, []string{}},
   273  		{"abc", []string{"a"}, []string{"bc"}},
   274  		{"abc", []string{"b"}, []string{"a", "c"}},
   275  		{"abc", []string{"c"}, []string{"ab"}},
   276  		{"abc", []string{"a", "b"}, []string{"c"}},
   277  		{"abc", []string{"a", "b", "c"}, []string{}},
   278  		{"abc#d", []string{"c", "#"}, []string{"ab", "d"}},
   279  
   280  		{" ", []string{}, []string{}},
   281  		{"a b", []string{}, []string{"a", "b"}},
   282  		{" a  b   c    ", []string{}, []string{"a", "b", "c"}},
   283  		{" a  b   c    ", []string{"="}, []string{"a", "b", "c"}},
   284  
   285  		{"Abc", []string{}, []string{"Abc"}},
   286  		{"aBc", []string{}, []string{"a", "Bc"}},
   287  		{"abC", []string{}, []string{"ab", "C"}},
   288  		{"aBC", []string{}, []string{"a", "BC"}},
   289  		{"Abc", []string{"="}, []string{"Abc"}},
   290  		{"aBc", []string{"="}, []string{"aBc"}},
   291  		{"abC", []string{"="}, []string{"abC"}},
   292  		{"aBC", []string{"="}, []string{"aBC"}},
   293  		{"Abc", []string{"=", CaseSplitter}, []string{"Abc"}},
   294  		{"aBc", []string{"=", CaseSplitter}, []string{"a", "Bc"}},
   295  		{"abC", []string{"=", CaseSplitter}, []string{"ab", "C"}},
   296  		{"aBC", []string{"=", CaseSplitter}, []string{"a", "BC"}},
   297  
   298  		{"ab cd_ef.ghIj-kl", []string{}, []string{"ab", "cd", "ef", "gh", "Ij", "kl"}},
   299  		{"ab cd_ef.ghIj-kl", []string{"="}, []string{"ab", "cd_ef.ghIj-kl"}},
   300  		{"ab cd_ef.ghIj-kl", []string{CaseSplitter}, []string{"ab", "cd_ef.gh", "Ij-kl"}},
   301  		{"ab cd_ef.ghIj-kl", []string{"_", "-"}, []string{"ab", "cd", "ef.ghIj", "kl"}},
   302  		{"ab cd_ef.ghIj-kl", []string{" ", ".", ".", CaseSplitter}, []string{"ab", "cd_ef", "gh", "Ij-kl"}},
   303  
   304  		{"!", []string{"!"}, []string{}},
   305  		{"!!a!!", []string{"!"}, []string{"a"}},
   306  		{"aB_c-d.e+f g h?i", []string{CaseSplitter, "+", "?", "!"}, []string{"a", "B_c-d.e", "f", "g", "h", "i"}},
   307  		{"aB_c-d.e+f g h?i", []string{CaseSplitter, "_", "-", ".", "+"}, []string{"a", "B", "c", "d", "e", "f", "g", "h?i"}},
   308  		{"тестТест-a", []string{}, []string{"тест", "Тест", "a"}},
   309  		{"тестТест-a", []string{"-"}, []string{"тестТест", "a"}},
   310  		{"测试andテスТестOr", []string{}, []string{"测试andテス", "Тест", "Or"}},
   311  		{"测试 and テス тест or", []string{"and"}, []string{"测试", "テス", "тест", "or"}},
   312  		{"mix-Mix_Mix.Mix?mix", []string{"-", "_", ".", "?"}, []string{"mix", "Mix", "Mix", "Mix", "mix"}},
   313  	} {
   314  		t.Run(tc.giveString, func(t *testing.T) {
   315  			words := SplitToWords(tc.giveString, tc.giveSeps...)
   316  			xtesting.Equal(t, words, tc.want)
   317  		})
   318  	}
   319  }
   320  
   321  func TestXXXCase(t *testing.T) {
   322  	for _, tc := range []struct {
   323  		give  string
   324  		wantP string
   325  		wantC string
   326  		wantS string
   327  		wantK string
   328  	}{
   329  		{"", "", "", "", ""},
   330  		{"a", "A", "a", "a", "a"},
   331  		{"A", "A", "a", "a", "a"},
   332  		{"abc", "Abc", "abc", "abc", "abc"},
   333  		{"abCdEF", "AbCdEF", "abCdEF", "ab_cd_ef", "ab-cd-ef"},
   334  		{"AAaaAA_bbBBbb", "AAaaAABbBBbb", "aaaaAABbBBbb", "aaaa_aa_bb_bbbb", "aaaa-aa-bb-bbbb"},
   335  		{"a1a b2B C3c D4D", "A1aB2BC3cD4D", "a1aB2BC3cD4D", "a1a_b2_b_c3c_d4_d", "a1a-b2-b-c3c-d4-d"},
   336  		{"IPv4Address-and-Port", "IPv4AddressAndPort", "ipv4AddressAndPort", "ipv4_address_and_port", "ipv4-address-and-port"},
   337  		{"TestPascalCase", "TestPascalCase", "testPascalCase", "test_pascal_case", "test-pascal-case"},
   338  		{"testCamelCase", "TestCamelCase", "testCamelCase", "test_camel_case", "test-camel-case"},
   339  		{"test_snake_case", "TestSnakeCase", "testSnakeCase", "test_snake_case", "test-snake-case"},
   340  		{"test-kebab-case", "TestKebabCase", "testKebabCase", "test_kebab_case", "test-kebab-case"},
   341  		{"testMixed_Case-Test.Test", "TestMixedCaseTestTest", "testMixedCaseTestTest", "test_mixed_case_test_test", "test-mixed-case-test-test"},
   342  	} {
   343  		t.Run(tc.give, func(t *testing.T) {
   344  			xtesting.Equal(t, PascalCase(tc.give), tc.wantP)
   345  			xtesting.Equal(t, CamelCase(tc.give), tc.wantC)
   346  			xtesting.Equal(t, SnakeCase(tc.give), tc.wantS)
   347  			xtesting.Equal(t, KebabCase(tc.give), tc.wantK)
   348  		})
   349  	}
   350  }
   351  
   352  // showFn represents if tests need to show shuffle and random result.
   353  var showFn = func() bool { return false }
   354  
   355  func TestTimeID(t *testing.T) {
   356  	zero := time.Time{}
   357  	now := time.Date(2021, time.Month(1), 18, 15, 07, 25, 123456789, time.UTC)
   358  	for _, tc := range []struct {
   359  		giveTime  time.Time
   360  		giveCount int
   361  		want      string
   362  	}{
   363  		{now, 0, ""},
   364  		{now, 1, "2"},
   365  		{zero, 4, "0001"},
   366  		{now, 5, "20210"},
   367  		{now, 8, "20210118"},
   368  		{zero, 8, "00010101"},
   369  		{now, 14, "20210118150725"},
   370  		{now, 23, "20210118150725123456789"},
   371  		{zero, 23, "00010101000000000000000"},
   372  		{now, 30, "20210118150725123456789"}, // 25
   373  		{now, 38, "20210118150725123456789"},
   374  	} {
   375  		uuid := TimeID(tc.giveTime, tc.giveCount)
   376  		if tc.giveCount <= 23 {
   377  			xtesting.Equal(t, uuid, tc.want)
   378  		} else {
   379  			xtesting.Equal(t, len(uuid), tc.giveCount)
   380  			xtesting.Equal(t, uuid[:23], tc.want)
   381  
   382  			for i := 0; i < 4; i++ {
   383  				time.Sleep(2 * time.Nanosecond)
   384  				uuid1 := TimeID(tc.giveTime, tc.giveCount)
   385  				time.Sleep(2 * time.Nanosecond)
   386  				uuid2 := TimeID(tc.giveTime, tc.giveCount)
   387  				xtesting.NotEqual(t, uuid1, uuid2)
   388  				if showFn() {
   389  					fmt.Println(uuid1, uuid2)
   390  				}
   391  			}
   392  		}
   393  	}
   394  }
   395  
   396  func TestRandString(t *testing.T) {
   397  	for _, tc := range []struct {
   398  		giveFn    func(int) string
   399  		giveCount int
   400  	}{
   401  		{RandCapitalLetterString, 0},
   402  		{RandCapitalLetterString, 5},
   403  		{RandCapitalLetterString, 20},
   404  
   405  		{RandLowercaseLetterString, 0},
   406  		{RandLowercaseLetterString, 5},
   407  		{RandLowercaseLetterString, 20},
   408  
   409  		{RandLetterString, 0},
   410  		{RandLetterString, 5},
   411  		{RandLetterString, 20},
   412  
   413  		{RandNumberString, 0},
   414  		{RandNumberString, 5},
   415  		{RandNumberString, 20},
   416  
   417  		{RandCapitalLetterNumberString, 0},
   418  		{RandCapitalLetterNumberString, 5},
   419  		{RandCapitalLetterNumberString, 20},
   420  
   421  		{RandLowercaseLetterNumberString, 0},
   422  		{RandLowercaseLetterNumberString, 5},
   423  		{RandLowercaseLetterNumberString, 20},
   424  
   425  		{RandLetterNumberString, 0},
   426  		{RandLetterNumberString, 5},
   427  		{RandLetterNumberString, 20},
   428  	} {
   429  		r := tc.giveFn(tc.giveCount)
   430  		if tc.giveCount == 0 {
   431  			xtesting.Equal(t, r, "")
   432  		} else {
   433  			xtesting.Equal(t, len(r), tc.giveCount)
   434  
   435  			for i := 0; i < 4; i++ {
   436  				time.Sleep(2 * time.Nanosecond)
   437  				r1 := tc.giveFn(tc.giveCount)
   438  				time.Sleep(2 * time.Nanosecond)
   439  				r2 := tc.giveFn(tc.giveCount)
   440  				xtesting.NotEqual(t, r1, r2)
   441  				if showFn() {
   442  					fmt.Println(r1, r2)
   443  				}
   444  			}
   445  		}
   446  	}
   447  }
   448  
   449  func TestFastStob(t *testing.T) {
   450  	for _, tc := range []struct {
   451  		give string
   452  		want []byte
   453  	}{
   454  		{"", []byte{}},
   455  		{"a", []byte{'a'}},
   456  		{"hello", []byte{'h', 'e', 'l', 'l', 'o'}},
   457  		{"a b c", []byte{'a', ' ', 'b', ' ', 'c'}},
   458  		{"测试", []byte("测试")},
   459  		{"テス", []byte("テス")},
   460  		{"тест", []byte("тест")},
   461  	} {
   462  		xtesting.Equal(t, FastStob(tc.give), tc.want)
   463  	}
   464  }
   465  
   466  func TestFastBtos(t *testing.T) {
   467  	for _, tc := range []struct {
   468  		give []byte
   469  		want string
   470  	}{
   471  		{[]byte{}, ""},
   472  		{[]byte{'a'}, "a"},
   473  		{[]byte{'h', 'e', 'l', 'l', 'o'}, "hello"},
   474  		{[]byte{'a', ' ', 'b', ' ', 'c'}, "a b c"},
   475  		{[]byte("测试"), "测试"},
   476  		{[]byte("テス"), "テス"},
   477  		{[]byte("тест"), "тест"},
   478  	} {
   479  		xtesting.Equal(t, FastBtos(tc.give), tc.want)
   480  	}
   481  }
   482  
   483  func BenchmarkFastStob(b *testing.B) {
   484  	s := "hello world"
   485  
   486  	b.Run("FastStob", func(b *testing.B) {
   487  		b.ReportAllocs()
   488  		b.ResetTimer()
   489  		for i := 0; i < b.N; i++ {
   490  			_ = FastStob(s)
   491  		}
   492  	})
   493  	b.Run("ConvertToBytes", func(b *testing.B) {
   494  		b.ReportAllocs()
   495  		b.ResetTimer()
   496  		for i := 0; i < b.N; i++ {
   497  			_ = []byte(s)
   498  		}
   499  	})
   500  }
   501  
   502  func BenchmarkFastBtos(b *testing.B) {
   503  	bs := []byte{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}
   504  
   505  	b.Run("FastBtos", func(b *testing.B) {
   506  		b.ReportAllocs()
   507  		b.ResetTimer()
   508  		for i := 0; i < b.N; i++ {
   509  			_ = FastBtos(bs)
   510  		}
   511  	})
   512  	b.Run("ConvertToString", func(b *testing.B) {
   513  		b.ReportAllocs()
   514  		b.ResetTimer()
   515  		for i := 0; i < b.N; i++ {
   516  			_ = string(bs)
   517  		}
   518  	})
   519  }
   520  
   521  func TestTrimUTF8XXX(t *testing.T) {
   522  	// Bom
   523  	for _, tc := range []struct {
   524  		giveStr string
   525  		giveBs  []byte
   526  		wantStr string
   527  		wantBs  []byte
   528  	}{
   529  		{"", []byte{}, "", []byte{}},
   530  		{"test", []byte{'t', 'e', 's', 't'}, "test", []byte{'t', 'e', 's', 't'}},
   531  		{"\xef\xbb\xbf", []byte{0xEF, 0xBB, 0xBF}, "", []byte{}},
   532  		{"\xef\xbb\xbftest", []byte{0xEF, 0xBB, 0xBF, 't', 'e', 's', 't'}, "test", []byte{'t', 'e', 's', 't'}},
   533  	} {
   534  		xtesting.Equal(t, TrimUTF8Bom(tc.giveStr), tc.wantStr)
   535  		xtesting.Equal(t, TrimUTF8BomBytes(tc.giveBs), tc.wantBs)
   536  	}
   537  
   538  	// Rc
   539  	for _, tc := range []struct {
   540  		giveStr string
   541  		giveBs  []byte
   542  		wantStr string
   543  		wantBs  []byte
   544  	}{
   545  		{"", []byte{}, "", []byte{}},
   546  		{"test", []byte{'t', 'e', 's', 't'}, "test", []byte{'t', 'e', 's', 't'}},
   547  		{"\xef\xbf\xbd", []byte{0xEF, 0xBF, 0xBD}, "", []byte{}},
   548  		{"\xef\xbf\xbdtest", []byte{0xEF, 0xBF, 0xBD, 't', 'e', 's', 't'}, "test", []byte{'t', 'e', 's', 't'}},
   549  	} {
   550  		xtesting.Equal(t, TrimUTF8Replacement(tc.giveStr), tc.wantStr)
   551  		xtesting.Equal(t, TrimUTF8ReplacementBytes(tc.giveBs), tc.wantBs)
   552  	}
   553  }
   554  
   555  func TestPadGetLeftRight(t *testing.T) {
   556  	for _, tc := range []struct {
   557  		give       string
   558  		givePad    rune
   559  		giveLength int
   560  		wantPadL   string
   561  		wantPadR   string
   562  		wantGetL   string
   563  		wantGetR   string
   564  		wantL      string
   565  		wantR      string
   566  	}{
   567  		{"", '0', -2, "", "", "", "", "", ""},
   568  		{"", '0', -1, "", "", "", "", "", ""},
   569  		{"", '0', 0, "", "", "", "", "", ""},
   570  		{"", '0', 1, "0", "0", "", "", "0", "0"},
   571  		{"", '0', 2, "00", "00", "", "", "00", "00"},
   572  
   573  		{"1", '0', -2, "1", "1", "", "", "", ""},
   574  		{"1", '0', -1, "1", "1", "", "", "", ""},
   575  		{"1", '0', 0, "1", "1", "", "", "", ""},
   576  		{"1", '0', 1, "1", "1", "1", "1", "1", "1"},
   577  		{"1", '0', 2, "01", "10", "1", "1", "01", "10"},
   578  		{"1", '0', 3, "001", "100", "1", "1", "001", "100"},
   579  
   580  		{"123", '0', -1, "123", "123", "", "", "", ""},
   581  		{"123", '0', 0, "123", "123", "", "", "", ""},
   582  		{"123", '0', 1, "123", "123", "1", "3", "1", "3"},
   583  		{"123", '0', 2, "123", "123", "12", "23", "12", "23"},
   584  		{"123", '0', 3, "123", "123", "123", "123", "123", "123"},
   585  		{"123", '0', 4, "0123", "1230", "123", "123", "0123", "1230"},
   586  		{"123", '0', 5, "00123", "12300", "123", "123", "00123", "12300"},
   587  
   588  		{"test", ' ', -1, "test", "test", "", "", "", ""},
   589  		{"test", ' ', 3, "test", "test", "tes", "est", "tes", "est"},
   590  		{"test", ' ', 4, "test", "test", "test", "test", "test", "test"},
   591  		{"test", ' ', 5, " test", "test ", "test", "test", " test", "test "},
   592  		{"test", ' ', 8, "    test", "test    ", "test", "test", "    test", "test    "},
   593  
   594  		{"测试テスтест", '零', 1, "测试テスтест", "测试テスтест", "测", "т", "测", "т"},
   595  		{"测试テスтест", '零', 3, "测试テスтест", "测试テスтест", "测试テ", "ест", "测试テ", "ест"},
   596  		{"测试テスтест", '零', 6, "测试テスтест", "测试テスтест", "测试テスте", "テスтест", "测试テスте", "テスтест"},
   597  		{"测试テスтест", '零', 7, "测试テスтест", "测试テスтест", "测试テスтес", "试テスтест", "测试テスтес", "试テスтест"},
   598  		{"测试テスтест", '零', 8, "测试テスтест", "测试テスтест", "测试テスтест", "测试テスтест", "测试テスтест", "测试テスтест"},
   599  		{"测试テスтест", '零', 9, "零测试テスтест", "测试テスтест零", "测试テスтест", "测试テスтест", "零测试テスтест", "测试テスтест零"},
   600  		{"测试テスтест", '零', 11, "零零零测试テスтест", "测试テスтест零零零", "测试テスтест", "测试テスтест", "零零零测试テスтест", "测试テスтест零零零"},
   601  	} {
   602  		t.Run(tc.give, func(t *testing.T) {
   603  			xtesting.Equal(t, PadLeft(tc.give, tc.givePad, tc.giveLength), tc.wantPadL)
   604  			xtesting.Equal(t, PadRight(tc.give, tc.givePad, tc.giveLength), tc.wantPadR)
   605  			xtesting.Equal(t, GetLeft(tc.give, tc.giveLength), tc.wantGetL)
   606  			xtesting.Equal(t, GetRight(tc.give, tc.giveLength), tc.wantGetR)
   607  			xtesting.Equal(t, GetOrPadLeft(tc.give, tc.giveLength, tc.givePad), tc.wantL)
   608  			xtesting.Equal(t, GetOrPadRight(tc.give, tc.giveLength, tc.givePad), tc.wantR)
   609  		})
   610  	}
   611  }
   612  
   613  func TestBool(t *testing.T) {
   614  	for _, tc := range []struct {
   615  		giveBool bool
   616  		giveT    string
   617  		giveF    string
   618  		want     string
   619  	}{
   620  		{true, "T", "F", "T"},
   621  		{true, "true", "false", "true"},
   622  		{true, "1", "0", "1"},
   623  		{false, "T", "F", "F"},
   624  		{false, "true", "false", "false"},
   625  		{false, "1", "0", "0"},
   626  	} {
   627  		xtesting.Equal(t, Bool(tc.giveBool, tc.giveT, tc.giveF), tc.want)
   628  	}
   629  }
   630  
   631  func TestExtraSpace(t *testing.T) {
   632  	for _, tc := range []struct {
   633  		give       string
   634  		wantLeftS  string
   635  		wantRightS string
   636  		wantLeftB  string
   637  		wantRightB string
   638  	}{
   639  		{"", "", "", "", ""},
   640  		{" ", "  ", "  ", " ", " "},
   641  		{"\t", " \t", "\t ", "\t", "\t"},
   642  		{"\r\n", " \r\n", "\r\n ", "\r\n", "\r\n"},
   643  		{"a", " a", "a ", " a", "a "},
   644  		{"  a|\n\n\n ", "   a|\n\n\n ", "  a|\n\n\n  ", " a|", "a| "},
   645  		{"\r\ta| ", " \r\ta| ", "\r\ta|  ", " a|", "a| "},
   646  	} {
   647  		t.Run(tc.give, func(t *testing.T) {
   648  			xtesting.Equal(t, ExtraSpaceOnLeftIfNotEmpty(tc.give), tc.wantLeftS)
   649  			xtesting.Equal(t, ExtraSpaceOnRightIfNotEmpty(tc.give), tc.wantRightS)
   650  			xtesting.Equal(t, ExtraSpaceOnLeftIfNotBlank(tc.give), tc.wantLeftB)
   651  			xtesting.Equal(t, ExtraSpaceOnRightIfNotBlank(tc.give), tc.wantRightB)
   652  		})
   653  	}
   654  }
   655  
   656  func TestMaskToken(t *testing.T) {
   657  	for _, tc := range []struct {
   658  		giveString  string
   659  		giveIndices []int
   660  		want        string
   661  		wantR       string
   662  	}{
   663  		{"", []int{}, "", ""},
   664  		{"", []int{-2}, "", ""},
   665  		{"", []int{-1}, "", ""},
   666  		{"", []int{0}, "", ""},
   667  		{"", []int{1}, "", ""},
   668  		{"", []int{2}, "", ""},
   669  
   670  		{"a", []int{}, "a", "*"},
   671  		{"a", []int{-3}, "a", "*"},
   672  		{"a", []int{-2}, "a", "*"},
   673  		{"a", []int{-1}, "*", "a"}, // <<<
   674  		{"a", []int{0}, "*", "a"},  // <<<
   675  		{"a", []int{1}, "a", "*"},
   676  		{"a", []int{2}, "a", "*"},
   677  		{"a", []int{3}, "a", "*"},
   678  		{"a", []int{0, 1}, "*", "a"},
   679  		{"a", []int{-1, -2}, "*", "a"},
   680  		{"a", []int{0, -1}, "*", "a"}, // <<<
   681  
   682  		{"aa", []int{}, "aa", "**"},
   683  		{"aa", []int{-3}, "aa", "**"},
   684  		{"aa", []int{-2}, "*a", "a*"}, // <<<
   685  		{"aa", []int{-1}, "a*", "*a"}, // <<<
   686  		{"aa", []int{0}, "*a", "a*"},  // <<<
   687  		{"aa", []int{1}, "a*", "*a"},  // <<<
   688  		{"aa", []int{2}, "aa", "**"},
   689  		{"aa", []int{3}, "aa", "**"},
   690  		{"aa", []int{-3, 0}, "*a", "a*"},
   691  		{"aa", []int{0, 1}, "**", "aa"}, // <<<
   692  		{"aa", []int{1, 2}, "a*", "*a"},
   693  		{"aa", []int{-1, -2}, "**", "aa"}, // <<<
   694  		{"aa", []int{-2, -3}, "*a", "a*"},
   695  		{"aa", []int{0, -1}, "**", "aa"}, // <<<
   696  
   697  		{"aaa", []int{}, "aaa", "***"},
   698  		{"aaa", []int{0}, "*aa", "a**"},
   699  		{"aaa", []int{1}, "a*a", "*a*"},
   700  		{"aaa", []int{2}, "aa*", "**a"},
   701  		{"aaa", []int{-1}, "aa*", "**a"},
   702  		{"aaa", []int{-2}, "a*a", "*a*"},
   703  		{"aaa", []int{-3}, "*aa", "a**"},
   704  		{"aaa", []int{0, 1}, "**a", "aa*"},
   705  		{"aaa", []int{-1, -3}, "*a*", "a*a"},
   706  		{"aaa", []int{1, -1}, "a**", "*aa"},
   707  
   708  		{"pwd123abcpwd", []int{3, 4, 5, 6, 7, 8, 20}, "pwd******pwd", "***123abc***"},
   709  		{"pwd123abcpwd", []int{0, 1, 2, 9, 10, 11, 20}, "***123abc***", "pwd******pwd"},
   710  		{"pwd123abcpwd", []int{-3, -4, -5, -6, -7, -8, -20}, "pwd1******wd", "****23abcp**"},
   711  		{"pwd123abcpwd", []int{0, -1, -2, -9, -10, -11, -20}, "****23abcp**", "pwd1******wd"},
   712  	} {
   713  		xtesting.Equal(t, MaskToken(tc.giveString, '*', tc.giveIndices...), tc.want)
   714  		xtesting.Equal(t, MaskTokenR(tc.giveString, '*', tc.giveIndices...), tc.wantR)
   715  		xtesting.Equal(t, StringMaskToken(tc.giveString, "#$%", tc.giveIndices...), strings.ReplaceAll(tc.want, "*", "#$%"))
   716  		xtesting.Equal(t, StringMaskTokenR(tc.giveString, "#$%", tc.giveIndices...), strings.ReplaceAll(tc.wantR, "*", "#$%"))
   717  	}
   718  }
   719  
   720  func TestEncodeUrlValues(t *testing.T) {
   721  	for _, tc := range []struct {
   722  		give           map[string][]string
   723  		giveEscapeFunc func(string) string
   724  		want           string
   725  	}{
   726  		{map[string][]string{}, nil, ""},
   727  		{map[string][]string{"a": {}}, nil, ""},
   728  		{map[string][]string{"a": {""}}, nil, "a="},
   729  		{map[string][]string{"": {"a"}}, nil, "=a"},
   730  		{map[string][]string{"a": {"", "", ""}}, nil, "a=&a=&a="},
   731  
   732  		{map[string][]string{"a": {"b"}, "c": {}}, nil, "a=b"},
   733  		{map[string][]string{"a": {"b"}, "c": {"d"}}, nil, "a=b&c=d"},
   734  		{map[string][]string{"c": {"e", "d"}, "a": {"b"}}, nil, "a=b&c=e&c=d"},
   735  		{map[string][]string{"a": {"a1", "a2", "a3"}, "b": {"b1", "b2", "b3"}}, nil, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3"},
   736  
   737  		{map[string][]string{"q": {"a+b"}, "order": {"-true"}, "%": {"?"}}, url.QueryEscape, "%25=%3F&order=-true&q=a%2Bb"},
   738  		{map[string][]string{"test": {"测试", "テス", "тест"}}, url.QueryEscape, "test=%E6%B5%8B%E8%AF%95&test=%E3%83%86%E3%82%B9&test=%D1%82%D0%B5%D1%81%D1%82"},
   739  		{map[string][]string{"q": {"a+b"}, "order": {"-true"}, "%": {"?"}}, url.PathEscape, "%25=%3F&order=-true&q=a+b"},
   740  		{map[string][]string{"test": {"测试", "テス", "тест"}}, url.PathEscape, "test=%E6%B5%8B%E8%AF%95&test=%E3%83%86%E3%82%B9&test=%D1%82%D0%B5%D1%81%D1%82"},
   741  		{map[string][]string{"a": {"b"}, "c": {"d"}}, func(s string) string { return "?" }, "?=?&?=?"},
   742  	} {
   743  		xtesting.Equal(t, EncodeUrlValues(tc.give, tc.giveEscapeFunc), tc.want)
   744  	}
   745  }
   746  
   747  func TestSplitAndGet(t *testing.T) {
   748  	for _, tc := range []struct {
   749  		give      string
   750  		giveSep   string
   751  		giveIndex int
   752  		want      string
   753  		wantPanic bool
   754  	}{
   755  		{"", "", 0, "", true},
   756  		{"", "", 1, "", true},
   757  		{"", "", -1, "", true},
   758  		{" ", "", 0, " ", false},
   759  		{" ", "", -1, " ", false},
   760  
   761  		{"a b", "", 0, "a", false},
   762  		{"a b", "", 1, " ", false},
   763  		{"a b", "", 2, "b", false},
   764  		{"a b", "", 3, "", true},
   765  		{"a b", "", -1, "b", false},
   766  		{"a b", "", -2, " ", false},
   767  		{"a b", "", -3, "a", false},
   768  		{"a b", "", -4, "", true},
   769  
   770  		{"a b", " ", 0, "a", false},
   771  		{"a b", " ", 1, "b", false},
   772  		{"a b", " ", 2, "", true},
   773  		{"a b", " ", -1, "b", false},
   774  		{"a b", " ", -2, "a", false},
   775  		{"a b", " ", -3, "", true},
   776  	} {
   777  		if tc.wantPanic {
   778  			xtesting.Panic(t, func() { SplitAndGet(tc.give, tc.giveSep, tc.giveIndex) })
   779  		} else {
   780  			xtesting.Equal(t, SplitAndGet(tc.give, tc.giveSep, tc.giveIndex), tc.want)
   781  		}
   782  	}
   783  }
   784  
   785  func TestStringSliceToStringMap(t *testing.T) {
   786  	type Map = map[string]string
   787  	for _, tc := range []struct {
   788  		give []string
   789  		want Map
   790  	}{
   791  		{nil, Map{}},
   792  		{[]string{}, Map{}},
   793  		{[]string{""}, Map{}},
   794  		{[]string{"xxx"}, Map{}},
   795  
   796  		{[]string{"a", "b"}, Map{"a": "b"}},
   797  		{[]string{"a", "b", "c"}, Map{"a": "b"}},
   798  		{[]string{"a", "", "b", "c", "d"}, Map{"a": "", "b": "c"}},
   799  		{[]string{"a", "", "a", "b", "c"}, Map{"a": "b"}},
   800  		{[]string{"", "", "", " ", " ", "", "x"}, Map{"": " ", " ": ""}},
   801  	} {
   802  		xtesting.Equal(t, StringSliceToMap(tc.give), tc.want)
   803  	}
   804  }
   805  
   806  func TestSliceToStringMap(t *testing.T) {
   807  	type Map = map[string]interface{}
   808  	for _, tc := range []struct {
   809  		give []interface{}
   810  		want Map
   811  	}{
   812  		{nil, Map{}},
   813  		{[]interface{}{}, Map{}},
   814  		{[]interface{}{1}, Map{}},
   815  		{[]interface{}{nil}, Map{}},
   816  
   817  		{[]interface{}{"1", 2}, Map{"1": 2}},
   818  		{[]interface{}{1, uint32(2)}, Map{"1": uint32(2)}},
   819  		{[]interface{}{uint(1), 2.3}, Map{"1": 2.3}},
   820  		{[]interface{}{true, 2i}, Map{"true": 2i}},
   821  		{[]interface{}{1 + 2i, false}, Map{"(1+2i)": false}},
   822  		{[]interface{}{[]byte("key"), []byte("value")}, Map{"key": []byte("value")}},
   823  
   824  		{[]interface{}{nil, 2}, Map{}},
   825  		{[]interface{}{1, 2, 3}, Map{"1": 2}},
   826  		{[]interface{}{nil, 2, 3}, Map{"2": 3}},
   827  		{[]interface{}{1, 2, nil, 3, 4}, Map{"1": 2, "3": 4}},
   828  		{[]interface{}{1, 2, 1, 3, 1, 4, 2, 3}, Map{"1": 4, "2": 3}},
   829  		{[]interface{}{1, "2", []byte("3"), 4.4}, Map{"1": "2", "3": 4.4}},
   830  		{[]interface{}{true, 2, 3.3, true}, Map{"true": 2, "3.3": true}},
   831  	} {
   832  		xtesting.Equal(t, SliceToStringMap(tc.give), tc.want)
   833  	}
   834  }
   835  
   836  func TestSemanticVersion(t *testing.T) {
   837  	for _, tc := range []struct {
   838  		give      string
   839  		wantP1    uint64
   840  		wantP2    uint64
   841  		wantP3    uint64
   842  		wantError bool
   843  	}{
   844  		{"", 0, 0, 0, true},
   845  		{".1", 0, 0, 0, true},
   846  		{"v", 0, 0, 0, true},
   847  		{"vw", 0, 0, 0, true},
   848  		{"w1", 0, 0, 0, true},
   849  		{"0v", 0, 0, 0, true},
   850  		{"0.v", 0, 0, 0, true},
   851  		{"0.0.v", 0, 0, 0, true},
   852  		{"0v", 0, 0, 0, true},
   853  		{"v0.v", 0, 0, 0, true},
   854  		{"v0.v.0", 0, 0, 0, true},
   855  
   856  		{"1", 1, 0, 0, false},
   857  		{"1.2", 1, 2, 0, false},
   858  		{"1.2.3", 1, 2, 3, false},
   859  		{"v1", 1, 0, 0, false},
   860  		{"v1.2", 1, 2, 0, false},
   861  		{"v1.2.3", 1, 3, 3, false},
   862  	} {
   863  		t.Run(tc.give, func(t *testing.T) {
   864  			p1, p2, p3, err := SemanticVersion(tc.give)
   865  			xtesting.Equal(t, err != nil, tc.wantError)
   866  			if err != nil {
   867  				xtesting.Equal(t, p1, tc.wantP1)
   868  				xtesting.Equal(t, p2, tc.wantP2)
   869  				xtesting.Equal(t, p3, tc.wantP3)
   870  			}
   871  		})
   872  	}
   873  }