github.com/searKing/golang/go@v1.2.117/exp/sync/lru_test.go (about) 1 // Copyright 2022 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sync_test 6 7 import ( 8 "math/rand" 9 "runtime" 10 "slices" 11 "sync" 12 "testing" 13 14 sync_ "github.com/searKing/golang/go/exp/sync" 15 ) 16 17 func TestLRU(t *testing.T) { 18 evictCounter := 0 19 onEvicted := func(k int, v int) { 20 if k != v { 21 t.Fatalf("Evict values not equal (%v!=%v)", k, v) 22 } 23 evictCounter++ 24 } 25 l := sync_.NewLRU[int, int](128) 26 l.SetEvictCallback(onEvicted) 27 28 for i := 0; i < 256; i++ { 29 if i%2 == 0 { 30 l.Store(i, i) 31 continue 32 } 33 l.Add(i, i) 34 } 35 if l.Len() != 128 { 36 t.Fatalf("bad len: %v", l.Len()) 37 } 38 39 if evictCounter != 128 { 40 t.Fatalf("bad evict count: %v", evictCounter) 41 } 42 43 for i, k := range l.Keys() { 44 if v, ok := l.Get(k); !ok || v != k || v != i+128 { 45 t.Fatalf("bad key: %v", k) 46 } 47 } 48 for i := 0; i < 128; i++ { 49 _, ok := l.Get(i) 50 if ok { 51 t.Fatalf("should be evicted") 52 } 53 } 54 for i := 128; i < 256; i++ { 55 _, ok := l.Get(i) 56 if !ok { 57 t.Fatalf("should not be evicted") 58 } 59 } 60 for i := 128; i < 192; i++ { 61 v, ok := l.LoadAndDelete(i) 62 if !ok { 63 t.Fatalf("should be contained") 64 } 65 if v != i { 66 t.Fatalf("bad key: %v", i) 67 } 68 ok = l.Remove(i) 69 if ok { 70 t.Fatalf("should not be contained") 71 } 72 _, ok = l.Get(i) 73 if ok { 74 t.Fatalf("should be deleted") 75 } 76 } 77 78 l.Get(192) // expect 192 to be last key in l.Keys() 79 80 for i, k := range l.Keys() { 81 if (i < 63 && k != i+193) || (i == 63 && k != 192) { 82 t.Fatalf("out of order key: %v", k) 83 } 84 } 85 86 l.Purge() 87 if l.Len() != 0 { 88 t.Fatalf("bad len: %v", l.Len()) 89 } 90 if _, ok := l.Get(200); ok { 91 t.Fatalf("should contain nothing") 92 } 93 } 94 95 // Test that Resize can upsize and downsize 96 func TestLRU_Resize(t *testing.T) { 97 onEvictCounter := 0 98 onEvicted := func(k int, v int) { onEvictCounter++ } 99 100 l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted) 101 102 // Downsize 103 l.Add(1, 1) 104 l.Add(2, 2) 105 evicted := l.Resize(1) 106 if evicted != 1 { 107 t.Errorf("1 element should have been evicted: %v", evicted) 108 } 109 if onEvictCounter != 1 { 110 t.Errorf("onEvicted should have been called 1 time: %v", onEvictCounter) 111 } 112 113 l.Add(3, 3) 114 if l.Contains(1) { 115 t.Errorf("Element 1 should have been evicted") 116 } 117 118 // Upsize 119 evicted = l.Resize(2) 120 if evicted != 0 { 121 t.Errorf("0 elements should have been evicted: %v", evicted) 122 } 123 124 l.Add(4, 4) 125 if !l.Contains(3) || !l.Contains(4) { 126 t.Errorf("Cache should have contained 2 elements") 127 } 128 } 129 130 // Test that Contains doesn't update recent-ness 131 func TestLRU_Contains(t *testing.T) { 132 l := sync_.NewLRU[int, int](2) 133 134 l.Add(1, 1) 135 l.Add(2, 2) 136 if !l.Contains(1) { 137 t.Errorf("1 should be contained") 138 } 139 140 l.Add(3, 3) 141 if l.Contains(1) { 142 t.Errorf("Contains should not have updated recent-ness of 1") 143 } 144 } 145 146 // Test that Peek doesn't update recent-ness 147 func TestLRU_Peek(t *testing.T) { 148 l := sync_.NewLRU[int, int](2) 149 150 l.Add(1, 1) 151 l.Add(2, 2) 152 if v, ok := l.Peek(1); !ok || v != 1 { 153 t.Errorf("1 should be set to 1: %v, %v", v, ok) 154 } 155 156 l.Add(3, 3) 157 if l.Contains(1) { 158 t.Errorf("should not have updated recent-ness of 1") 159 } 160 } 161 162 // Test that Add returns true/false if an eviction occurred 163 func TestLRU_Add(t *testing.T) { 164 evictCounter := 0 165 onEvicted := func(k int, v int) { evictCounter++ } 166 167 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 168 169 if l.Add(1, 1) == true || evictCounter != 0 { 170 t.Errorf("should not have an eviction") 171 } 172 if l.Add(1, -1) == true || evictCounter != 0 { 173 t.Errorf("should not have an eviction") 174 } 175 if l.Add(2, 2) == false || evictCounter != 1 { 176 t.Errorf("should have an eviction") 177 } 178 } 179 180 func TestLRU_Store(t *testing.T) { 181 evictCounter := 0 182 onEvicted := func(k int, v int) { evictCounter++ } 183 184 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 185 186 l.Store(1, 1) 187 if l.Len() != 1 || evictCounter != 0 { 188 t.Errorf("should not have an eviction") 189 } 190 l.Store(1, -1) 191 if l.Len() != 1 || evictCounter != 0 { 192 t.Errorf("should not have an eviction") 193 } 194 l.Store(2, 2) 195 if l.Len() != 1 || evictCounter != 1 { 196 t.Errorf("should have an eviction") 197 } 198 } 199 200 func TestLRU_LoadOrStore(t *testing.T) { 201 evictCounter := 0 202 onEvicted := func(k int, v int) { evictCounter++ } 203 204 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 205 206 { 207 _, loaded := l.LoadOrStore(1, 1) 208 if loaded { 209 t.Errorf("should not loaded") 210 } 211 if evictCounter != 0 { 212 t.Errorf("should not have an eviction") 213 } 214 } 215 { 216 old, loaded := l.LoadOrStore(1, -1) 217 if !loaded { 218 t.Errorf("should loaded") 219 } 220 if old != 1 { 221 t.Errorf("should load old value 1") 222 } 223 if evictCounter != 0 { 224 t.Errorf("should not have an eviction") 225 } 226 } 227 228 { 229 _, loaded := l.LoadOrStore(2, 2) 230 if loaded { 231 t.Errorf("should not loaded") 232 } 233 if evictCounter != 1 { 234 t.Errorf("should not have an eviction") 235 } 236 } 237 } 238 239 func TestLRU_LoadAndDelete(t *testing.T) { 240 evictCounter := 0 241 onEvicted := func(k int, v int) { evictCounter++ } 242 243 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 244 l.Add(1, 1) 245 l.Add(2, 2) 246 247 { 248 _, loaded := l.LoadAndDelete(-1) 249 if loaded { 250 t.Errorf("should not loaded") 251 } 252 } 253 { 254 _, loaded := l.LoadAndDelete(1) 255 if loaded { 256 t.Errorf("should not loaded") 257 } 258 } 259 260 { 261 old, loaded := l.LoadAndDelete(2) 262 if !loaded { 263 t.Errorf("should not loaded") 264 } 265 if old != 2 { 266 t.Errorf("should load old value 2") 267 } 268 } 269 } 270 271 func TestLRU_Delete(t *testing.T) { 272 evictCounter := 0 273 onEvicted := func(k int, v int) { evictCounter++ } 274 275 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 276 l.Add(1, 1) 277 l.Add(2, 2) 278 279 { 280 l.Delete(-1) 281 if l.Len() != 1 { 282 t.Errorf("should not deleted") 283 } 284 } 285 { 286 l.Delete(1) 287 if l.Len() != 1 { 288 t.Errorf("should not deleted") 289 } 290 } 291 { 292 l.Delete(2) 293 if l.Len() != 0 { 294 t.Errorf("should not deleted") 295 } 296 } 297 } 298 299 func TestLRU_Remove(t *testing.T) { 300 evictCounter := 0 301 onEvicted := func(k int, v int) { evictCounter++ } 302 303 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 304 l.Add(1, 1) 305 l.Add(2, 2) 306 307 { 308 loaded := l.Remove(-1) 309 if loaded { 310 t.Errorf("should not loaded") 311 } 312 } 313 { 314 loaded := l.Remove(1) 315 if loaded { 316 t.Errorf("should not loaded") 317 } 318 } 319 320 { 321 loaded := l.Remove(2) 322 if !loaded { 323 t.Errorf("should not loaded") 324 } 325 } 326 } 327 328 func TestLRU_Swap(t *testing.T) { 329 evictCounter := 0 330 onEvicted := func(k int, v int) { evictCounter++ } 331 332 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 333 334 if _, loaded := l.Swap(1, 1); loaded == true || evictCounter != 0 { 335 t.Errorf("should not have an eviction") 336 } 337 if pre, loaded := l.Swap(1, -1); loaded == false || pre != 1 || evictCounter != 0 { 338 t.Errorf("should not have an eviction") 339 } 340 if _, loaded := l.Swap(2, 2); loaded == true || evictCounter != 1 { 341 t.Errorf("should have an eviction") 342 } 343 } 344 345 func TestLRU_CompareAndSwap(t *testing.T) { 346 evictCounter := 0 347 onEvicted := func(k int, v int) { evictCounter++ } 348 349 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 350 l.Add(1, 1) 351 l.Add(2, 2) 352 353 if swapped := l.CompareAndSwap(-1, -1, -2); swapped { 354 t.Errorf("should not swapped") 355 } 356 if swapped := l.CompareAndSwap(1, 1, -1); swapped { 357 t.Errorf("should not swapped") 358 } 359 if swapped := l.CompareAndSwap(2, -2, 2); swapped { 360 t.Errorf("should not swapped") 361 } 362 if swapped := l.CompareAndSwap(2, 2, -2); !swapped { 363 t.Errorf("should swapped") 364 } else { 365 if val, ok := l.Peek(2); !ok || val != -2 { 366 t.Errorf("should swapped with value -2") 367 } 368 } 369 } 370 371 func TestLRU_CompareAndDelete(t *testing.T) { 372 evictCounter := 0 373 onEvicted := func(k int, v int) { evictCounter++ } 374 375 l := sync_.NewLRU[int, int](1).SetEvictCallback(onEvicted) 376 l.Add(1, 1) 377 l.Add(2, 2) 378 379 if deleted := l.CompareAndDelete(-1, -1); deleted { 380 t.Errorf("should not deleted") 381 } 382 if deleted := l.CompareAndDelete(1, 1); deleted { 383 t.Errorf("should not deleted") 384 } 385 if deleted := l.CompareAndDelete(2, -2); deleted { 386 t.Errorf("should not deleted") 387 } 388 if deleted := l.CompareAndDelete(2, 2); !deleted { 389 t.Errorf("should deleted") 390 } else { 391 if l.Len() != 0 { 392 t.Errorf("should empty") 393 } 394 } 395 } 396 397 func TestLRU_Keys(t *testing.T) { 398 evictCounter := 0 399 onEvicted := func(k int, v int) { evictCounter++ } 400 401 l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted) 402 l.Add(1, 1) 403 l.Add(2, 2) 404 l.Add(3, 3) 405 406 if !slices.Equal(l.Keys(), []int{2, 3}) { 407 t.Fatalf("bad key order: %v", l.Keys()) 408 } 409 } 410 411 func TestLRU_Range(t *testing.T) { 412 evictCounter := 0 413 onEvicted := func(k int, v int) { evictCounter++ } 414 415 l := sync_.NewLRU[int, int](2).SetEvictCallback(onEvicted) 416 l.Add(1, 1) 417 l.Add(2, 2) 418 l.Add(3, 3) 419 420 var keys, vals []int 421 l.Range(func(key int, value int) bool { 422 keys = append(keys, key) 423 vals = append(vals, value) 424 return true 425 }) 426 427 if !slices.Equal(l.Keys(), keys) { 428 t.Fatalf("bad key order: %v", l.Keys()) 429 } 430 if !slices.Equal(keys, vals) { 431 t.Fatalf("mismatched kv pairs: %v:%v", keys, vals) 432 } 433 } 434 435 func TestLRU_GetOldest(t *testing.T) { 436 l := sync_.NewLRU[int, int](128) 437 438 for i := 0; i < 256; i++ { 439 l.Add(i, i) 440 } 441 k, _, ok := l.GetOldest() 442 if !ok { 443 t.Fatalf("missing") 444 } 445 if k != 128 { 446 t.Fatalf("bad: %v", k) 447 } 448 } 449 450 func TestLRU_GetOldest_RemoveOldest(t *testing.T) { 451 l := sync_.NewLRU[int, int](128) 452 453 for i := 0; i < 256; i++ { 454 l.Add(i, i) 455 } 456 k, _, ok := l.GetOldest() 457 if !ok { 458 t.Fatalf("missing") 459 } 460 if k != 128 { 461 t.Fatalf("bad: %v", k) 462 } 463 464 k, _, ok = l.RemoveOldest() 465 if !ok { 466 t.Fatalf("missing") 467 } 468 if k != 128 { 469 t.Fatalf("bad: %v", k) 470 } 471 472 k, _, ok = l.RemoveOldest() 473 if !ok { 474 t.Fatalf("missing") 475 } 476 if k != 129 { 477 t.Fatalf("bad: %v", k) 478 } 479 } 480 481 func TestLRU_PeekAndDeleteOldest(t *testing.T) { 482 l := sync_.NewLRU[int, int](128) 483 484 for i := 0; i < 256; i++ { 485 l.Add(i, i) 486 } 487 k, _, ok := l.PeekOldest() 488 if !ok { 489 t.Fatalf("missing") 490 } 491 if k != 128 { 492 t.Fatalf("bad: %v", k) 493 } 494 495 k, v, ok := l.PeekAndDeleteOldest() 496 if !ok { 497 t.Fatalf("missing") 498 } 499 if k != 128 { 500 t.Fatalf("bad key: %v", k) 501 } 502 if v != 128 { 503 t.Fatalf("bad value: %v", k) 504 } 505 506 k, _, ok = l.PeekAndDeleteOldest() 507 if !ok { 508 t.Fatalf("missing") 509 } 510 if k != 129 { 511 t.Fatalf("bad: %v", k) 512 } 513 } 514 515 func TestLRU_RemoveOldest(t *testing.T) { 516 l := sync_.NewLRU[int, int](128) 517 518 for i := 0; i < 256; i++ { 519 l.Add(i, i) 520 } 521 k, _, ok := l.GetOldest() 522 if !ok { 523 t.Fatalf("missing") 524 } 525 if k != 128 { 526 t.Fatalf("bad: %v", k) 527 } 528 529 k, _, ok = l.RemoveOldest() 530 if !ok { 531 t.Fatalf("missing") 532 } 533 if k != 128 { 534 t.Fatalf("bad: %v", k) 535 } 536 537 k, _, ok = l.RemoveOldest() 538 if !ok { 539 t.Fatalf("missing") 540 } 541 if k != 129 { 542 t.Fatalf("bad: %v", k) 543 } 544 } 545 546 func TestConcurrentRange(t *testing.T) { 547 const lruSize = 1 << 10 548 549 m := sync_.NewLRU[int64, int64](lruSize) 550 for n := int64(1); n <= lruSize; n++ { 551 m.Store(n, n) 552 } 553 554 done := make(chan struct{}) 555 var wg sync.WaitGroup 556 defer func() { 557 close(done) 558 wg.Wait() 559 }() 560 for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- { 561 r := rand.New(rand.NewSource(g)) 562 wg.Add(1) 563 go func(g int64) { 564 defer wg.Done() 565 for i := int64(0); ; i++ { 566 select { 567 case <-done: 568 return 569 default: 570 } 571 for n := int64(1); n < lruSize; n++ { 572 if r.Int63n(lruSize) == 0 { 573 m.Store(n, n*i*g) 574 } else { 575 m.Load(n) 576 } 577 } 578 } 579 }(g) 580 } 581 582 iters := 1 << 10 583 if testing.Short() { 584 iters = 16 585 } 586 for n := iters; n > 0; n-- { 587 seen := make(map[int64]bool, lruSize) 588 589 m.Range(func(ki, vi int64) bool { 590 k, v := ki, vi 591 if v%k != 0 { 592 t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v) 593 } 594 if seen[k] { 595 t.Fatalf("Range visited key %v twice", k) 596 } 597 seen[k] = true 598 return true 599 }) 600 601 if len(seen) != lruSize { 602 t.Fatalf("Range visited %v elements of %v-element Map", len(seen), lruSize) 603 } 604 } 605 }