slices_test.go

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