github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/strmatcher/matchergroup_ac_automation_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 TestACAutomatonMatcherGroup(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  		ac := NewACAutomatonMatcherGroup()
    63  		matcher, err := test.mType.New(test.pattern)
    64  		common.Must(err)
    65  		common.Must(AddMatcherToGroup(ac, matcher, 0))
    66  		ac.Build()
    67  		if m := ac.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  				pattern: "google.com",
    90  				mType:   Substr,
    91  			},
    92  			{
    93  				pattern: "vgoogle.com",
    94  				mType:   Substr,
    95  			},
    96  		}
    97  		ac := NewACAutomatonMatcherGroup()
    98  		for _, test := range cases2Input {
    99  			matcher, err := test.mType.New(test.pattern)
   100  			common.Must(err)
   101  			common.Must(AddMatcherToGroup(ac, matcher, 0))
   102  		}
   103  		ac.Build()
   104  		cases2Output := []struct {
   105  			pattern string
   106  			res     bool
   107  		}{
   108  			{
   109  				pattern: "126.com",
   110  				res:     false,
   111  			},
   112  			{
   113  				pattern: "m.163.com",
   114  				res:     true,
   115  			},
   116  			{
   117  				pattern: "mm163.com",
   118  				res:     false,
   119  			},
   120  			{
   121  				pattern: "m.126.com",
   122  				res:     true,
   123  			},
   124  			{
   125  				pattern: "163.com",
   126  				res:     true,
   127  			},
   128  			{
   129  				pattern: "63.com",
   130  				res:     false,
   131  			},
   132  			{
   133  				pattern: "oogle.com",
   134  				res:     false,
   135  			},
   136  			{
   137  				pattern: "vvgoogle.com",
   138  				res:     true,
   139  			},
   140  		}
   141  		for _, test := range cases2Output {
   142  			if m := ac.MatchAny(test.pattern); m != test.res {
   143  				t.Error("unexpected output: ", m, " for test case ", test)
   144  			}
   145  		}
   146  	}
   147  
   148  	{
   149  		cases3Input := []struct {
   150  			pattern string
   151  			mType   Type
   152  		}{
   153  			{
   154  				pattern: "video.google.com",
   155  				mType:   Domain,
   156  			},
   157  			{
   158  				pattern: "gle.com",
   159  				mType:   Domain,
   160  			},
   161  		}
   162  		ac := NewACAutomatonMatcherGroup()
   163  		for _, test := range cases3Input {
   164  			matcher, err := test.mType.New(test.pattern)
   165  			common.Must(err)
   166  			common.Must(AddMatcherToGroup(ac, matcher, 0))
   167  		}
   168  		ac.Build()
   169  		cases3Output := []struct {
   170  			pattern string
   171  			res     bool
   172  		}{
   173  			{
   174  				pattern: "google.com",
   175  				res:     false,
   176  			},
   177  		}
   178  		for _, test := range cases3Output {
   179  			if m := ac.MatchAny(test.pattern); m != test.res {
   180  				t.Error("unexpected output: ", m, " for test case ", test)
   181  			}
   182  		}
   183  	}
   184  
   185  	{
   186  		cases4Input := []struct {
   187  			pattern string
   188  			mType   Type
   189  		}{
   190  			{
   191  				pattern: "apis",
   192  				mType:   Substr,
   193  			},
   194  			{
   195  				pattern: "googleapis.com",
   196  				mType:   Domain,
   197  			},
   198  		}
   199  		ac := NewACAutomatonMatcherGroup()
   200  		for _, test := range cases4Input {
   201  			matcher, err := test.mType.New(test.pattern)
   202  			common.Must(err)
   203  			common.Must(AddMatcherToGroup(ac, matcher, 0))
   204  		}
   205  		ac.Build()
   206  		cases4Output := []struct {
   207  			pattern string
   208  			res     bool
   209  		}{
   210  			{
   211  				pattern: "gapis.com",
   212  				res:     true,
   213  			},
   214  		}
   215  		for _, test := range cases4Output {
   216  			if m := ac.MatchAny(test.pattern); m != test.res {
   217  				t.Error("unexpected output: ", m, " for test case ", test)
   218  			}
   219  		}
   220  	}
   221  }
   222  
   223  func TestACAutomatonMatcherGroupSubstr(t *testing.T) {
   224  	patterns := []struct {
   225  		pattern string
   226  		mType   Type
   227  	}{
   228  		{
   229  			pattern: "apis",
   230  			mType:   Substr,
   231  		},
   232  		{
   233  			pattern: "google",
   234  			mType:   Substr,
   235  		},
   236  		{
   237  			pattern: "apis",
   238  			mType:   Substr,
   239  		},
   240  	}
   241  	cases := []struct {
   242  		input  string
   243  		output []uint32
   244  	}{
   245  		{
   246  			input:  "google.com",
   247  			output: []uint32{1},
   248  		},
   249  		{
   250  			input:  "apis.com",
   251  			output: []uint32{0, 2},
   252  		},
   253  		{
   254  			input:  "googleapis.com",
   255  			output: []uint32{1, 0, 2},
   256  		},
   257  		{
   258  			input:  "fonts.googleapis.com",
   259  			output: []uint32{1, 0, 2},
   260  		},
   261  		{
   262  			input:  "apis.googleapis.com",
   263  			output: []uint32{0, 2, 1, 0, 2},
   264  		},
   265  	}
   266  	matcherGroup := NewACAutomatonMatcherGroup()
   267  	for id, entry := range patterns {
   268  		matcher, err := entry.mType.New(entry.pattern)
   269  		common.Must(err)
   270  		common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(id)))
   271  	}
   272  	matcherGroup.Build()
   273  	for _, test := range cases {
   274  		if r := matcherGroup.Match(test.input); !reflect.DeepEqual(r, test.output) {
   275  			t.Error("unexpected output: ", r, " for test case ", test)
   276  		}
   277  	}
   278  }
   279  
   280  // See https://github.com/v2fly/v2ray-core/issues/92#issuecomment-673238489
   281  func TestACAutomatonMatcherGroupAsIndexMatcher(t *testing.T) {
   282  	rules := []struct {
   283  		Type   Type
   284  		Domain string
   285  	}{
   286  		// Regex not supported by ACAutomationMatcherGroup
   287  		// {
   288  		// 	Type:   Regex,
   289  		// 	Domain: "apis\\.us$",
   290  		// },
   291  		{
   292  			Type:   Substr,
   293  			Domain: "apis",
   294  		},
   295  		{
   296  			Type:   Domain,
   297  			Domain: "googleapis.com",
   298  		},
   299  		{
   300  			Type:   Domain,
   301  			Domain: "com",
   302  		},
   303  		{
   304  			Type:   Full,
   305  			Domain: "www.baidu.com",
   306  		},
   307  		{
   308  			Type:   Substr,
   309  			Domain: "apis",
   310  		},
   311  		{
   312  			Type:   Domain,
   313  			Domain: "googleapis.com",
   314  		},
   315  		{
   316  			Type:   Full,
   317  			Domain: "fonts.googleapis.com",
   318  		},
   319  		{
   320  			Type:   Full,
   321  			Domain: "www.baidu.com",
   322  		},
   323  		{
   324  			Type:   Domain,
   325  			Domain: "example.com",
   326  		},
   327  	}
   328  	cases := []struct {
   329  		Input  string
   330  		Output []uint32
   331  	}{
   332  		{
   333  			Input:  "www.baidu.com",
   334  			Output: []uint32{5, 9, 4},
   335  		},
   336  		{
   337  			Input:  "fonts.googleapis.com",
   338  			Output: []uint32{8, 3, 7, 4, 2, 6},
   339  		},
   340  		{
   341  			Input:  "example.googleapis.com",
   342  			Output: []uint32{3, 7, 4, 2, 6},
   343  		},
   344  		{
   345  			Input:  "testapis.us",
   346  			Output: []uint32{2, 6 /*, 1*/},
   347  		},
   348  		{
   349  			Input:  "example.com",
   350  			Output: []uint32{10, 4},
   351  		},
   352  	}
   353  	matcherGroup := NewACAutomatonMatcherGroup()
   354  	for i, rule := range rules {
   355  		matcher, err := rule.Type.New(rule.Domain)
   356  		common.Must(err)
   357  		common.Must(AddMatcherToGroup(matcherGroup, matcher, uint32(i+2)))
   358  	}
   359  	matcherGroup.Build()
   360  	for _, test := range cases {
   361  		if m := matcherGroup.Match(test.Input); !reflect.DeepEqual(m, test.Output) {
   362  			t.Error("unexpected output: ", m, " for test case ", test)
   363  		}
   364  	}
   365  }