1package csync
2
3import (
4 "sync"
5 "sync/atomic"
6 "testing"
7 "time"
8
9 "github.com/stretchr/testify/require"
10)
11
12func TestLazySlice_Seq(t *testing.T) {
13 t.Parallel()
14
15 data := []string{"a", "b", "c"}
16 s := NewLazySlice(func() []string {
17 // TODO: use synctest when new Go is out.
18 time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
19 return data
20 })
21
22 var result []string
23 for v := range s.Seq() {
24 result = append(result, v)
25 }
26
27 require.Equal(t, data, result)
28}
29
30func TestLazySlice_SeqWaitsForLoading(t *testing.T) {
31 t.Parallel()
32
33 var loaded atomic.Bool
34 data := []string{"x", "y", "z"}
35
36 s := NewLazySlice(func() []string {
37 // TODO: use synctest when new Go is out.
38 time.Sleep(100 * time.Millisecond)
39 loaded.Store(true)
40 return data
41 })
42
43 require.False(t, loaded.Load(), "should not be loaded immediately")
44
45 var result []string
46 for v := range s.Seq() {
47 result = append(result, v)
48 }
49
50 require.True(t, loaded.Load(), "should be loaded after Seq")
51 require.Equal(t, data, result)
52}
53
54func TestLazySlice_EmptySlice(t *testing.T) {
55 t.Parallel()
56
57 s := NewLazySlice(func() []string {
58 return []string{}
59 })
60
61 var result []string
62 for v := range s.Seq() {
63 result = append(result, v)
64 }
65
66 require.Empty(t, result)
67}
68
69func TestLazySlice_EarlyBreak(t *testing.T) {
70 t.Parallel()
71
72 data := []string{"a", "b", "c", "d", "e"}
73 s := NewLazySlice(func() []string {
74 // TODO: use synctest when new Go is out.
75 time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
76 return data
77 })
78
79 var result []string
80 for v := range s.Seq() {
81 result = append(result, v)
82 if len(result) == 2 {
83 break
84 }
85 }
86
87 require.Equal(t, []string{"a", "b"}, result)
88}
89
90func TestSlice(t *testing.T) {
91 t.Run("NewSlice", func(t *testing.T) {
92 s := NewSlice[int]()
93 require.Equal(t, 0, s.Len())
94 })
95
96 t.Run("NewSliceFrom", func(t *testing.T) {
97 original := []int{1, 2, 3}
98 s := NewSliceFrom(original)
99 require.Equal(t, 3, s.Len())
100
101 // Verify it's a copy, not a reference
102 original[0] = 999
103 val, ok := s.Get(0)
104 require.True(t, ok)
105 require.Equal(t, 1, val)
106 })
107
108 t.Run("Append", func(t *testing.T) {
109 s := NewSlice[string]()
110 s.Append("hello")
111 s.Append("world")
112
113 require.Equal(t, 2, s.Len())
114 val, ok := s.Get(0)
115 require.True(t, ok)
116 require.Equal(t, "hello", val)
117
118 val, ok = s.Get(1)
119 require.True(t, ok)
120 require.Equal(t, "world", val)
121 })
122
123 t.Run("Prepend", func(t *testing.T) {
124 s := NewSlice[string]()
125 s.Append("world")
126 s.Prepend("hello")
127
128 require.Equal(t, 2, s.Len())
129 val, ok := s.Get(0)
130 require.True(t, ok)
131 require.Equal(t, "hello", val)
132
133 val, ok = s.Get(1)
134 require.True(t, ok)
135 require.Equal(t, "world", val)
136 })
137
138 t.Run("Delete", func(t *testing.T) {
139 s := NewSliceFrom([]int{1, 2, 3, 4, 5})
140
141 // Delete middle element
142 ok := s.Delete(2)
143 require.True(t, ok)
144 require.Equal(t, 4, s.Len())
145
146 expected := []int{1, 2, 4, 5}
147 actual := s.Slice()
148 require.Equal(t, expected, actual)
149
150 // Delete out of bounds
151 ok = s.Delete(10)
152 require.False(t, ok)
153 require.Equal(t, 4, s.Len())
154
155 // Delete negative index
156 ok = s.Delete(-1)
157 require.False(t, ok)
158 require.Equal(t, 4, s.Len())
159 })
160
161 t.Run("Get", func(t *testing.T) {
162 s := NewSliceFrom([]string{"a", "b", "c"})
163
164 val, ok := s.Get(1)
165 require.True(t, ok)
166 require.Equal(t, "b", val)
167
168 // Out of bounds
169 _, ok = s.Get(10)
170 require.False(t, ok)
171
172 // Negative index
173 _, ok = s.Get(-1)
174 require.False(t, ok)
175 })
176
177 t.Run("Set", func(t *testing.T) {
178 s := NewSliceFrom([]string{"a", "b", "c"})
179
180 ok := s.Set(1, "modified")
181 require.True(t, ok)
182
183 val, ok := s.Get(1)
184 require.True(t, ok)
185 require.Equal(t, "modified", val)
186
187 // Out of bounds
188 ok = s.Set(10, "invalid")
189 require.False(t, ok)
190
191 // Negative index
192 ok = s.Set(-1, "invalid")
193 require.False(t, ok)
194 })
195
196 t.Run("SetSlice", func(t *testing.T) {
197 s := NewSlice[int]()
198 s.Append(1)
199 s.Append(2)
200
201 newItems := []int{10, 20, 30}
202 s.SetSlice(newItems)
203
204 require.Equal(t, 3, s.Len())
205 require.Equal(t, newItems, s.Slice())
206
207 // Verify it's a copy
208 newItems[0] = 999
209 val, ok := s.Get(0)
210 require.True(t, ok)
211 require.Equal(t, 10, val)
212 })
213
214 t.Run("Clear", func(t *testing.T) {
215 s := NewSliceFrom([]int{1, 2, 3})
216 require.Equal(t, 3, s.Len())
217
218 s.Clear()
219 require.Equal(t, 0, s.Len())
220 })
221
222 t.Run("Slice", func(t *testing.T) {
223 original := []int{1, 2, 3}
224 s := NewSliceFrom(original)
225
226 copy := s.Slice()
227 require.Equal(t, original, copy)
228
229 // Verify it's a copy
230 copy[0] = 999
231 val, ok := s.Get(0)
232 require.True(t, ok)
233 require.Equal(t, 1, val)
234 })
235
236 t.Run("Seq", func(t *testing.T) {
237 s := NewSliceFrom([]int{1, 2, 3})
238
239 var result []int
240 for v := range s.Seq() {
241 result = append(result, v)
242 }
243
244 require.Equal(t, []int{1, 2, 3}, result)
245 })
246
247 t.Run("SeqWithIndex", func(t *testing.T) {
248 s := NewSliceFrom([]string{"a", "b", "c"})
249
250 var indices []int
251 var values []string
252 for i, v := range s.Seq2() {
253 indices = append(indices, i)
254 values = append(values, v)
255 }
256
257 require.Equal(t, []int{0, 1, 2}, indices)
258 require.Equal(t, []string{"a", "b", "c"}, values)
259 })
260
261 t.Run("ConcurrentAccess", func(t *testing.T) {
262 s := NewSlice[int]()
263 const numGoroutines = 100
264 const itemsPerGoroutine = 10
265
266 var wg sync.WaitGroup
267
268 // Concurrent appends
269 for i := 0; i < numGoroutines; i++ {
270 wg.Add(1)
271 go func(start int) {
272 defer wg.Done()
273 for j := 0; j < itemsPerGoroutine; j++ {
274 s.Append(start*itemsPerGoroutine + j)
275 }
276 }(i)
277 }
278
279 // Concurrent reads
280 for i := 0; i < numGoroutines; i++ {
281 wg.Add(1)
282 go func() {
283 defer wg.Done()
284 for j := 0; j < itemsPerGoroutine; j++ {
285 s.Len() // Just read the length
286 }
287 }()
288 }
289
290 wg.Wait()
291
292 // Should have all items
293 require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
294 })
295}