bitbucket.org/ai69/amoy@v0.2.3/replace_test.go (about) 1 package amoy 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/1set/gut/yrand" 9 ) 10 11 func BenchmarkReplaceString_Single_CaseSensitive_Literal(b *testing.B) { 12 s := "aloha Hello hello word WORLD World 123456" 13 replace := map[string]string{"hello": "Goodbye"} 14 opt := ReplaceStringOptions{replace, false, false} 15 b.ResetTimer() 16 for i := 0; i < b.N; i++ { 17 _ = ReplaceString(s, opt) 18 } 19 } 20 21 func BenchmarkReplaceString_Multiple_CaseSensitive_Literal(b *testing.B) { 22 s := "aloha Hello hello word WORLD World 123456" 23 replace := map[string]string{"hello": "Goodbye", "world": "Earth"} 24 opt := ReplaceStringOptions{replace, false, false} 25 b.ResetTimer() 26 for i := 0; i < b.N; i++ { 27 _ = ReplaceString(s, opt) 28 } 29 } 30 31 func BenchmarkReplaceString_Single_CaseInsensitive_Literal(b *testing.B) { 32 s := "aloha Hello hello word WORLD World 123456" 33 replace := map[string]string{"hello": "Goodbye"} 34 opt := ReplaceStringOptions{replace, true, false} 35 b.ResetTimer() 36 for i := 0; i < b.N; i++ { 37 _ = ReplaceString(s, opt) 38 } 39 } 40 41 func BenchmarkReplaceString_Multiple_CaseInsensitive_Literal(b *testing.B) { 42 s := "aloha Hello hello word WORLD World 123456" 43 replace := map[string]string{"hello": "Goodbye", "world": "Earth"} 44 opt := ReplaceStringOptions{replace, true, false} 45 b.ResetTimer() 46 for i := 0; i < b.N; i++ { 47 _ = ReplaceString(s, opt) 48 } 49 } 50 51 func BenchmarkReplaceString_Single_CaseInsensitive_Imitation(b *testing.B) { 52 s := "aloha Hello hello word WORLD World 123456" 53 replace := map[string]string{"hello": "Goodbye"} 54 opt := ReplaceStringOptions{replace, true, true} 55 b.ResetTimer() 56 for i := 0; i < b.N; i++ { 57 _ = ReplaceString(s, opt) 58 } 59 } 60 61 func BenchmarkReplaceString_Multiple_CaseInsensitive_Imitation(b *testing.B) { 62 s := "aloha Hello hello word WORLD World 123456" 63 replace := map[string]string{"hello": "Goodbye", "world": "Earth"} 64 opt := ReplaceStringOptions{replace, true, true} 65 b.ResetTimer() 66 for i := 0; i < b.N; i++ { 67 _ = ReplaceString(s, opt) 68 } 69 } 70 71 func TestReplaceString(t *testing.T) { 72 optsNilReplace := ReplaceStringOptions{} 73 optsEmptyReplace := ReplaceStringOptions{ 74 Replacements: map[string]string{}, 75 CaseInsensitive: false, 76 ImitateResult: false, 77 } 78 optsInvalidReplace := ReplaceStringOptions{ 79 Replacements: map[string]string{"": "123"}, 80 CaseInsensitive: false, 81 ImitateResult: false, 82 } 83 optsMixedReplace := ReplaceStringOptions{ 84 Replacements: map[string]string{"": "456", "hello": "goodbye"}, 85 CaseInsensitive: false, 86 ImitateResult: false, 87 } 88 optsSingleReplace := ReplaceStringOptions{ 89 Replacements: map[string]string{"hello": "goodbye"}, 90 CaseInsensitive: false, 91 ImitateResult: false, 92 } 93 s0 := "Hello hello word WORLD" 94 replace0 := map[string]string{"hello": "Goodbye"} 95 s1 := "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD" 96 s2 := "abcdefghijklmnopqrstuvwxyzstuvwxabd" 97 replace1 := map[string]string{ 98 "ABC": "123", 99 "STUVWX": "888ABCGH", 100 "GH": "6789", 101 "STUVW": "999", 102 } 103 replace2 := map[string]string{ 104 "ABC": "11", 105 "ABCDEF": "22222", 106 "DEF": "3", 107 "GH": "6789", 108 } 109 replace3 := map[string]string{ 110 `standard`: `{{ .AppName }}`, 111 `bitbucket.org/ai69/keiki/kgengo/standard`: `{{ .ModulePath }}`, 112 `Copyright (c) 2022 Hyori`: `Copyright (c) {{ .Now.UTC.Year }} {{ .Author }}`, 113 } 114 replace4 := map[string]string{ 115 "": "888", 116 "abc": "123", 117 "hello-world": "good-bye", 118 "github.com": "gitlab.com", 119 "github.com/abc": "bitbucket.org/def", 120 } 121 tests := []struct { 122 name string 123 s string 124 opt ReplaceStringOptions 125 want string 126 }{ 127 {"Default Options", "aloha", optsNilReplace, "aloha"}, 128 {"Empty Replace", "aloha", optsEmptyReplace, "aloha"}, 129 {"Empty Src", "", optsSingleReplace, ""}, 130 {"Invalid Replace", "aloha", optsInvalidReplace, "aloha"}, 131 {"Single Replace", "hello world", optsSingleReplace, "goodbye world"}, 132 {"Mixed Replace", "hello world", optsMixedReplace, "goodbye world"}, 133 {"No Match", s1, optsSingleReplace, s1}, 134 {"Single Case-sensitive and Imitation", s0, ReplaceStringOptions{replace0, false, true}, "Hello goodbye word WORLD"}, 135 {"Single Case-insensitive and Literal", s0, ReplaceStringOptions{replace0, true, false}, "Goodbye Goodbye word WORLD"}, 136 {"Multiple Case-sensitive and Literal - Replace 1", s1, ReplaceStringOptions{replace1, false, false}, "123DEF6789IJKLMNOPQR888ABCGHYZ888ABCGHABD"}, 137 {"Multiple Case-sensitive and Literal - Replace 2", s1, ReplaceStringOptions{replace2, false, false}, "222226789IJKLMNOPQRSTUVWXYZSTUVWXABD"}, 138 {"Multiple Case-sensitive and Imitation - Miss 1", s2, ReplaceStringOptions{replace1, false, true}, s2}, 139 {"Multiple Case-sensitive and Imitation - Miss 2", s2, ReplaceStringOptions{replace2, false, true}, s2}, 140 {"Multiple Case-insensitive and Literal - Replace 1", s2, ReplaceStringOptions{replace1, true, false}, "123def6789ijklmnopqr888ABCGHyz888ABCGHabd"}, 141 {"Multiple Case-insensitive and Literal - Replace 2", s2, ReplaceStringOptions{replace2, true, false}, "222226789ijklmnopqrstuvwxyzstuvwxabd"}, 142 {"Multiple Case-insensitive and Imitation - Replace 1", s2, ReplaceStringOptions{replace1, true, true}, "123def6789ijklmnopqr888abcghyz888abcghabd"}, 143 {"Multiple Case-insensitive and Imitation - Replace 2", s2, ReplaceStringOptions{replace2, true, true}, "222226789ijklmnopqrstuvwxyzstuvwxabd"}, 144 {"Real Case 1", "# standard\n\n```bash\ngo install bitbucket.org/ai69/keiki/kgengo/standard\n```\n", ReplaceStringOptions{replace3, true, true}, "# {{ .appname }}\n\n```bash\ngo install {{ .modulepath }}\n```\n"}, 145 {"Real Case 2", "https://gitlab.com/ab3C/repo\n", ReplaceStringOptions{replace4, true, true}, "https://gitlab.com/ab3C/repo\n"}, 146 {"Real Case 3", "https://github.com/abc/repo\n", ReplaceStringOptions{replace4, true, true}, "https://bitbucket.org/def/repo\n"}, 147 {"Real Case 4", "https://github.com/Hello-World", ReplaceStringOptions{replace4, true, true}, "https://gitlab.com/Good-Bye"}, 148 } 149 for _, tt := range tests { 150 t.Run(tt.name, func(t *testing.T) { 151 if got := ReplaceString(tt.s, tt.opt); got != tt.want { 152 t.Errorf("ReplaceString() = %v, want %v", got, tt.want) 153 } 154 }) 155 } 156 } 157 158 func Test_replaceMultipleString(t *testing.T) { 159 rps1 := []*replacePair{ 160 &replacePair{"STUVWX", "888ABCGH"}, 161 &replacePair{"STUVW", "999"}, 162 &replacePair{"ABC", "123"}, 163 &replacePair{"GH", "6789"}, 164 } 165 rps2 := []*replacePair{ 166 &replacePair{"ABC", "11"}, 167 &replacePair{"ABCDEF", "22222"}, 168 &replacePair{"DEF", "3"}, 169 &replacePair{"GH", "6789"}, 170 } 171 rps3 := []*replacePair{ 172 &replacePair{"ABCDEF", "22222"}, 173 &replacePair{"ABC", "11"}, 174 &replacePair{"DEF", "3"}, 175 &replacePair{"GH", "6789"}, 176 } 177 tests := []struct { 178 name string 179 s string 180 rps []*replacePair 181 ignoreCase bool 182 imitateOld bool 183 want string 184 }{ 185 {"Nil Pairs", "", nil, false, false, ""}, 186 {"Empty Pairs", "", []*replacePair{}, false, false, ""}, 187 {"Empty Src", "", []*replacePair{&replacePair{"abc", "123"}}, false, false, ""}, 188 {"Empty Old", "Aloha", []*replacePair{&replacePair{"", "123"}}, false, false, "Aloha"}, 189 {"Empty New", "Aloha 123", []*replacePair{&replacePair{"Aloha", ""}}, false, false, " 123"}, 190 {"Case-sensitive and Literal - Miss", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, false, false, "Hello World"}, 191 {"Case-sensitive and Literal - Hit", "Hello World", []*replacePair{&replacePair{"Hello", "aloha"}}, false, false, "aloha World"}, 192 {"Case-sensitive and Imitation - Miss", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, false, true, "Hello World"}, 193 {"Case-sensitive and Imitation - Hit", "Hello World", []*replacePair{&replacePair{"Hello", "aloha"}}, false, true, "Aloha World"}, 194 {"Case-insensitive and Literal - Miss", "Hello World", []*replacePair{&replacePair{"halo", "aloha"}}, true, false, "Hello World"}, 195 {"Case-insensitive and Literal - Hit", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, true, false, "aloha World"}, 196 {"Case-insensitive and Imitation - Miss", "Hello World", []*replacePair{&replacePair{"halo", "aloha"}}, true, true, "Hello World"}, 197 {"Case-insensitive and Imitation - Hit", "Hello World", []*replacePair{&replacePair{"hello", "aloha"}}, true, true, "Aloha World"}, 198 {"Case-insensitive and Imitation - Order 1", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps1, true, true, "123DEF6789IJKLMNOPQR888ABCGHYZ888ABCGHABD"}, 199 {"Case-insensitive and Imitation - Order 2", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps2, true, true, "1136789IJKLMNOPQRSTUVWXYZSTUVWXABD"}, 200 {"Case-insensitive and Imitation - Order 3", "ABCDEFGHIJKLMNOPQRSTUVWXYZSTUVWXABD", rps3, true, true, "222226789IJKLMNOPQRSTUVWXYZSTUVWXABD"}, 201 } 202 for _, tt := range tests { 203 t.Run(tt.name, func(t *testing.T) { 204 if got := replaceMultipleString(tt.s, tt.rps, tt.ignoreCase, tt.imitateOld); got != tt.want { 205 t.Errorf("replaceMultipleString() = %v, want %v", got, tt.want) 206 } 207 }) 208 } 209 } 210 211 func Test_replaceMultipleString_FuzzVerify(t *testing.T) { 212 const ( 213 targetTimes = 50 214 srcSize = 4096 215 pairSize = 10 216 ) 217 gen := func() (string, []string, []*replacePair) { 218 s, _ := yrand.StringBase62(srcSize) 219 bp := make([]string, 0, pairSize*2) 220 rp := make([]*replacePair, 0, pairSize) 221 for i := pairSize; i > 0; i-- { 222 olds, _ := yrand.StringBase62(i*2 - 1) 223 news, _ := yrand.StringBase62(i*2 - 2) 224 bp = append(bp, olds, news) 225 rp = append(rp, &replacePair{olds, news}) 226 } 227 return s, bp, rp 228 } 229 for i := 0; i < targetTimes; i++ { 230 t.Run(fmt.Sprintf("Try%d", i+1), func(t *testing.T) { 231 s, bp, rp := gen() 232 replacer := strings.NewReplacer(bp...) 233 res1 := replacer.Replace(s) 234 res2 := replaceMultipleString(s, rp, false, false) 235 if res1 != res2 { 236 t.Errorf("replaceMultipleString(%q, %v) got = %q, want %q", s, bp, res2, res1) 237 } 238 }) 239 } 240 } 241 242 func Test_replaceSingleString(t *testing.T) { 243 tests := []struct { 244 name string 245 s string 246 rp *replacePair 247 ignoreCase bool 248 imitateOld bool 249 want string 250 }{ 251 {"Nil Pair", "aloha", nil, false, false, "aloha"}, 252 {"Empty Src", "", &replacePair{"abc", "123"}, false, false, ""}, 253 {"Empty Pair", "aloha", &replacePair{"", ""}, false, false, "aloha"}, 254 {"Empty Old", "aloha", &replacePair{"", "123"}, false, false, "aloha"}, 255 {"Empty New", "aloha 123", &replacePair{"aloha", ""}, false, false, " 123"}, 256 {"Case-sensitive and Literal - Miss", "Hello World", &replacePair{"hello", "aloha"}, false, false, "Hello World"}, 257 {"Case-sensitive and Literal - Hit", "Hello World", &replacePair{"Hello", "aloha"}, false, false, "aloha World"}, 258 {"Case-insensitive and Literal - Miss", "Hello World", &replacePair{"halo", "aloha"}, true, false, "Hello World"}, 259 {"Case-insensitive and Literal - Hit", "Hello World", &replacePair{"hello", "aloha"}, true, false, "aloha World"}, 260 {"Case-sensitive and Imitation - Miss", "Hello World", &replacePair{"hello", "aloha"}, false, true, "Hello World"}, 261 {"Case-sensitive and Imitation - Hit", "Hello World", &replacePair{"Hello", "aloha"}, false, true, "Aloha World"}, 262 {"Case-insensitive and Imitation - Miss", "Hello World", &replacePair{"halo", "aloha"}, true, true, "Hello World"}, 263 {"Case-insensitive and Imitation - Hit", "Hello World", &replacePair{"hello", "aloha"}, true, true, "Aloha World"}, 264 } 265 for _, tt := range tests { 266 t.Run(tt.name, func(t *testing.T) { 267 if got := replaceSingleString(tt.s, tt.rp, tt.ignoreCase, tt.imitateOld); got != tt.want { 268 t.Errorf("replaceSingleString() = %v, want %v", got, tt.want) 269 } 270 }) 271 } 272 } 273 274 func Test_imitateString(t *testing.T) { 275 tests := []struct { 276 name string 277 src string 278 dest string 279 want string 280 }{ 281 {"Empty", "", "", ""}, 282 {"Empty old", "", "Aloha", "Aloha"}, 283 {"Empty new", "Aloha", "", ""}, 284 {"Lower case", "aloha", "HELLO", "hello"}, 285 {"Upper case", "ALOHA", "hello", "HELLO"}, 286 {"Title case", "Aloha", "hello", "Hello"}, 287 {"Title words 1", "Aloha", "hello world", "Hello World"}, 288 {"Title words 2", "Aloha", "good-job", "Good-Job"}, 289 {"Title words 3", "Aloha", "loco.moco", "Loco.Moco"}, 290 } 291 for _, tt := range tests { 292 t.Run(tt.name, func(t *testing.T) { 293 if got := imitateString(tt.src, tt.dest); got != tt.want { 294 t.Errorf("imitateString() = %v, want %v", got, tt.want) 295 } 296 }) 297 } 298 } 299 300 func Test_getStringCaseType(t *testing.T) { 301 tests := []struct { 302 name string 303 s string 304 want stringCaseType 305 }{ 306 {"Empty", "", stringCaseMisc}, 307 {"Single Lower", "a", stringCaseLower}, 308 {"Word Lower", "aloha", stringCaseLower}, 309 {"Single Upper", "A", stringCaseUpper}, 310 {"Word Upper", "ALOHA", stringCaseUpper}, 311 {"One Word Title", "Aloha", stringCaseTitle}, 312 {"Two Words Title", "Aloha World", stringCaseTitle}, 313 {"Dashed Title", "Aloha-World", stringCaseTitle}, 314 {"Mixed Word 1", "AlohA", stringCaseMisc}, 315 {"Mixed Word 2", "ALOHa", stringCaseMisc}, 316 {"Mixed Word 3", "alohA", stringCaseMisc}, 317 {"Mixed Title 1", "Aloha WORLD", stringCaseMisc}, 318 {"Mixed Title 2", "Aloha world", stringCaseMisc}, 319 {"Mixed Title 3", "aloha World", stringCaseMisc}, 320 {"Mixed Title 4", "ALOHA World", stringCaseMisc}, 321 {"Mixed Title 5", "AlOha World", stringCaseMisc}, 322 {"Mixed Title 6", "ALOHA world", stringCaseMisc}, 323 {"Non-word", "123$%^", stringCaseMisc}, 324 } 325 for _, tt := range tests { 326 t.Run(tt.name, func(t *testing.T) { 327 if got := getStringCaseType(tt.s); got != tt.want { 328 t.Errorf("getStringCaseType(%q) = %v, want %v", tt.s, got, tt.want) 329 } 330 }) 331 } 332 }