github.com/fufuok/utils@v1.0.10/xjson/match/match_test.go (about) 1 package match 2 3 import ( 4 "fmt" 5 "math/rand" 6 "strings" 7 "testing" 8 "time" 9 "unicode/utf8" 10 ) 11 12 func TestMatch(t *testing.T) { 13 if !Match("hello world", "hello world") { 14 t.Fatal("fail") 15 } 16 if Match("hello world", "jello world") { 17 t.Fatal("fail") 18 } 19 if !Match("hello world", "hello*") { 20 t.Fatal("fail") 21 } 22 if Match("hello world", "jello*") { 23 t.Fatal("fail") 24 } 25 if !Match("hello world", "hello?world") { 26 t.Fatal("fail") 27 } 28 if Match("hello world", "jello?world") { 29 t.Fatal("fail") 30 } 31 if !Match("hello world", "he*o?world") { 32 t.Fatal("fail") 33 } 34 if !Match("hello world", "he*o?wor*") { 35 t.Fatal("fail") 36 } 37 if !Match("hello world", "he*o?*r*") { 38 t.Fatal("fail") 39 } 40 if !Match("hello*world", `hello\*world`) { 41 t.Fatal("fail") 42 } 43 if !Match("he解lo*world", `he解lo\*world`) { 44 t.Fatal("fail") 45 } 46 if !Match("的情况下解析一个", "*") { 47 t.Fatal("fail") 48 } 49 if !Match("的情况下解析一个", "*况下*") { 50 t.Fatal("fail") 51 } 52 if !Match("的情况下解析一个", "*况?*") { 53 t.Fatal("fail") 54 } 55 if !Match("的情况下解析一个", "的情况?解析一个") { 56 t.Fatal("fail") 57 } 58 if Match("hello world\\", "hello world\\") { 59 t.Fatal("fail") 60 } 61 } 62 63 // TestWildcardMatch - Tests validate the logic of wild card matching. 64 // `WildcardMatch` supports '*' and '?' wildcards. 65 // Sample usage: In resource matching for folder policy validation. 66 func TestWildcardMatch(t *testing.T) { 67 testCases := []struct { 68 pattern string 69 text string 70 matched bool 71 }{ 72 // Test case - 1. 73 // Test case with pattern containing key name with a prefix. Should accept the same text without a "*". 74 { 75 pattern: "my-folder/oo*", 76 text: "my-folder/oo", 77 matched: true, 78 }, 79 // Test case - 2. 80 // Test case with "*" at the end of the pattern. 81 { 82 pattern: "my-folder/In*", 83 text: "my-folder/India/Karnataka/", 84 matched: true, 85 }, 86 // Test case - 3. 87 // Test case with prefixes shuffled. 88 // This should fail. 89 { 90 pattern: "my-folder/In*", 91 text: "my-folder/Karnataka/India/", 92 matched: false, 93 }, 94 // Test case - 4. 95 // Test case with text expanded to the wildcards in the pattern. 96 { 97 pattern: "my-folder/In*/Ka*/Ban", 98 text: "my-folder/India/Karnataka/Ban", 99 matched: true, 100 }, 101 // Test case - 5. 102 // Test case with the keyname part is repeated as prefix several times. 103 // This is valid. 104 { 105 pattern: "my-folder/In*/Ka*/Ban", 106 text: "my-folder/India/Karnataka/Ban/Ban/Ban/Ban/Ban", 107 matched: true, 108 }, 109 // Test case - 6. 110 // Test case to validate that `*` can be expanded into multiple prefixes. 111 { 112 pattern: "my-folder/In*/Ka*/Ban", 113 text: "my-folder/India/Karnataka/Area1/Area2/Area3/Ban", 114 matched: true, 115 }, 116 // Test case - 7. 117 // Test case to validate that `*` can be expanded into multiple prefixes. 118 { 119 pattern: "my-folder/In*/Ka*/Ban", 120 text: "my-folder/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban", 121 matched: true, 122 }, 123 // Test case - 8. 124 // Test case where the keyname part of the pattern is expanded in the text. 125 { 126 pattern: "my-folder/In*/Ka*/Ban", 127 text: "my-folder/India/Karnataka/Bangalore", 128 matched: false, 129 }, 130 // Test case - 9. 131 // Test case with prefixes and wildcard expanded for all "*". 132 { 133 pattern: "my-folder/In*/Ka*/Ban*", 134 text: "my-folder/India/Karnataka/Bangalore", 135 matched: true, 136 }, 137 // Test case - 10. 138 // Test case with keyname part being a wildcard in the pattern. 139 { 140 pattern: "my-folder/*", 141 text: "my-folder/India", 142 matched: true, 143 }, 144 // Test case - 11. 145 { 146 pattern: "my-folder/oo*", 147 text: "my-folder/odo", 148 matched: false, 149 }, 150 151 // Test case with pattern containing wildcard '?'. 152 // Test case - 12. 153 // "my-folder?/" matches "my-folder1/", "my-folder2/", "my-folder3" etc... 154 // doesn't match "myfolder/". 155 { 156 pattern: "my-folder?/abc*", 157 text: "myfolder/abc", 158 matched: false, 159 }, 160 // Test case - 13. 161 { 162 pattern: "my-folder?/abc*", 163 text: "my-folder1/abc", 164 matched: true, 165 }, 166 // Test case - 14. 167 { 168 pattern: "my-?-folder/abc*", 169 text: "my--folder/abc", 170 matched: false, 171 }, 172 // Test case - 15. 173 { 174 pattern: "my-?-folder/abc*", 175 text: "my-1-folder/abc", 176 matched: true, 177 }, 178 // Test case - 16. 179 { 180 pattern: "my-?-folder/abc*", 181 text: "my-k-folder/abc", 182 matched: true, 183 }, 184 // Test case - 17. 185 { 186 pattern: "my??folder/abc*", 187 text: "myfolder/abc", 188 matched: false, 189 }, 190 // Test case - 18. 191 { 192 pattern: "my??folder/abc*", 193 text: "my4afolder/abc", 194 matched: true, 195 }, 196 // Test case - 19. 197 { 198 pattern: "my-folder?abc*", 199 text: "my-folder/abc", 200 matched: true, 201 }, 202 // Test case 20-21. 203 // '?' matches '/' too. (works with s3). 204 // This is because the namespace is considered flat. 205 // "abc?efg" matches both "abcdefg" and "abc/efg". 206 { 207 pattern: "my-folder/abc?efg", 208 text: "my-folder/abcdefg", 209 matched: true, 210 }, 211 { 212 pattern: "my-folder/abc?efg", 213 text: "my-folder/abc/efg", 214 matched: true, 215 }, 216 // Test case - 22. 217 { 218 pattern: "my-folder/abc????", 219 text: "my-folder/abc", 220 matched: false, 221 }, 222 // Test case - 23. 223 { 224 pattern: "my-folder/abc????", 225 text: "my-folder/abcde", 226 matched: false, 227 }, 228 // Test case - 24. 229 { 230 pattern: "my-folder/abc????", 231 text: "my-folder/abcdefg", 232 matched: true, 233 }, 234 // Test case 25-26. 235 // test case with no '*'. 236 { 237 pattern: "my-folder/abc?", 238 text: "my-folder/abc", 239 matched: false, 240 }, 241 { 242 pattern: "my-folder/abc?", 243 text: "my-folder/abcd", 244 matched: true, 245 }, 246 { 247 pattern: "my-folder/abc?", 248 text: "my-folder/abcde", 249 matched: false, 250 }, 251 // Test case 27. 252 { 253 pattern: "my-folder/mnop*?", 254 text: "my-folder/mnop", 255 matched: false, 256 }, 257 // Test case 28. 258 { 259 pattern: "my-folder/mnop*?", 260 text: "my-folder/mnopqrst/mnopqr", 261 matched: true, 262 }, 263 // Test case 29. 264 { 265 pattern: "my-folder/mnop*?", 266 text: "my-folder/mnopqrst/mnopqrs", 267 matched: true, 268 }, 269 // Test case 30. 270 { 271 pattern: "my-folder/mnop*?", 272 text: "my-folder/mnop", 273 matched: false, 274 }, 275 // Test case 31. 276 { 277 pattern: "my-folder/mnop*?", 278 text: "my-folder/mnopq", 279 matched: true, 280 }, 281 // Test case 32. 282 { 283 pattern: "my-folder/mnop*?", 284 text: "my-folder/mnopqr", 285 matched: true, 286 }, 287 // Test case 33. 288 { 289 pattern: "my-folder/mnop*?and", 290 text: "my-folder/mnopqand", 291 matched: true, 292 }, 293 // Test case 34. 294 { 295 pattern: "my-folder/mnop*?and", 296 text: "my-folder/mnopand", 297 matched: false, 298 }, 299 // Test case 35. 300 { 301 pattern: "my-folder/mnop*?and", 302 text: "my-folder/mnopqand", 303 matched: true, 304 }, 305 // Test case 36. 306 { 307 pattern: "my-folder/mnop*?", 308 text: "my-folder/mn", 309 matched: false, 310 }, 311 // Test case 37. 312 { 313 pattern: "my-folder/mnop*?", 314 text: "my-folder/mnopqrst/mnopqrs", 315 matched: true, 316 }, 317 // Test case 38. 318 { 319 pattern: "my-folder/mnop*??", 320 text: "my-folder/mnopqrst", 321 matched: true, 322 }, 323 // Test case 39. 324 { 325 pattern: "my-folder/mnop*qrst", 326 text: "my-folder/mnopabcdegqrst", 327 matched: true, 328 }, 329 // Test case 40. 330 { 331 pattern: "my-folder/mnop*?and", 332 text: "my-folder/mnopqand", 333 matched: true, 334 }, 335 // Test case 41. 336 { 337 pattern: "my-folder/mnop*?and", 338 text: "my-folder/mnopand", 339 matched: false, 340 }, 341 // Test case 42. 342 { 343 pattern: "my-folder/mnop*?and?", 344 text: "my-folder/mnopqanda", 345 matched: true, 346 }, 347 // Test case 43. 348 { 349 pattern: "my-folder/mnop*?and", 350 text: "my-folder/mnopqanda", 351 matched: false, 352 }, 353 // Test case 44. 354 355 { 356 pattern: "my-?-folder/abc*", 357 text: "my-folder/mnopqanda", 358 matched: false, 359 }, 360 } 361 // Iterating over the test cases, call the function under test and asert the output. 362 for i, testCase := range testCases { 363 // println("=====", i+1, "=====") 364 actualResult := Match(testCase.text, testCase.pattern) 365 if testCase.matched != actualResult { 366 t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult) 367 } 368 } 369 } 370 371 func TestRandomInput(t *testing.T) { 372 rand.Seed(time.Now().UnixNano()) 373 b1 := make([]byte, 100) 374 b2 := make([]byte, 100) 375 for i := 0; i < 1000000; i++ { 376 if _, err := rand.Read(b1); err != nil { 377 t.Fatal(err) 378 } 379 if _, err := rand.Read(b2); err != nil { 380 t.Fatal(err) 381 } 382 Match(string(b1), string(b2)) 383 } 384 } 385 386 func testAllowable(pattern, exmin, exmax string) error { 387 min, max := Allowable(pattern) 388 if min != exmin || max != exmax { 389 return fmt.Errorf("expected '%v'/'%v', got '%v'/'%v'", 390 exmin, exmax, min, max) 391 } 392 return nil 393 } 394 395 func TestAllowable(t *testing.T) { 396 if err := testAllowable("*", "", ""); err != nil { 397 t.Fatal(err) 398 } 399 if err := testAllowable("hell*", "hell", "helm"); err != nil { 400 t.Fatal(err) 401 } 402 if err := testAllowable("hell?", "hell"+string(rune(0)), "hell"+string(utf8.MaxRune)); err != nil { 403 t.Fatal(err) 404 } 405 if err := testAllowable("h解析ell*", "h解析ell", "h解析elm"); err != nil { 406 t.Fatal(err) 407 } 408 if err := testAllowable("h解*ell*", "h解", "h觤"); err != nil { 409 t.Fatal(err) 410 } 411 } 412 413 func TestIsPattern(t *testing.T) { 414 patterns := []string{ 415 "*", "hello*", "hello*world", "*world", 416 "?", "hello?", "hello?world", "?world", 417 } 418 nonPatterns := []string{ 419 "", "hello", 420 } 421 for _, pattern := range patterns { 422 if !IsPattern(pattern) { 423 t.Fatalf("expected true") 424 } 425 } 426 427 for _, s := range nonPatterns { 428 if IsPattern(s) { 429 t.Fatalf("expected false") 430 } 431 } 432 } 433 434 func BenchmarkAscii(t *testing.B) { 435 for i := 0; i < t.N; i++ { 436 if !Match("hello", "hello") { 437 t.Fatal("fail") 438 } 439 } 440 } 441 442 func BenchmarkUnicode(t *testing.B) { 443 for i := 0; i < t.N; i++ { 444 if !Match("h情llo", "h情llo") { 445 t.Fatal("fail") 446 } 447 } 448 } 449 450 func TestLotsaStars(t *testing.T) { 451 // This tests that a pattern with lots of stars will complete quickly. 452 var str, pat string 453 454 str = `,**,,**,**,**,**,**,**,` 455 pat = `,**********************************************{**",**,,**,**,` + 456 `**,**,"",**,**,**,**,**,**,**,**,**,**]` 457 Match(pat, str) 458 459 str = strings.Replace(str, ",", "情", -1) 460 pat = strings.Replace(pat, ",", "情", -1) 461 Match(pat, str) 462 463 str = strings.Repeat("hello", 100) 464 pat = `*?*?*?*?*?*?*""` 465 Match(str, pat) 466 467 str = `*?**?**?**?**?**?***?**?**?**?**?*""` 468 pat = `*?*?*?*?*?*?**?**?**?**?**?**?**?*""` 469 Match(str, pat) 470 } 471 472 func TestLimit(t *testing.T) { 473 var str, pat string 474 str = `,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` 475 pat = `*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"*,*` 476 _, stopped := MatchLimit(str, pat, 100) 477 if !stopped { 478 t.Fatal("expected true") 479 } 480 481 match, _ := MatchLimit(str, "*", 100) 482 if !match { 483 t.Fatal("expected true") 484 } 485 match, _ = MatchLimit(str, "*,*", 100) 486 if !match { 487 t.Fatal("expected true") 488 } 489 } 490 491 func TestSuffix(t *testing.T) { 492 sufmatch := func(t *testing.T, str, pat string, exstr, expat string, exok bool) { 493 t.Helper() 494 rstr, rpat, rok := matchTrimSuffix(str, pat) 495 if rstr != exstr || rpat != expat || rok != exok { 496 t.Fatalf( 497 "for '%s' '%s', expected '%s' '%s' '%t', got '%s' '%s' '%t'", 498 str, pat, exstr, expat, exok, rstr, rpat, rok) 499 } 500 } 501 sufmatch(t, "hello", "*hello", "", "*", true) 502 sufmatch(t, "jello", "*hello", "j", "*h", false) 503 sufmatch(t, "jello", "*?ello", "", "*", true) 504 sufmatch(t, "jello", "*\\?ello", "j", "*\\?", false) 505 sufmatch(t, "?ello", "*\\?ello", "", "*", true) 506 sufmatch(t, "?ello", "*\\?ello", "", "*", true) 507 sufmatch(t, "f?ello", "*\\?ello", "f", "*", true) 508 sufmatch(t, "f?ello", "**\\?ello", "f", "**", true) 509 sufmatch(t, "f?el*o", "**\\?el\\*o", "f", "**", true) 510 }