1package csync
2
3import (
4 "slices"
5 "sync"
6 "sync/atomic"
7 "testing"
8 "time"
9
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 require.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 require.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 require.True(t, loaded.Load(), "should be loaded after Seq")
52 require.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 require.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 require.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 require.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 require.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 require.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 require.Equal(t, 2, s.Len())
115 val, ok := s.Get(0)
116 require.True(t, ok)
117 require.Equal(t, "hello", val)
118
119 val, ok = s.Get(1)
120 require.True(t, ok)
121 require.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 require.Equal(t, 2, s.Len())
130 val, ok := s.Get(0)
131 require.True(t, ok)
132 require.Equal(t, "hello", val)
133
134 val, ok = s.Get(1)
135 require.True(t, ok)
136 require.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 require.True(t, ok)
145 require.Equal(t, 4, s.Len())
146
147 expected := []int{1, 2, 4, 5}
148 actual := slices.Collect(s.Seq())
149 require.Equal(t, expected, actual)
150
151 // Delete out of bounds
152 ok = s.Delete(10)
153 require.False(t, ok)
154 require.Equal(t, 4, s.Len())
155
156 // Delete negative index
157 ok = s.Delete(-1)
158 require.False(t, ok)
159 require.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 require.Equal(t, "b", val)
168
169 // Out of bounds
170 _, ok = s.Get(10)
171 require.False(t, ok)
172
173 // Negative index
174 _, ok = s.Get(-1)
175 require.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 require.True(t, ok)
183
184 val, ok := s.Get(1)
185 require.True(t, ok)
186 require.Equal(t, "modified", val)
187
188 // Out of bounds
189 ok = s.Set(10, "invalid")
190 require.False(t, ok)
191
192 // Negative index
193 ok = s.Set(-1, "invalid")
194 require.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 require.Equal(t, 3, s.Len())
206 require.Equal(t, newItems, slices.Collect(s.Seq()))
207
208 // Verify it's a copy
209 newItems[0] = 999
210 val, ok := s.Get(0)
211 require.True(t, ok)
212 require.Equal(t, 10, val)
213 })
214
215 t.Run("Slice", func(t *testing.T) {
216 original := []int{1, 2, 3}
217 s := NewSliceFrom(original)
218
219 copied := slices.Collect(s.Seq())
220 require.Equal(t, original, copied)
221
222 // Verify it's a copy
223 copied[0] = 999
224 val, ok := s.Get(0)
225 require.True(t, ok)
226 require.Equal(t, 1, val)
227 })
228
229 t.Run("Seq", func(t *testing.T) {
230 s := NewSliceFrom([]int{1, 2, 3})
231
232 var result []int
233 for v := range s.Seq() {
234 result = append(result, v)
235 }
236
237 require.Equal(t, []int{1, 2, 3}, result)
238 })
239
240 t.Run("SeqWithIndex", func(t *testing.T) {
241 s := NewSliceFrom([]string{"a", "b", "c"})
242
243 var indices []int
244 var values []string
245 for i, v := range s.Seq2() {
246 indices = append(indices, i)
247 values = append(values, v)
248 }
249
250 require.Equal(t, []int{0, 1, 2}, indices)
251 require.Equal(t, []string{"a", "b", "c"}, values)
252 })
253
254 t.Run("ConcurrentAccess", func(t *testing.T) {
255 s := NewSlice[int]()
256 const numGoroutines = 100
257 const itemsPerGoroutine = 10
258
259 var wg sync.WaitGroup
260
261 // Concurrent appends
262 for i := range numGoroutines {
263 wg.Add(2)
264 go func(start int) {
265 defer wg.Done()
266 for j := range itemsPerGoroutine {
267 s.Append(start*itemsPerGoroutine + j)
268 }
269 }(i)
270 go func() {
271 defer wg.Done()
272 for range itemsPerGoroutine {
273 s.Len() // Just read the length
274 }
275 }()
276 }
277
278 wg.Wait()
279
280 // Should have all items
281 require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
282 })
283}