| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- package glob
- import "testing"
- func TestGlob_EqualityAndEmpty(t *testing.T) {
- cases := []struct {
- name string
- pattern string
- subj string
- want bool
- }{
- {"exact match", "hello", "hello", true},
- {"exact mismatch", "hello", "hell", false},
- {"empty pattern and subject", "", "", true},
- {"empty pattern with non-empty subject", "", "x", false},
- {"pattern star matches empty", "*", "", true},
- {"pattern star matches anything", "*", "anything at all", true},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- })
- }
- }
- func TestGlob_LeadingAndTrailing(t *testing.T) {
- cases := []struct {
- name string
- pattern string
- subj string
- want bool
- }{
- {"prefix match - minimal", "foo*", "foo", true},
- {"prefix match - extended", "foo*", "foobar", true},
- {"prefix mismatch - not at start", "foo*", "xfoo", false},
- {"suffix match - minimal", "*foo", "foo", true},
- {"suffix match - extended", "*foo", "xfoo", true},
- {"suffix mismatch - not at end", "*foo", "foox", false},
- {"contains match", "*foo*", "barfoobaz", true},
- {"contains mismatch - missing needle", "*foo*", "f", false},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- })
- }
- }
- func TestGlob_MiddleAndOrder(t *testing.T) {
- cases := []struct {
- name string
- pattern string
- subj string
- want bool
- }{
- {"middle wildcard basic", "f*o", "fo", true},
- {"middle wildcard gap", "f*o", "fZZZo", true},
- {"middle wildcard requires start f", "f*o", "xfyo", false},
- {"order enforced across parts", "a*b*c*d", "axxbxxcxxd", true},
- {"order mismatch fails", "a*b*c*d", "abdc", false},
- {"must end with last part when no trailing *", "*foo*bar", "zzfooqqbar", true},
- {"failing when trailing chars remain", "*foo*bar", "zzfooqqbarzz", false},
- {"first part must start when no leading *", "foo*bar", "zzfooqqbar", false},
- {"works with overlapping content", "ab*ba", "ababa", true},
- {"needle not found fails", "foo*bar", "foobaz", false},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- })
- }
- }
- func TestGlob_ConsecutiveStarsAndEmptyParts(t *testing.T) {
- cases := []struct {
- name string
- pattern string
- subj string
- want bool
- }{
- {"double star matches anything", "**", "", true},
- {"double star matches anything non-empty", "**", "abc", true},
- {"consecutive stars behave like single", "a**b", "ab", true},
- {"consecutive stars with gaps", "a**b", "axxxb", true},
- {"consecutive stars + trailing star", "a**b*", "axxbzzz", true},
- {"consecutive stars still enforce anchors", "a**b", "xaBy", false},
- }
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- })
- }
- }
- func TestGlob_MaxPartsLimit(t *testing.T) {
- // Allowed: up to 4 '*' (5 parts)
- allowed := []struct {
- pattern string
- subj string
- want bool
- }{
- {"a*b*c*d*e", "axxbxxcxxdxxe", true}, // 4 stars -> 5 parts
- {"*a*b*c*d", "zzzaaaabbbcccddd", true},
- {"a*b*c*d*e", "abcde", true},
- {"a*b*c*d*e", "abxdxe", false}, // missing 'c' should fail
- }
- for _, tc := range allowed {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("allowed pattern Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- }
- // Disallowed: 5 '*' (6 parts) -> always false by complexity check
- disallowed := []struct {
- pattern string
- subj string
- }{
- {"a*b*c*d*e*f", "aXXbYYcZZdQQeRRf"},
- {"*a*b*c*d*e*", "abcdef"},
- {"******", "anything"}, // 6 stars -> 7 parts
- }
- for _, tc := range disallowed {
- if got := Glob(tc.pattern, tc.subj); got {
- t.Fatalf("disallowed pattern should fail Glob(%q,%q) = %v, want false", tc.pattern, tc.subj, got)
- }
- }
- }
- func TestGlob_CaseSensitivity(t *testing.T) {
- cases := []struct {
- pattern string
- subj string
- want bool
- }{
- {"FOO*", "foo", false},
- {"*Bar", "bar", false},
- {"Foo*Bar", "FooZZZBar", true},
- }
- for _, tc := range cases {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- }
- }
- func TestGlob_EmptySubjectInteractions(t *testing.T) {
- cases := []struct {
- pattern string
- subj string
- want bool
- }{
- {"*a", "", false},
- {"a*", "", false},
- {"**", "", true},
- {"*", "", true},
- }
- for _, tc := range cases {
- if got := Glob(tc.pattern, tc.subj); got != tc.want {
- t.Fatalf("Glob(%q,%q) = %v, want %v", tc.pattern, tc.subj, got, tc.want)
- }
- }
- }
- func BenchmarkGlob(b *testing.B) {
- patterns := []string{
- "*", "*foo*", "foo*bar", "a*b*c*d*e", "a**b*", "*needle*end",
- }
- subjects := []string{
- "", "foo", "barfoo", "foobarbaz", "axxbxxcxxdxxe", "zzfooqqbarzz",
- "lorem ipsum dolor sit amet, consectetur adipiscing elit",
- }
- for _, p := range patterns {
- for _, s := range subjects {
- b.Run(p+"::"+s, func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = Glob(p, s)
- }
- })
- }
- }
- }
|