github.com/zhangdapeng520/zdpgo_json@v0.1.5/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  		{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  }
   369  func 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  }
   383  func 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  }
   391  func 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  
   409  func 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  }
   429  func 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  
   437  func 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  
   445  func 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  
   467  func 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  
   486  func 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  }