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("Slice", func(t *testing.T) {
217 original := []int{1, 2, 3}
218 s := NewSliceFrom(original)
219
220 copied := slices.Collect(s.Seq())
221 assert.Equal(t, original, copied)
222
223 // Verify it's a copy
224 copied[0] = 999
225 val, ok := s.Get(0)
226 require.True(t, ok)
227 assert.Equal(t, 1, val)
228 })
229
230 t.Run("Seq", func(t *testing.T) {
231 s := NewSliceFrom([]int{1, 2, 3})
232
233 var result []int
234 for v := range s.Seq() {
235 result = append(result, v)
236 }
237
238 assert.Equal(t, []int{1, 2, 3}, result)
239 })
240
241 t.Run("SeqWithIndex", func(t *testing.T) {
242 s := NewSliceFrom([]string{"a", "b", "c"})
243
244 var indices []int
245 var values []string
246 for i, v := range s.Seq2() {
247 indices = append(indices, i)
248 values = append(values, v)
249 }
250
251 assert.Equal(t, []int{0, 1, 2}, indices)
252 assert.Equal(t, []string{"a", "b", "c"}, values)
253 })
254
255 t.Run("ConcurrentAccess", func(t *testing.T) {
256 s := NewSlice[int]()
257 const numGoroutines = 100
258 const itemsPerGoroutine = 10
259
260 var wg sync.WaitGroup
261
262 // Concurrent appends
263 for i := 0; i < numGoroutines; i++ {
264 wg.Add(1)
265 go func(start int) {
266 defer wg.Done()
267 for j := 0; j < itemsPerGoroutine; j++ {
268 s.Append(start*itemsPerGoroutine + j)
269 }
270 }(i)
271 }
272
273 // Concurrent reads
274 for i := 0; i < numGoroutines; i++ {
275 wg.Add(1)
276 go func() {
277 defer wg.Done()
278 for j := 0; j < itemsPerGoroutine; j++ {
279 s.Len() // Just read the length
280 }
281 }()
282 }
283
284 wg.Wait()
285
286 // Should have all items
287 assert.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
288 })
289}