v0.33.0 62a09f5

lru_test.go RawHistoryFolder

1package cache
2
3import (
4	"sync"
5	"testing"
6
7	"github.com/stretchr/testify/assert"
8	"github.com/stretchr/testify/require"
9)
10
11func TestLRU_BasicOperations(t *testing.T) {
12	cache := NewLRU[string, int](3)
13
14	// Test Set and Get
15	cache.Set("a", 1)
16	cache.Set("b", 2)
17	cache.Set("c", 3)
18
19	val, ok := cache.Get("a")
20	assert.True(t, ok)
21	assert.Equal(t, 1, val)
22
23	val, ok = cache.Get("b")
24	assert.True(t, ok)
25	assert.Equal(t, 2, val)
26
27	// Test not found
28	val, ok = cache.Get("notfound")
29	assert.False(t, ok)
30	assert.Equal(t, 0, val)
31
32	assert.Equal(t, 3, cache.Len())
33}
34
35func TestLRU_Eviction(t *testing.T) {
36	cache := NewLRU[string, int](2)
37
38	cache.Set("a", 1)
39	cache.Set("b", 2)
40	// Cache is now at capacity: [b, a] (b is most recent)
41
42	// Adding "c" should evict "a" (least recently used)
43	cache.Set("c", 3)
44
45	_, ok := cache.Get("a")
46	assert.False(t, ok, "a should have been evicted")
47
48	val, ok := cache.Get("b")
49	assert.True(t, ok)
50	assert.Equal(t, 2, val)
51
52	val, ok = cache.Get("c")
53	assert.True(t, ok)
54	assert.Equal(t, 3, val)
55}
56
57func TestLRU_GetUpdatesRecency(t *testing.T) {
58	cache := NewLRU[string, int](2)
59
60	cache.Set("a", 1)
61	cache.Set("b", 2)
62	// Order: [b, a]
63
64	// Access "a" to make it most recent
65	cache.Get("a")
66	// Order: [a, b]
67
68	// Adding "c" should now evict "b" (least recently used)
69	cache.Set("c", 3)
70
71	val, ok := cache.Get("a")
72	assert.True(t, ok, "a should still exist")
73	assert.Equal(t, 1, val)
74
75	_, ok = cache.Get("b")
76	assert.False(t, ok, "b should have been evicted")
77}
78
79func TestLRU_UpdateExisting(t *testing.T) {
80	cache := NewLRU[string, int](2)
81
82	cache.Set("a", 1)
83	cache.Set("b", 2)
84
85	// Update "a" with new value
86	cache.Set("a", 100)
87
88	val, ok := cache.Get("a")
89	assert.True(t, ok)
90	assert.Equal(t, 100, val)
91
92	// Cache should still have 2 items (no duplicate)
93	assert.Equal(t, 2, cache.Len())
94}
95
96func TestLRU_Remove(t *testing.T) {
97	cache := NewLRU[string, int](3)
98
99	cache.Set("a", 1)
100	cache.Set("b", 2)
101	cache.Set("c", 3)
102
103	cache.Remove("b")
104
105	_, ok := cache.Get("b")
106	assert.False(t, ok)
107	assert.Equal(t, 2, cache.Len())
108
109	// Remove non-existent key should not panic
110	cache.Remove("notfound")
111	assert.Equal(t, 2, cache.Len())
112}
113
114func TestLRU_Clear(t *testing.T) {
115	cache := NewLRU[string, int](3)
116
117	cache.Set("a", 1)
118	cache.Set("b", 2)
119	cache.Set("c", 3)
120
121	cache.Clear()
122
123	assert.Equal(t, 0, cache.Len())
124	_, ok := cache.Get("a")
125	assert.False(t, ok)
126}
127
128func TestLRU_ZeroCapacity(t *testing.T) {
129	// Should default to capacity of 1
130	cache := NewLRU[string, int](0)
131
132	cache.Set("a", 1)
133	val, ok := cache.Get("a")
134	assert.True(t, ok)
135	assert.Equal(t, 1, val)
136
137	// Adding another should evict first
138	cache.Set("b", 2)
139	_, ok = cache.Get("a")
140	assert.False(t, ok)
141}
142
143func TestLRU_ConcurrentAccess(t *testing.T) {
144	cache := NewLRU[int, int](100)
145	var wg sync.WaitGroup
146
147	// Concurrent writes
148	for i := range 100 {
149		wg.Add(1)
150		go func(i int) {
151			defer wg.Done()
152			cache.Set(i, i*10)
153		}(i)
154	}
155	wg.Wait()
156
157	// Concurrent reads
158	for i := range 100 {
159		wg.Add(1)
160		go func(i int) {
161			defer wg.Done()
162			cache.Get(i)
163		}(i)
164	}
165	wg.Wait()
166
167	// Concurrent mixed operations
168	for i := range 50 {
169		wg.Add(3)
170		go func(i int) {
171			defer wg.Done()
172			cache.Set(i+100, i)
173		}(i)
174		go func(i int) {
175			defer wg.Done()
176			cache.Get(i)
177		}(i)
178		go func(i int) {
179			defer wg.Done()
180			cache.Remove(i + 50)
181		}(i)
182	}
183	wg.Wait()
184
185	// Should not panic and cache should be in valid state
186	require.LessOrEqual(t, cache.Len(), 100)
187}
188
189func TestLRU_PointerValues(t *testing.T) {
190	type Data struct {
191		Name  string
192		Value float64
193	}
194
195	cache := NewLRU[string, *Data](2)
196
197	cache.Set("key1", &Data{Name: "test1", Value: 1.5})
198	cache.Set("key2", &Data{Name: "test2", Value: 2.5})
199
200	val, ok := cache.Get("key1")
201	require.True(t, ok)
202	assert.Equal(t, "test1", val.Name)
203	assert.InDelta(t, 1.5, val.Value, 0.001)
204
205	// Update pointer value
206	cache.Set("key1", &Data{Name: "updated", Value: 99.9})
207
208	val, ok = cache.Get("key1")
209	require.True(t, ok)
210	assert.Equal(t, "updated", val.Name)
211	assert.InDelta(t, 99.9, val.Value, 0.001)
212}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close