slices_test.go

  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}