github.com/kelleygo/clashcore@v1.0.2/component/fakeip/pool_test.go (about)

     1  package fakeip
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/kelleygo/clashcore/component/profile/cachefile"
    11  	"github.com/kelleygo/clashcore/component/trie"
    12  
    13  	"github.com/sagernet/bbolt"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func createPools(options Options) ([]*Pool, string, error) {
    18  	pool, err := New(options)
    19  	if err != nil {
    20  		return nil, "", err
    21  	}
    22  	filePool, tempfile, err := createCachefileStore(options)
    23  	if err != nil {
    24  		return nil, "", err
    25  	}
    26  
    27  	return []*Pool{pool, filePool}, tempfile, nil
    28  }
    29  
    30  func createCachefileStore(options Options) (*Pool, string, error) {
    31  	pool, err := New(options)
    32  	if err != nil {
    33  		return nil, "", err
    34  	}
    35  	f, err := os.CreateTemp("", "yiclashcore")
    36  	if err != nil {
    37  		return nil, "", err
    38  	}
    39  
    40  	db, err := bbolt.Open(f.Name(), 0o666, &bbolt.Options{Timeout: time.Second})
    41  	if err != nil {
    42  		return nil, "", err
    43  	}
    44  
    45  	pool.store = &cachefileStore{
    46  		cache: &cachefile.CacheFile{DB: db},
    47  	}
    48  	return pool, f.Name(), nil
    49  }
    50  
    51  func TestPool_Basic(t *testing.T) {
    52  	ipnet := netip.MustParsePrefix("192.168.0.0/28")
    53  	pools, tempfile, err := createPools(Options{
    54  		IPNet: ipnet,
    55  		Size:  10,
    56  	})
    57  	assert.Nil(t, err)
    58  	defer os.Remove(tempfile)
    59  
    60  	for _, pool := range pools {
    61  		first := pool.Lookup("foo.com")
    62  		last := pool.Lookup("bar.com")
    63  		bar, exist := pool.LookBack(last)
    64  
    65  		assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 4}))
    66  		assert.True(t, pool.Lookup("foo.com") == netip.AddrFrom4([4]byte{192, 168, 0, 4}))
    67  		assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 5}))
    68  		assert.True(t, exist)
    69  		assert.Equal(t, bar, "bar.com")
    70  		assert.True(t, pool.Gateway() == netip.AddrFrom4([4]byte{192, 168, 0, 1}))
    71  		assert.True(t, pool.Broadcast() == netip.AddrFrom4([4]byte{192, 168, 0, 15}))
    72  		assert.Equal(t, pool.IPNet().String(), ipnet.String())
    73  		assert.True(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 5})))
    74  		assert.False(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 6})))
    75  		assert.False(t, pool.Exist(netip.MustParseAddr("::1")))
    76  	}
    77  }
    78  
    79  func TestPool_BasicV6(t *testing.T) {
    80  	ipnet := netip.MustParsePrefix("2001:4860:4860::8888/118")
    81  	pools, tempfile, err := createPools(Options{
    82  		IPNet: ipnet,
    83  		Size:  10,
    84  	})
    85  	assert.Nil(t, err)
    86  	defer os.Remove(tempfile)
    87  
    88  	for _, pool := range pools {
    89  		first := pool.Lookup("foo.com")
    90  		last := pool.Lookup("bar.com")
    91  		bar, exist := pool.LookBack(last)
    92  
    93  		assert.True(t, first == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804"))
    94  		assert.True(t, pool.Lookup("foo.com") == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8804"))
    95  		assert.True(t, last == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805"))
    96  		assert.True(t, exist)
    97  		assert.Equal(t, bar, "bar.com")
    98  		assert.True(t, pool.Gateway() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8801"))
    99  		assert.True(t, pool.Broadcast() == netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8bff"))
   100  		assert.Equal(t, pool.IPNet().String(), ipnet.String())
   101  		assert.True(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8805")))
   102  		assert.False(t, pool.Exist(netip.MustParseAddr("2001:4860:4860:0000:0000:0000:0000:8806")))
   103  		assert.False(t, pool.Exist(netip.MustParseAddr("127.0.0.1")))
   104  	}
   105  }
   106  
   107  func TestPool_Case_Insensitive(t *testing.T) {
   108  	ipnet := netip.MustParsePrefix("192.168.0.1/29")
   109  	pools, tempfile, err := createPools(Options{
   110  		IPNet: ipnet,
   111  		Size:  10,
   112  	})
   113  	assert.Nil(t, err)
   114  	defer os.Remove(tempfile)
   115  
   116  	for _, pool := range pools {
   117  		first := pool.Lookup("foo.com")
   118  		last := pool.Lookup("Foo.Com")
   119  		foo, exist := pool.LookBack(last)
   120  
   121  		assert.Equal(t, first, pool.Lookup("Foo.Com"))
   122  		assert.Equal(t, pool.Lookup("fOo.cOM"), first)
   123  		assert.True(t, exist)
   124  		assert.Equal(t, foo, "foo.com")
   125  	}
   126  }
   127  
   128  func TestPool_CycleUsed(t *testing.T) {
   129  	ipnet := netip.MustParsePrefix("192.168.0.16/28")
   130  	pools, tempfile, err := createPools(Options{
   131  		IPNet: ipnet,
   132  		Size:  10,
   133  	})
   134  	assert.Nil(t, err)
   135  	defer os.Remove(tempfile)
   136  
   137  	for _, pool := range pools {
   138  		foo := pool.Lookup("foo.com")
   139  		bar := pool.Lookup("bar.com")
   140  		for i := 0; i < 9; i++ {
   141  			pool.Lookup(fmt.Sprintf("%d.com", i))
   142  		}
   143  		baz := pool.Lookup("baz.com")
   144  		next := pool.Lookup("foo.com")
   145  		assert.True(t, foo == baz)
   146  		assert.True(t, next == bar)
   147  	}
   148  }
   149  
   150  func TestPool_Skip(t *testing.T) {
   151  	ipnet := netip.MustParsePrefix("192.168.0.1/29")
   152  	tree := trie.New[struct{}]()
   153  	tree.Insert("example.com", struct{}{})
   154  	pools, tempfile, err := createPools(Options{
   155  		IPNet: ipnet,
   156  		Size:  10,
   157  		Host:  tree,
   158  	})
   159  	assert.Nil(t, err)
   160  	defer os.Remove(tempfile)
   161  
   162  	for _, pool := range pools {
   163  		assert.True(t, pool.ShouldSkipped("example.com"))
   164  		assert.False(t, pool.ShouldSkipped("foo.com"))
   165  	}
   166  }
   167  
   168  func TestPool_MaxCacheSize(t *testing.T) {
   169  	ipnet := netip.MustParsePrefix("192.168.0.1/24")
   170  	pool, _ := New(Options{
   171  		IPNet: ipnet,
   172  		Size:  2,
   173  	})
   174  
   175  	first := pool.Lookup("foo.com")
   176  	pool.Lookup("bar.com")
   177  	pool.Lookup("baz.com")
   178  	next := pool.Lookup("foo.com")
   179  
   180  	assert.False(t, first == next)
   181  }
   182  
   183  func TestPool_DoubleMapping(t *testing.T) {
   184  	ipnet := netip.MustParsePrefix("192.168.0.1/24")
   185  	pool, _ := New(Options{
   186  		IPNet: ipnet,
   187  		Size:  2,
   188  	})
   189  
   190  	// fill cache
   191  	fooIP := pool.Lookup("foo.com")
   192  	bazIP := pool.Lookup("baz.com")
   193  
   194  	// make foo.com hot
   195  	pool.Lookup("foo.com")
   196  
   197  	// should drop baz.com
   198  	barIP := pool.Lookup("bar.com")
   199  
   200  	_, fooExist := pool.LookBack(fooIP)
   201  	_, bazExist := pool.LookBack(bazIP)
   202  	_, barExist := pool.LookBack(barIP)
   203  
   204  	newBazIP := pool.Lookup("baz.com")
   205  
   206  	assert.True(t, fooExist)
   207  	assert.False(t, bazExist)
   208  	assert.True(t, barExist)
   209  
   210  	assert.False(t, bazIP == newBazIP)
   211  }
   212  
   213  func TestPool_Clone(t *testing.T) {
   214  	ipnet := netip.MustParsePrefix("192.168.0.1/24")
   215  	pool, _ := New(Options{
   216  		IPNet: ipnet,
   217  		Size:  2,
   218  	})
   219  
   220  	first := pool.Lookup("foo.com")
   221  	last := pool.Lookup("bar.com")
   222  	assert.True(t, first == netip.AddrFrom4([4]byte{192, 168, 0, 4}))
   223  	assert.True(t, last == netip.AddrFrom4([4]byte{192, 168, 0, 5}))
   224  
   225  	newPool, _ := New(Options{
   226  		IPNet: ipnet,
   227  		Size:  2,
   228  	})
   229  	newPool.CloneFrom(pool)
   230  	_, firstExist := newPool.LookBack(first)
   231  	_, lastExist := newPool.LookBack(last)
   232  	assert.True(t, firstExist)
   233  	assert.True(t, lastExist)
   234  }
   235  
   236  func TestPool_Error(t *testing.T) {
   237  	ipnet := netip.MustParsePrefix("192.168.0.1/31")
   238  	_, err := New(Options{
   239  		IPNet: ipnet,
   240  		Size:  10,
   241  	})
   242  
   243  	assert.Error(t, err)
   244  }
   245  
   246  func TestPool_FlushFileCache(t *testing.T) {
   247  	ipnet := netip.MustParsePrefix("192.168.0.1/28")
   248  	pools, tempfile, err := createPools(Options{
   249  		IPNet: ipnet,
   250  		Size:  10,
   251  	})
   252  	assert.Nil(t, err)
   253  	defer os.Remove(tempfile)
   254  
   255  	for _, pool := range pools {
   256  		foo := pool.Lookup("foo.com")
   257  		bar := pool.Lookup("baz.com")
   258  		bax := pool.Lookup("baz.com")
   259  		fox := pool.Lookup("foo.com")
   260  
   261  		err = pool.FlushFakeIP()
   262  		assert.Nil(t, err)
   263  
   264  		next := pool.Lookup("baz.com")
   265  		baz := pool.Lookup("foo.com")
   266  		nero := pool.Lookup("foo.com")
   267  
   268  		assert.True(t, foo == fox)
   269  		assert.True(t, foo == next)
   270  		assert.False(t, foo == baz)
   271  		assert.True(t, bar == bax)
   272  		assert.True(t, bar == baz)
   273  		assert.False(t, bar == next)
   274  		assert.True(t, baz == nero)
   275  	}
   276  }
   277  
   278  func TestPool_FlushMemoryCache(t *testing.T) {
   279  	ipnet := netip.MustParsePrefix("192.168.0.1/28")
   280  	pool, _ := New(Options{
   281  		IPNet: ipnet,
   282  		Size:  10,
   283  	})
   284  
   285  	foo := pool.Lookup("foo.com")
   286  	bar := pool.Lookup("baz.com")
   287  	bax := pool.Lookup("baz.com")
   288  	fox := pool.Lookup("foo.com")
   289  
   290  	err := pool.FlushFakeIP()
   291  	assert.Nil(t, err)
   292  
   293  	next := pool.Lookup("baz.com")
   294  	baz := pool.Lookup("foo.com")
   295  	nero := pool.Lookup("foo.com")
   296  
   297  	assert.True(t, foo == fox)
   298  	assert.True(t, foo == next)
   299  	assert.False(t, foo == baz)
   300  	assert.True(t, bar == bax)
   301  	assert.True(t, bar == baz)
   302  	assert.False(t, bar == next)
   303  	assert.True(t, baz == nero)
   304  }