slices_test.go

  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}