1package csync
2
3import (
4 "slices"
5 "sync"
6 "sync/atomic"
7 "testing"
8 "testing/synctest"
9 "time"
10
11 "github.com/stretchr/testify/require"
12)
13
14func TestLazySlice_Seq(t *testing.T) {
15 t.Parallel()
16
17 synctest.Test(t, func(t *testing.T) {
18 t.Helper()
19 data := []string{"a", "b", "c"}
20 s := NewLazySlice(func() []string {
21 time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
22 return data
23 })
24 require.Equal(t, data, slices.Collect(s.Seq()))
25 })
26}
27
28func TestLazySlice_SeqWaitsForLoading(t *testing.T) {
29 t.Parallel()
30 synctest.Test(t, func(t *testing.T) {
31 t.Helper()
32
33 var loaded atomic.Bool
34 data := []string{"x", "y", "z"}
35
36 s := NewLazySlice(func() []string {
37 time.Sleep(100 * time.Millisecond)
38 loaded.Store(true)
39 return data
40 })
41
42 require.False(t, loaded.Load(), "should not be loaded immediately")
43 require.Equal(t, data, slices.Collect(s.Seq()))
44 require.True(t, loaded.Load(), "should be loaded after Seq")
45 })
46}
47
48func TestLazySlice_EmptySlice(t *testing.T) {
49 t.Parallel()
50 s := NewLazySlice(func() []string {
51 return []string{}
52 })
53 require.Empty(t, slices.Collect(s.Seq()))
54}
55
56func TestLazySlice_EarlyBreak(t *testing.T) {
57 t.Parallel()
58
59 synctest.Test(t, func(t *testing.T) {
60 t.Helper()
61 data := []string{"a", "b", "c", "d", "e"}
62 s := NewLazySlice(func() []string {
63 time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
64 return data
65 })
66
67 var result []string
68 for v := range s.Seq() {
69 result = append(result, v)
70 if len(result) == 2 {
71 break
72 }
73 }
74
75 require.Equal(t, []string{"a", "b"}, result)
76 })
77}
78
79func TestSlice(t *testing.T) {
80 t.Run("NewSlice", func(t *testing.T) {
81 s := NewSlice[int]()
82 require.Equal(t, 0, s.Len())
83 })
84
85 t.Run("NewSliceFrom", func(t *testing.T) {
86 original := []int{1, 2, 3}
87 s := NewSliceFrom(original)
88 require.Equal(t, 3, s.Len())
89
90 // Verify it's a copy, not a reference
91 original[0] = 999
92 val, ok := s.Get(0)
93 require.True(t, ok)
94 require.Equal(t, 1, val)
95 })
96
97 t.Run("Append", func(t *testing.T) {
98 s := NewSlice[string]()
99 s.Append("hello")
100 s.Append("world")
101
102 require.Equal(t, 2, s.Len())
103 val, ok := s.Get(0)
104 require.True(t, ok)
105 require.Equal(t, "hello", val)
106
107 val, ok = s.Get(1)
108 require.True(t, ok)
109 require.Equal(t, "world", val)
110 })
111
112 t.Run("Get", func(t *testing.T) {
113 s := NewSliceFrom([]string{"a", "b", "c"})
114
115 val, ok := s.Get(1)
116 require.True(t, ok)
117 require.Equal(t, "b", val)
118
119 // Out of bounds
120 _, ok = s.Get(10)
121 require.False(t, ok)
122
123 // Negative index
124 _, ok = s.Get(-1)
125 require.False(t, ok)
126 })
127
128 t.Run("SetSlice", func(t *testing.T) {
129 s := NewSlice[int]()
130 s.Append(1)
131 s.Append(2)
132
133 newItems := []int{10, 20, 30}
134 s.SetSlice(newItems)
135
136 require.Equal(t, 3, s.Len())
137 require.Equal(t, newItems, slices.Collect(s.Seq()))
138
139 // Verify it's a copy
140 newItems[0] = 999
141 val, ok := s.Get(0)
142 require.True(t, ok)
143 require.Equal(t, 10, val)
144 })
145
146 t.Run("Slice", func(t *testing.T) {
147 original := []int{1, 2, 3}
148 s := NewSliceFrom(original)
149
150 copied := slices.Collect(s.Seq())
151 require.Equal(t, original, copied)
152
153 // Verify it's a copy
154 copied[0] = 999
155 val, ok := s.Get(0)
156 require.True(t, ok)
157 require.Equal(t, 1, val)
158 })
159
160 t.Run("Seq", func(t *testing.T) {
161 s := NewSliceFrom([]int{1, 2, 3})
162
163 var result []int
164 for v := range s.Seq() {
165 result = append(result, v)
166 }
167
168 require.Equal(t, []int{1, 2, 3}, result)
169 })
170
171 t.Run("SeqWithIndex", func(t *testing.T) {
172 s := NewSliceFrom([]string{"a", "b", "c"})
173
174 var indices []int
175 var values []string
176 for i, v := range s.Seq2() {
177 indices = append(indices, i)
178 values = append(values, v)
179 }
180
181 require.Equal(t, []int{0, 1, 2}, indices)
182 require.Equal(t, []string{"a", "b", "c"}, values)
183 })
184
185 t.Run("ConcurrentAccess", func(t *testing.T) {
186 s := NewSlice[int]()
187 const numGoroutines = 100
188 const itemsPerGoroutine = 10
189
190 var wg sync.WaitGroup
191
192 // Concurrent appends
193 for i := range numGoroutines {
194 wg.Add(2)
195 go func(start int) {
196 defer wg.Done()
197 for j := range itemsPerGoroutine {
198 s.Append(start*itemsPerGoroutine + j)
199 }
200 }(i)
201 go func() {
202 defer wg.Done()
203 for range itemsPerGoroutine {
204 s.Len() // Just read the length
205 }
206 }()
207 }
208
209 wg.Wait()
210
211 // Should have all items
212 require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
213 })
214}