1package match 2 3import ( 4 "fmt" 5 "math/rand" 6 "strings" 7 "testing" 8 "time" 9 "unicode/utf8" 10) 11 12func 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. 66func 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 {pattern: "my-folder/*", 140 text: "my-folder/India", 141 matched: true, 142 }, 143 // Test case - 11. 144 { 145 pattern: "my-folder/oo*", 146 text: "my-folder/odo", 147 matched: false, 148 }, 149 150 // Test case with pattern containing wildcard '?'. 151 // Test case - 12. 152 // "my-folder?/" matches "my-folder1/", "my-folder2/", "my-folder3" etc... 153 // doesn't match "myfolder/". 154 { 155 pattern: "my-folder?/abc*", 156 text: "myfolder/abc", 157 matched: false, 158 }, 159 // Test case - 13. 160 { 161 pattern: "my-folder?/abc*", 162 text: "my-folder1/abc", 163 matched: true, 164 }, 165 // Test case - 14. 166 { 167 pattern: "my-?-folder/abc*", 168 text: "my--folder/abc", 169 matched: false, 170 }, 171 // Test case - 15. 172 { 173 pattern: "my-?-folder/abc*", 174 text: "my-1-folder/abc", 175 matched: true, 176 }, 177 // Test case - 16. 178 { 179 pattern: "my-?-folder/abc*", 180 text: "my-k-folder/abc", 181 matched: true, 182 }, 183 // Test case - 17. 184 { 185 pattern: "my??folder/abc*", 186 text: "myfolder/abc", 187 matched: false, 188 }, 189 // Test case - 18. 190 { 191 pattern: "my??folder/abc*", 192 text: "my4afolder/abc", 193 matched: true, 194 }, 195 // Test case - 19. 196 { 197 pattern: "my-folder?abc*", 198 text: "my-folder/abc", 199 matched: true, 200 }, 201 // Test case 20-21. 202 // '?' matches '/' too. (works with s3). 203 // This is because the namespace is considered flat. 204 // "abc?efg" matches both "abcdefg" and "abc/efg". 205 { 206 pattern: "my-folder/abc?efg", 207 text: "my-folder/abcdefg", 208 matched: true, 209 }, 210 { 211 pattern: "my-folder/abc?efg", 212 text: "my-folder/abc/efg", 213 matched: true, 214 }, 215 // Test case - 22. 216 { 217 pattern: "my-folder/abc????", 218 text: "my-folder/abc", 219 matched: false, 220 }, 221 // Test case - 23. 222 { 223 pattern: "my-folder/abc????", 224 text: "my-folder/abcde", 225 matched: false, 226 }, 227 // Test case - 24. 228 { 229 pattern: "my-folder/abc????", 230 text: "my-folder/abcdefg", 231 matched: true, 232 }, 233 // Test case 25-26. 234 // test case with no '*'. 235 { 236 pattern: "my-folder/abc?", 237 text: "my-folder/abc", 238 matched: false, 239 }, 240 { 241 pattern: "my-folder/abc?", 242 text: "my-folder/abcd", 243 matched: true, 244 }, 245 { 246 pattern: "my-folder/abc?", 247 text: "my-folder/abcde", 248 matched: false, 249 }, 250 // Test case 27. 251 { 252 pattern: "my-folder/mnop*?", 253 text: "my-folder/mnop", 254 matched: false, 255 }, 256 // Test case 28. 257 { 258 pattern: "my-folder/mnop*?", 259 text: "my-folder/mnopqrst/mnopqr", 260 matched: true, 261 }, 262 // Test case 29. 263 { 264 pattern: "my-folder/mnop*?", 265 text: "my-folder/mnopqrst/mnopqrs", 266 matched: true, 267 }, 268 // Test case 30. 269 { 270 pattern: "my-folder/mnop*?", 271 text: "my-folder/mnop", 272 matched: false, 273 }, 274 // Test case 31. 275 { 276 pattern: "my-folder/mnop*?", 277 text: "my-folder/mnopq", 278 matched: true, 279 }, 280 // Test case 32. 281 { 282 pattern: "my-folder/mnop*?", 283 text: "my-folder/mnopqr", 284 matched: true, 285 }, 286 // Test case 33. 287 { 288 pattern: "my-folder/mnop*?and", 289 text: "my-folder/mnopqand", 290 matched: true, 291 }, 292 // Test case 34. 293 { 294 pattern: "my-folder/mnop*?and", 295 text: "my-folder/mnopand", 296 matched: false, 297 }, 298 // Test case 35. 299 { 300 pattern: "my-folder/mnop*?and", 301 text: "my-folder/mnopqand", 302 matched: true, 303 }, 304 // Test case 36. 305 { 306 pattern: "my-folder/mnop*?", 307 text: "my-folder/mn", 308 matched: false, 309 }, 310 // Test case 37. 311 { 312 pattern: "my-folder/mnop*?", 313 text: "my-folder/mnopqrst/mnopqrs", 314 matched: true, 315 }, 316 // Test case 38. 317 { 318 pattern: "my-folder/mnop*??", 319 text: "my-folder/mnopqrst", 320 matched: true, 321 }, 322 // Test case 39. 323 { 324 pattern: "my-folder/mnop*qrst", 325 text: "my-folder/mnopabcdegqrst", 326 matched: true, 327 }, 328 // Test case 40. 329 { 330 pattern: "my-folder/mnop*?and", 331 text: "my-folder/mnopqand", 332 matched: true, 333 }, 334 // Test case 41. 335 { 336 pattern: "my-folder/mnop*?and", 337 text: "my-folder/mnopand", 338 matched: false, 339 }, 340 // Test case 42. 341 { 342 pattern: "my-folder/mnop*?and?", 343 text: "my-folder/mnopqanda", 344 matched: true, 345 }, 346 // Test case 43. 347 { 348 pattern: "my-folder/mnop*?and", 349 text: "my-folder/mnopqanda", 350 matched: false, 351 }, 352 // Test case 44. 353 354 { 355 pattern: "my-?-folder/abc*", 356 text: "my-folder/mnopqanda", 357 matched: false, 358 }, 359 } 360 // Iterating over the test cases, call the function under test and asert the output. 361 for i, testCase := range testCases { 362 // println("=====", i+1, "=====") 363 actualResult := Match(testCase.text, testCase.pattern) 364 if testCase.matched != actualResult { 365 t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult) 366 } 367 } 368} 369func TestRandomInput(t *testing.T) { 370 rand.Seed(time.Now().UnixNano()) 371 b1 := make([]byte, 100) 372 b2 := make([]byte, 100) 373 for i := 0; i < 1000000; i++ { 374 if _, err := rand.Read(b1); err != nil { 375 t.Fatal(err) 376 } 377 if _, err := rand.Read(b2); err != nil { 378 t.Fatal(err) 379 } 380 Match(string(b1), string(b2)) 381 } 382} 383func testAllowable(pattern, exmin, exmax string) error { 384 min, max := Allowable(pattern) 385 if min != exmin || max != exmax { 386 return fmt.Errorf("expected '%v'/'%v', got '%v'/'%v'", 387 exmin, exmax, min, max) 388 } 389 return nil 390} 391func TestAllowable(t *testing.T) { 392 if err := testAllowable("*", "", ""); err != nil { 393 t.Fatal(err) 394 } 395 if err := testAllowable("hell*", "hell", "helm"); err != nil { 396 t.Fatal(err) 397 } 398 if err := testAllowable("hell?", "hell"+string(rune(0)), "hell"+string(utf8.MaxRune)); err != nil { 399 t.Fatal(err) 400 } 401 if err := testAllowable("h解析ell*", "h解析ell", "h解析elm"); err != nil { 402 t.Fatal(err) 403 } 404 if err := testAllowable("h解*ell*", "h解", "h觤"); err != nil { 405 t.Fatal(err) 406 } 407} 408 409func TestIsPattern(t *testing.T) { 410 patterns := []string{ 411 "*", "hello*", "hello*world", "*world", 412 "?", "hello?", "hello?world", "?world", 413 } 414 nonPatterns := []string{ 415 "", "hello", 416 } 417 for _, pattern := range patterns { 418 if !IsPattern(pattern) { 419 t.Fatalf("expected true") 420 } 421 } 422 423 for _, s := range nonPatterns { 424 if IsPattern(s) { 425 t.Fatalf("expected false") 426 } 427 } 428} 429func BenchmarkAscii(t *testing.B) { 430 for i := 0; i < t.N; i++ { 431 if !Match("hello", "hello") { 432 t.Fatal("fail") 433 } 434 } 435} 436 437func BenchmarkUnicode(t *testing.B) { 438 for i := 0; i < t.N; i++ { 439 if !Match("h情llo", "h情llo") { 440 t.Fatal("fail") 441 } 442 } 443} 444 445func TestLotsaStars(t *testing.T) { 446 // This tests that a pattern with lots of stars will complete quickly. 447 var str, pat string 448 449 str = `,**,,**,**,**,**,**,**,` 450 pat = `,**********************************************{**",**,,**,**,` + 451 `**,**,"",**,**,**,**,**,**,**,**,**,**]` 452 Match(pat, str) 453 454 str = strings.Replace(str, ",", "情", -1) 455 pat = strings.Replace(pat, ",", "情", -1) 456 Match(pat, str) 457 458 str = strings.Repeat("hello", 100) 459 pat = `*?*?*?*?*?*?*""` 460 Match(str, pat) 461 462 str = `*?**?**?**?**?**?***?**?**?**?**?*""` 463 pat = `*?*?*?*?*?*?**?**?**?**?**?**?**?*""` 464 Match(str, pat) 465} 466 467func TestLimit(t *testing.T) { 468 var str, pat string 469 str = `,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,` 470 pat = `*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*"*,*` 471 _, stopped := MatchLimit(str, pat, 100) 472 if !stopped { 473 t.Fatal("expected true") 474 } 475 476 match, _ := MatchLimit(str, "*", 100) 477 if !match { 478 t.Fatal("expected true") 479 } 480 match, _ = MatchLimit(str, "*,*", 100) 481 if !match { 482 t.Fatal("expected true") 483 } 484} 485 486func TestSuffix(t *testing.T) { 487 sufmatch := func(t *testing.T, str, pat string, exstr, expat string, exok bool) { 488 t.Helper() 489 rstr, rpat, rok := matchTrimSuffix(str, pat) 490 if rstr != exstr || rpat != expat || rok != exok { 491 t.Fatalf( 492 "for '%s' '%s', expected '%s' '%s' '%t', got '%s' '%s' '%t'", 493 str, pat, exstr, expat, exok, rstr, rpat, rok) 494 } 495 } 496 sufmatch(t, "hello", "*hello", "", "*", true) 497 sufmatch(t, "jello", "*hello", "j", "*h", false) 498 sufmatch(t, "jello", "*?ello", "", "*", true) 499 sufmatch(t, "jello", "*\\?ello", "j", "*\\?", false) 500 sufmatch(t, "?ello", "*\\?ello", "", "*", true) 501 sufmatch(t, "?ello", "*\\?ello", "", "*", true) 502 sufmatch(t, "f?ello", "*\\?ello", "f", "*", true) 503 sufmatch(t, "f?ello", "**\\?ello", "f", "**", true) 504 sufmatch(t, "f?el*o", "**\\?el\\*o", "f", "**", true) 505} 506