lru_test.go
⎇
Raw
1package service
2
3import (
4 "strconv"
5 "sync"
6 "testing"
7)
8
9func TestLRUEvictsLeastRecentlyUsed(t *testing.T) {
10 c := newLRU[int](2)
11 c.Put("a", 1)
12 c.Put("b", 2)
13
14 // Touching "a" makes "b" the eviction candidate.
15 if _, ok := c.Get("a"); !ok {
16 t.Fatal("a missing right after Put")
17 }
18 c.Put("c", 3)
19
20 if _, ok := c.Get("b"); ok {
21 t.Error("b survived, want it evicted as least recently used")
22 }
23 for _, key := range []string{"a", "c"} {
24 if _, ok := c.Get(key); !ok {
25 t.Errorf("%s evicted, want it kept", key)
26 }
27 }
28
29 // Overwriting an existing key must not grow the cache past its size.
30 c.Put("a", 9)
31 if v, _ := c.Get("a"); v != 9 {
32 t.Errorf("a = %d, want 9", v)
33 }
34 if got := c.order.Len(); got != 2 {
35 t.Errorf("cache holds %d entries, want 2", got)
36 }
37
38 c.Delete("a")
39 if _, ok := c.Get("a"); ok {
40 t.Error("a readable after Delete")
41 }
42}
43
44// The per-file thumbnail locks are reference counted rather than cached. The map
45// must not grow with every file ever touched, and must not drop a lock that is
46// still held.
47func TestThumbLocksReleasedWhenUnused(t *testing.T) {
48 s := NewLibraryService(t.TempDir(), "ffmpeg", "ffprobe")
49
50 var wg sync.WaitGroup
51 shared := 0
52 for i := 0; i < 50; i++ {
53 wg.Add(1)
54 go func(i int) {
55 defer wg.Done()
56 // Half contend on one path, half take a path of their own.
57 if i%2 == 0 {
58 defer s.lockThumbFile("shared")()
59 shared++
60 return
61 }
62 defer s.lockThumbFile("file-" + strconv.Itoa(i))()
63 }(i)
64 }
65 wg.Wait()
66
67 if shared != 25 {
68 t.Errorf("shared counter = %d, want 25: the lock did not serialize callers", shared)
69 }
70 if got := len(s.thumbLocks); got != 0 {
71 t.Errorf("%d locks left behind, want 0", got)
72 }
73}
74