github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/strmatcher/matchergroup_mph_test.go (about)

     1  package strmatcher_test
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/v2fly/v2ray-core/v5/common"
     8  	. "github.com/v2fly/v2ray-core/v5/common/strmatcher"
     9  )
    10  
    11  func TestMphMatcherGroup(t *testing.T) {
    12  	cases1 := []struct {
    13  		pattern string
    14  		mType   Type
    15  		input   string
    16  		output  bool
    17  	}{
    18  		{
    19  			pattern: "v2fly.org",
    20  			mType:   Domain,
    21  			input:   "www.v2fly.org",
    22  			output:  true,
    23  		},
    24  		{
    25  			pattern: "v2fly.org",
    26  			mType:   Domain,
    27  			input:   "v2fly.org",
    28  			output:  true,
    29  		},
    30  		{
    31  			pattern: "v2fly.org",
    32  			mType:   Domain,
    33  			input:   "www.v3fly.org",
    34  			output:  false,
    35  		},
    36  		{
    37  			pattern: "v2fly.org",
    38  			mType:   Domain,
    39  			input:   "2fly.org",
    40  			output:  false,
    41  		},
    42  		{
    43  			pattern: "v2fly.org",
    44  			mType:   Domain,
    45  			input:   "xv2fly.org",
    46  			output:  false,
    47  		},
    48  		{
    49  			pattern: "v2fly.org",
    50  			mType:   Full,
    51  			input:   "v2fly.org",
    52  			output:  true,
    53  		},
    54  		{
    55  			pattern: "v2fly.org",
    56  			mType:   Full,
    57  			input:   "xv2fly.org",
    58  			output:  false,
    59  		},
    60  	}
    61  	for _, test := range cases1 {
    62  		mph := NewMphMatcherGroup()
    63  		matcher, err := test.mType.New(test.pattern)
    64  		common.Must(err)
    65  		common.Must(AddMatcherToGroup(mph, matcher, 0))
    66  		mph.Build()
    67  		if m := mph.MatchAny(test.input); m != test.output {
    68  			t.Error("unexpected output: ", m, " for test case ", test)
    69  		}
    70  	}
    71  	{
    72  		cases2Input := []struct {
    73  			pattern string
    74  			mType   Type
    75  		}{
    76  			{
    77  				pattern: "163.com",
    78  				mType:   Domain,
    79  			},
    80  			{
    81  				pattern: "m.126.com",
    82  				mType:   Full,
    83  			},
    84  			{
    85  				pattern: "3.com",
    86  				mType:   Full,
    87  			},
    88  		}
    89  		mph := NewMphMatcherGroup()
    90  		for _, test := range cases2Input {
    91  			matcher, err := test.mType.New(test.pattern)
    92  			common.Must(err)
    93  			common.Must(AddMatcherToGroup(mph, matcher, 0))
    94  		}
    95  		mph.Build()
    96  		cases2Output := []struct {
    97  			pattern string
    98  			res     bool
    99  		}{
   100  			{
   101  				pattern: "126.com",
   102  				res:     false,
   103  			},
   104  			{
   105  				pattern: "m.163.com",
   106  				res:     true,
   107  			},
   108  			{
   109  				pattern: "mm163.com",
   110  				res:     false,
   111  			},
   112  			{
   113  				pattern: "m.126.com",
   114  				res:     true,
   115  			},
   116  			{
   117  				pattern: "163.com",
   118  				res:     true,
   119  			},
   120  			{
   121  				pattern: "63.com",
   122  				res:     false,
   123  			},
   124  			{
   125  				pattern: "oogle.com",
   126  				res:     false,
   127  			},
   128  			{
   129  				pattern: "vvgoogle.com",
   130  				res:     false,
   131  			},
   132  		}
   133  		for _, test := range cases2Output {
   134  			if m := mph.MatchAny(test.pattern); m != test.res {
   135  				t.Error("unexpected output: ", m, " for test case ", test)
   136  			}
   137  		}
   138  	}
   139  	{
   140  		cases3Input := []struct {
   141  			pattern string
   142  			mType   Type
   143  		}{
   144  			{
   145  				pattern: "video.google.com",
   146  				mType:   Domain,
   147  			},
   148  			{
   149  				pattern: "gle.com",
   150  				mType:   Domain,
   151  			},
   152  		}
   153  		mph := NewMphMatcherGroup()
   154  		for _, test := range cases3Input {
   155  			matcher, err := test.mType.New(test.pattern)
   156  			common.Must(err)
   157  			common.Must(AddMatcherToGroup(mph, matcher, 0))
   158  		}
   159  		mph.Build()
   160  		cases3Output := []struct {
   161  			pattern string
   162  			res     bool
   163  		}{
   164  			{
   165  				pattern: "google.com",
   166  				res:     false,
   167  			},
   168  		}
   169  		for _, test := range cases3Output {
   170  			if m := mph.MatchAny(test.pattern); m != test.res {
   171  				t.Error("unexpected output: ", m, " for test case ", test)
   172  			}
   173  		}
   174  	}
   175  }
   176  
   177  // See https://github.com/v2fly/v2ray-core/issues/92#issuecomment-673238489
   178  func TestMphMatcherGroupAsIndexMatcher(t *testing.T) {
   179  	rules := []struct {
   180  		Type   Type
   181  		Domain string
   182  	}{
   183  		// Regex not supported by MphMatcherGroup
   184  		// {
   185  		// 	Type:   Regex,
   186  		// 	Domain: "apis\\.us$",
   187  		// },
   188  		// Substr not supported by MphMatcherGroup
   189  		// {
   190  		// 	Type:   Substr,
   191  		// 	Domain: "apis",
   192  		// },
   193  		{
   194  			Type:   Domain,
   195  			Domain: "googleapis.com",
   196  		},
   197  		{
   198  			Type:   Domain,
   199  			Domain: "com",
   200  		},
   201  		{
   202  			Type:   Full,
   203  			Domain: "www.baidu.com",
   204  		},
   205  		// Substr not supported by MphMatcherGroup, We add another matcher to preserve index
   206  		{
   207  			Type:   Domain,        // Substr,
   208  			Domain: "example.com", // "apis",
   209  		},
   210  		{
   211  			Type:   Domain,
   212  			Domain: "googleapis.com",
   213  		},
   214  		{
   215  			Type:   Full,
   216  			Domain: "fonts.googleapis.com",
   217  		},
   218  		{
   219  			Type:   Full,
   220  			Domain: "www.baidu.com",
   221  		},
   222  		{ // This matcher (index 10) is swapped with matcher (index 6) to test that full matcher takes high priority.
   223  			Type:   Full,
   224  			Domain: "example.com",
   225  		},
   226  		{
   227  			Type:   Domain,
   228  			Domain: "example.com",
   229  		},
   230  	}
   231  	cases := []struct {
   232  		Input  string
   233  		Output []uint32
   234  	}{
   235  		{
   236  			Input:  "www.baidu.com",
   237  			Output: []uint32{5, 9, 4},
   238  		},
   239  		{
   240  			Input:  "fonts.googleapis.com",
   241  			Output: []uint32{8, 3, 7, 4 /*2, 6*/},
   242  		},
   243  		{
   244  			Input:  "example.googleapis.com",
   245  			Output: []uint32{3, 7, 4 /*2, 6*/},
   246  		},
   247  		{
   248  			Input: "testapis.us",
   249  			// Output: []uint32{ /*2, 6*/ /*1,*/ },
   250  			Output: nil,
   251  		},
   252  		{
   253  			Input:  "example.com",
   254  			Output: []uint32{10, 6, 11, 4},
   255  		},
   256  	}
   257  	matcherGroup := NewMphMatcherGroup()
   258  	for i, rule := range rules {
   259  		matcher, err := rule.Type.New(rule.Domain)
   260  		common.Must(err)
   261  		common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(i+3)))
   262  	}
   263  	matcherGroup.Build()
   264  	for _, test := range cases {
   265  		if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) {
   266  			t.Error("unexpected output: ", m, " for test case ", test)
   267  		}
   268  	}
   269  }
   270  
   271  func TestEmptyMphMatcherGroup(t *testing.T) {
   272  	g := NewMphMatcherGroup()
   273  	g.Build()
   274  	r := g.Match("v2fly.org")
   275  	if len(r) != 0 {
   276  		t.Error("Expect [], but ", r)
   277  	}
   278  }