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  }