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 }