slices_test.go

  1package csync
  2
  3import (
  4	"sync"
  5	"sync/atomic"
  6	"testing"
  7	"time"
  8
  9	"github.com/stretchr/testify/require"
 10)
 11
 12func TestLazySlice_Seq(t *testing.T) {
 13	t.Parallel()
 14
 15	data := []string{"a", "b", "c"}
 16	s := NewLazySlice(func() []string {
 17		// TODO: use synctest when new Go is out.
 18		time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
 19		return data
 20	})
 21
 22	var result []string
 23	for v := range s.Seq() {
 24		result = append(result, v)
 25	}
 26
 27	require.Equal(t, data, result)
 28}
 29
 30func TestLazySlice_SeqWaitsForLoading(t *testing.T) {
 31	t.Parallel()
 32
 33	var loaded atomic.Bool
 34	data := []string{"x", "y", "z"}
 35
 36	s := NewLazySlice(func() []string {
 37		// TODO: use synctest when new Go is out.
 38		time.Sleep(100 * time.Millisecond)
 39		loaded.Store(true)
 40		return data
 41	})
 42
 43	require.False(t, loaded.Load(), "should not be loaded immediately")
 44
 45	var result []string
 46	for v := range s.Seq() {
 47		result = append(result, v)
 48	}
 49
 50	require.True(t, loaded.Load(), "should be loaded after Seq")
 51	require.Equal(t, data, result)
 52}
 53
 54func TestLazySlice_EmptySlice(t *testing.T) {
 55	t.Parallel()
 56
 57	s := NewLazySlice(func() []string {
 58		return []string{}
 59	})
 60
 61	var result []string
 62	for v := range s.Seq() {
 63		result = append(result, v)
 64	}
 65
 66	require.Empty(t, result)
 67}
 68
 69func TestLazySlice_EarlyBreak(t *testing.T) {
 70	t.Parallel()
 71
 72	data := []string{"a", "b", "c", "d", "e"}
 73	s := NewLazySlice(func() []string {
 74		// TODO: use synctest when new Go is out.
 75		time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
 76		return data
 77	})
 78
 79	var result []string
 80	for v := range s.Seq() {
 81		result = append(result, v)
 82		if len(result) == 2 {
 83			break
 84		}
 85	}
 86
 87	require.Equal(t, []string{"a", "b"}, result)
 88}
 89
 90func TestSlice(t *testing.T) {
 91	t.Run("NewSlice", func(t *testing.T) {
 92		s := NewSlice[int]()
 93		require.Equal(t, 0, s.Len())
 94	})
 95
 96	t.Run("NewSliceFrom", func(t *testing.T) {
 97		original := []int{1, 2, 3}
 98		s := NewSliceFrom(original)
 99		require.Equal(t, 3, s.Len())
100
101		// Verify it's a copy, not a reference
102		original[0] = 999
103		val, ok := s.Get(0)
104		require.True(t, ok)
105		require.Equal(t, 1, val)
106	})
107
108	t.Run("Append", func(t *testing.T) {
109		s := NewSlice[string]()
110		s.Append("hello")
111		s.Append("world")
112
113		require.Equal(t, 2, s.Len())
114		val, ok := s.Get(0)
115		require.True(t, ok)
116		require.Equal(t, "hello", val)
117
118		val, ok = s.Get(1)
119		require.True(t, ok)
120		require.Equal(t, "world", val)
121	})
122
123	t.Run("Prepend", func(t *testing.T) {
124		s := NewSlice[string]()
125		s.Append("world")
126		s.Prepend("hello")
127
128		require.Equal(t, 2, s.Len())
129		val, ok := s.Get(0)
130		require.True(t, ok)
131		require.Equal(t, "hello", val)
132
133		val, ok = s.Get(1)
134		require.True(t, ok)
135		require.Equal(t, "world", val)
136	})
137
138	t.Run("Delete", func(t *testing.T) {
139		s := NewSliceFrom([]int{1, 2, 3, 4, 5})
140
141		// Delete middle element
142		ok := s.Delete(2)
143		require.True(t, ok)
144		require.Equal(t, 4, s.Len())
145
146		expected := []int{1, 2, 4, 5}
147		actual := s.Slice()
148		require.Equal(t, expected, actual)
149
150		// Delete out of bounds
151		ok = s.Delete(10)
152		require.False(t, ok)
153		require.Equal(t, 4, s.Len())
154
155		// Delete negative index
156		ok = s.Delete(-1)
157		require.False(t, ok)
158		require.Equal(t, 4, s.Len())
159	})
160
161	t.Run("Get", func(t *testing.T) {
162		s := NewSliceFrom([]string{"a", "b", "c"})
163
164		val, ok := s.Get(1)
165		require.True(t, ok)
166		require.Equal(t, "b", val)
167
168		// Out of bounds
169		_, ok = s.Get(10)
170		require.False(t, ok)
171
172		// Negative index
173		_, ok = s.Get(-1)
174		require.False(t, ok)
175	})
176
177	t.Run("Set", func(t *testing.T) {
178		s := NewSliceFrom([]string{"a", "b", "c"})
179
180		ok := s.Set(1, "modified")
181		require.True(t, ok)
182
183		val, ok := s.Get(1)
184		require.True(t, ok)
185		require.Equal(t, "modified", val)
186
187		// Out of bounds
188		ok = s.Set(10, "invalid")
189		require.False(t, ok)
190
191		// Negative index
192		ok = s.Set(-1, "invalid")
193		require.False(t, ok)
194	})
195
196	t.Run("SetSlice", func(t *testing.T) {
197		s := NewSlice[int]()
198		s.Append(1)
199		s.Append(2)
200
201		newItems := []int{10, 20, 30}
202		s.SetSlice(newItems)
203
204		require.Equal(t, 3, s.Len())
205		require.Equal(t, newItems, s.Slice())
206
207		// Verify it's a copy
208		newItems[0] = 999
209		val, ok := s.Get(0)
210		require.True(t, ok)
211		require.Equal(t, 10, val)
212	})
213
214	t.Run("Clear", func(t *testing.T) {
215		s := NewSliceFrom([]int{1, 2, 3})
216		require.Equal(t, 3, s.Len())
217
218		s.Clear()
219		require.Equal(t, 0, s.Len())
220	})
221
222	t.Run("Slice", func(t *testing.T) {
223		original := []int{1, 2, 3}
224		s := NewSliceFrom(original)
225
226		copy := s.Slice()
227		require.Equal(t, original, copy)
228
229		// Verify it's a copy
230		copy[0] = 999
231		val, ok := s.Get(0)
232		require.True(t, ok)
233		require.Equal(t, 1, val)
234	})
235
236	t.Run("Seq", func(t *testing.T) {
237		s := NewSliceFrom([]int{1, 2, 3})
238
239		var result []int
240		for v := range s.Seq() {
241			result = append(result, v)
242		}
243
244		require.Equal(t, []int{1, 2, 3}, result)
245	})
246
247	t.Run("SeqWithIndex", func(t *testing.T) {
248		s := NewSliceFrom([]string{"a", "b", "c"})
249
250		var indices []int
251		var values []string
252		for i, v := range s.Seq2() {
253			indices = append(indices, i)
254			values = append(values, v)
255		}
256
257		require.Equal(t, []int{0, 1, 2}, indices)
258		require.Equal(t, []string{"a", "b", "c"}, values)
259	})
260
261	t.Run("ConcurrentAccess", func(t *testing.T) {
262		s := NewSlice[int]()
263		const numGoroutines = 100
264		const itemsPerGoroutine = 10
265
266		var wg sync.WaitGroup
267
268		// Concurrent appends
269		for i := range numGoroutines {
270			wg.Add(1)
271			go func(start int) {
272				defer wg.Done()
273				for j := range itemsPerGoroutine {
274					s.Append(start*itemsPerGoroutine + j)
275				}
276			}(i)
277		}
278
279		// Concurrent reads
280		for range numGoroutines {
281			wg.Add(1)
282			go func() {
283				defer wg.Done()
284				for range itemsPerGoroutine {
285					s.Len() // Just read the length
286				}
287			}()
288		}
289
290		wg.Wait()
291
292		// Should have all items
293		require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
294	})
295}