glob_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package glob
  2. import "testing"
  3. func TestGlob_EqualityAndEmpty(t *testing.T) {
  4. cases := []struct {
  5. name string
  6. pattern string
  7. subj string
  8. want bool
  9. }{
  10. {"exact match", "hello", "hello", true},
  11. {"exact mismatch", "hello", "hell", false},
  12. {"empty pattern and subject", "", "", true},
  13. {"empty pattern with non-empty subject", "", "x", false},
  14. {"pattern star matches empty", "*", "", true},
  15. {"pattern star matches anything", "*", "anything at all", true},
  16. }
  17. for _, tc := range cases {
  18. t.Run(tc.name, func(t *testing.T) {
  19. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  20. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  21. }
  22. })
  23. }
  24. }
  25. func TestGlob_LeadingAndTrailing(t *testing.T) {
  26. cases := []struct {
  27. name string
  28. pattern string
  29. subj string
  30. want bool
  31. }{
  32. {"prefix match - minimal", "foo*", "foo", true},
  33. {"prefix match - extended", "foo*", "foobar", true},
  34. {"prefix mismatch - not at start", "foo*", "xfoo", false},
  35. {"suffix match - minimal", "*foo", "foo", true},
  36. {"suffix match - extended", "*foo", "xfoo", true},
  37. {"suffix mismatch - not at end", "*foo", "foox", false},
  38. {"contains match", "*foo*", "barfoobaz", true},
  39. {"contains mismatch - missing needle", "*foo*", "f", false},
  40. }
  41. for _, tc := range cases {
  42. t.Run(tc.name, func(t *testing.T) {
  43. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  44. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  45. }
  46. })
  47. }
  48. }
  49. func TestGlob_MiddleAndOrder(t *testing.T) {
  50. cases := []struct {
  51. name string
  52. pattern string
  53. subj string
  54. want bool
  55. }{
  56. {"middle wildcard basic", "f*o", "fo", true},
  57. {"middle wildcard gap", "f*o", "fZZZo", true},
  58. {"middle wildcard requires start f", "f*o", "xfyo", false},
  59. {"order enforced across parts", "a*b*c*d", "axxbxxcxxd", true},
  60. {"order mismatch fails", "a*b*c*d", "abdc", false},
  61. {"must end with last part when no trailing *", "*foo*bar", "zzfooqqbar", true},
  62. {"failing when trailing chars remain", "*foo*bar", "zzfooqqbarzz", false},
  63. {"first part must start when no leading *", "foo*bar", "zzfooqqbar", false},
  64. {"works with overlapping content", "ab*ba", "ababa", true},
  65. {"needle not found fails", "foo*bar", "foobaz", false},
  66. }
  67. for _, tc := range cases {
  68. t.Run(tc.name, func(t *testing.T) {
  69. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  70. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  71. }
  72. })
  73. }
  74. }
  75. func TestGlob_ConsecutiveStarsAndEmptyParts(t *testing.T) {
  76. cases := []struct {
  77. name string
  78. pattern string
  79. subj string
  80. want bool
  81. }{
  82. {"double star matches anything", "**", "", true},
  83. {"double star matches anything non-empty", "**", "abc", true},
  84. {"consecutive stars behave like single", "a**b", "ab", true},
  85. {"consecutive stars with gaps", "a**b", "axxxb", true},
  86. {"consecutive stars + trailing star", "a**b*", "axxbzzz", true},
  87. {"consecutive stars still enforce anchors", "a**b", "xaBy", false},
  88. }
  89. for _, tc := range cases {
  90. t.Run(tc.name, func(t *testing.T) {
  91. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  92. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  93. }
  94. })
  95. }
  96. }
  97. func TestGlob_MaxPartsLimit(t *testing.T) {
  98. // Allowed: up to 4 '*' (5 parts)
  99. allowed := []struct {
  100. pattern string
  101. subj string
  102. want bool
  103. }{
  104. {"a*b*c*d*e", "axxbxxcxxdxxe", true}, // 4 stars -> 5 parts
  105. {"*a*b*c*d", "zzzaaaabbbcccddd", true},
  106. {"a*b*c*d*e", "abcde", true},
  107. {"a*b*c*d*e", "abxdxe", false}, // missing 'c' should fail
  108. }
  109. for _, tc := range allowed {
  110. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  111. t.Fatalf("allowed pattern Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  112. }
  113. }
  114. // Disallowed: 5 '*' (6 parts) -> always false by complexity check
  115. disallowed := []struct {
  116. pattern string
  117. subj string
  118. }{
  119. {"a*b*c*d*e*f", "aXXbYYcZZdQQeRRf"},
  120. {"*a*b*c*d*e*", "abcdef"},
  121. {"******", "anything"}, // 6 stars -> 7 parts
  122. }
  123. for _, tc := range disallowed {
  124. if got := Glob(tc.pattern, tc.subj); got {
  125. t.Fatalf("disallowed pattern should fail Glob(%q,%q) = %v, want false", tc.pattern, tc.subj, got)
  126. }
  127. }
  128. }
  129. func TestGlob_CaseSensitivity(t *testing.T) {
  130. cases := []struct {
  131. pattern string
  132. subj string
  133. want bool
  134. }{
  135. {"FOO*", "foo", false},
  136. {"*Bar", "bar", false},
  137. {"Foo*Bar", "FooZZZBar", true},
  138. }
  139. for _, tc := range cases {
  140. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  141. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  142. }
  143. }
  144. }
  145. func TestGlob_EmptySubjectInteractions(t *testing.T) {
  146. cases := []struct {
  147. pattern string
  148. subj string
  149. want bool
  150. }{
  151. {"*a", "", false},
  152. {"a*", "", false},
  153. {"**", "", true},
  154. {"*", "", true},
  155. }
  156. for _, tc := range cases {
  157. if got := Glob(tc.pattern, tc.subj); got != tc.want {
  158. t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
  159. }
  160. }
  161. }
  162. func BenchmarkGlob(b *testing.B) {
  163. patterns := []string{
  164. "*", "*foo*", "foo*bar", "a*b*c*d*e", "a**b*", "*needle*end",
  165. }
  166. subjects := []string{
  167. "", "foo", "barfoo", "foobarbaz", "axxbxxcxxdxxe", "zzfooqqbarzz",
  168. "lorem ipsum dolor sit amet, consectetur adipiscing elit",
  169. }
  170. for _, p := range patterns {
  171. for _, s := range subjects {
  172. b.Run(p+"::"+s, func(b *testing.B) {
  173. for i := 0; i < b.N; i++ {
  174. _ = Glob(p, s)
  175. }
  176. })
  177. }
  178. }
  179. }