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("Prepend", func(t *testing.T) {
113		s := NewSlice[string]()
114		s.Append("world")
115		s.Prepend("hello")
116
117		require.Equal(t, 2, s.Len())
118		val, ok := s.Get(0)
119		require.True(t, ok)
120		require.Equal(t, "hello", val)
121
122		val, ok = s.Get(1)
123		require.True(t, ok)
124		require.Equal(t, "world", val)
125	})
126
127	t.Run("Delete", func(t *testing.T) {
128		s := NewSliceFrom([]int{1, 2, 3, 4, 5})
129
130		// Delete middle element
131		ok := s.Delete(2)
132		require.True(t, ok)
133		require.Equal(t, 4, s.Len())
134
135		expected := []int{1, 2, 4, 5}
136		actual := slices.Collect(s.Seq())
137		require.Equal(t, expected, actual)
138
139		// Delete out of bounds
140		ok = s.Delete(10)
141		require.False(t, ok)
142		require.Equal(t, 4, s.Len())
143
144		// Delete negative index
145		ok = s.Delete(-1)
146		require.False(t, ok)
147		require.Equal(t, 4, s.Len())
148	})
149
150	t.Run("Get", func(t *testing.T) {
151		s := NewSliceFrom([]string{"a", "b", "c"})
152
153		val, ok := s.Get(1)
154		require.True(t, ok)
155		require.Equal(t, "b", val)
156
157		// Out of bounds
158		_, ok = s.Get(10)
159		require.False(t, ok)
160
161		// Negative index
162		_, ok = s.Get(-1)
163		require.False(t, ok)
164	})
165
166	t.Run("Set", func(t *testing.T) {
167		s := NewSliceFrom([]string{"a", "b", "c"})
168
169		ok := s.Set(1, "modified")
170		require.True(t, ok)
171
172		val, ok := s.Get(1)
173		require.True(t, ok)
174		require.Equal(t, "modified", val)
175
176		// Out of bounds
177		ok = s.Set(10, "invalid")
178		require.False(t, ok)
179
180		// Negative index
181		ok = s.Set(-1, "invalid")
182		require.False(t, ok)
183	})
184
185	t.Run("SetSlice", func(t *testing.T) {
186		s := NewSlice[int]()
187		s.Append(1)
188		s.Append(2)
189
190		newItems := []int{10, 20, 30}
191		s.SetSlice(newItems)
192
193		require.Equal(t, 3, s.Len())
194		require.Equal(t, newItems, slices.Collect(s.Seq()))
195
196		// Verify it's a copy
197		newItems[0] = 999
198		val, ok := s.Get(0)
199		require.True(t, ok)
200		require.Equal(t, 10, val)
201	})
202
203	t.Run("Slice", func(t *testing.T) {
204		original := []int{1, 2, 3}
205		s := NewSliceFrom(original)
206
207		copied := slices.Collect(s.Seq())
208		require.Equal(t, original, copied)
209
210		// Verify it's a copy
211		copied[0] = 999
212		val, ok := s.Get(0)
213		require.True(t, ok)
214		require.Equal(t, 1, val)
215	})
216
217	t.Run("Seq", func(t *testing.T) {
218		s := NewSliceFrom([]int{1, 2, 3})
219
220		var result []int
221		for v := range s.Seq() {
222			result = append(result, v)
223		}
224
225		require.Equal(t, []int{1, 2, 3}, result)
226	})
227
228	t.Run("SeqWithIndex", func(t *testing.T) {
229		s := NewSliceFrom([]string{"a", "b", "c"})
230
231		var indices []int
232		var values []string
233		for i, v := range s.Seq2() {
234			indices = append(indices, i)
235			values = append(values, v)
236		}
237
238		require.Equal(t, []int{0, 1, 2}, indices)
239		require.Equal(t, []string{"a", "b", "c"}, values)
240	})
241
242	t.Run("ConcurrentAccess", func(t *testing.T) {
243		s := NewSlice[int]()
244		const numGoroutines = 100
245		const itemsPerGoroutine = 10
246
247		var wg sync.WaitGroup
248
249		// Concurrent appends
250		for i := range numGoroutines {
251			wg.Add(2)
252			go func(start int) {
253				defer wg.Done()
254				for j := range itemsPerGoroutine {
255					s.Append(start*itemsPerGoroutine + j)
256				}
257			}(i)
258			go func() {
259				defer wg.Done()
260				for range itemsPerGoroutine {
261					s.Len() // Just read the length
262				}
263			}()
264		}
265
266		wg.Wait()
267
268		// Should have all items
269		require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
270	})
271}