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