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