slices_test.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package csync
  2. import (
  3. "slices"
  4. "sync"
  5. "sync/atomic"
  6. "testing"
  7. "testing/synctest"
  8. "time"
  9. "github.com/stretchr/testify/require"
  10. )
  11. func TestLazySlice_Seq(t *testing.T) {
  12. t.Parallel()
  13. synctest.Test(t, func(t *testing.T) {
  14. t.Helper()
  15. data := []string{"a", "b", "c"}
  16. s := NewLazySlice(func() []string {
  17. time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
  18. return data
  19. })
  20. require.Equal(t, data, slices.Collect(s.Seq()))
  21. })
  22. }
  23. func TestLazySlice_SeqWaitsForLoading(t *testing.T) {
  24. t.Parallel()
  25. synctest.Test(t, func(t *testing.T) {
  26. t.Helper()
  27. var loaded atomic.Bool
  28. data := []string{"x", "y", "z"}
  29. s := NewLazySlice(func() []string {
  30. time.Sleep(100 * time.Millisecond)
  31. loaded.Store(true)
  32. return data
  33. })
  34. require.False(t, loaded.Load(), "should not be loaded immediately")
  35. require.Equal(t, data, slices.Collect(s.Seq()))
  36. require.True(t, loaded.Load(), "should be loaded after Seq")
  37. })
  38. }
  39. func TestLazySlice_EmptySlice(t *testing.T) {
  40. t.Parallel()
  41. s := NewLazySlice(func() []string {
  42. return []string{}
  43. })
  44. require.Empty(t, slices.Collect(s.Seq()))
  45. }
  46. func TestLazySlice_EarlyBreak(t *testing.T) {
  47. t.Parallel()
  48. synctest.Test(t, func(t *testing.T) {
  49. t.Helper()
  50. data := []string{"a", "b", "c", "d", "e"}
  51. s := NewLazySlice(func() []string {
  52. time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
  53. return data
  54. })
  55. var result []string
  56. for v := range s.Seq() {
  57. result = append(result, v)
  58. if len(result) == 2 {
  59. break
  60. }
  61. }
  62. require.Equal(t, []string{"a", "b"}, result)
  63. })
  64. }
  65. func TestSlice(t *testing.T) {
  66. t.Run("NewSlice", func(t *testing.T) {
  67. s := NewSlice[int]()
  68. require.Equal(t, 0, s.Len())
  69. })
  70. t.Run("NewSliceFrom", func(t *testing.T) {
  71. original := []int{1, 2, 3}
  72. s := NewSliceFrom(original)
  73. require.Equal(t, 3, s.Len())
  74. // Verify it's a copy, not a reference
  75. original[0] = 999
  76. val, ok := s.Get(0)
  77. require.True(t, ok)
  78. require.Equal(t, 1, val)
  79. })
  80. t.Run("Append", func(t *testing.T) {
  81. s := NewSlice[string]()
  82. s.Append("hello")
  83. s.Append("world")
  84. require.Equal(t, 2, s.Len())
  85. val, ok := s.Get(0)
  86. require.True(t, ok)
  87. require.Equal(t, "hello", val)
  88. val, ok = s.Get(1)
  89. require.True(t, ok)
  90. require.Equal(t, "world", val)
  91. })
  92. t.Run("Get", func(t *testing.T) {
  93. s := NewSliceFrom([]string{"a", "b", "c"})
  94. val, ok := s.Get(1)
  95. require.True(t, ok)
  96. require.Equal(t, "b", val)
  97. // Out of bounds
  98. _, ok = s.Get(10)
  99. require.False(t, ok)
  100. // Negative index
  101. _, ok = s.Get(-1)
  102. require.False(t, ok)
  103. })
  104. t.Run("SetSlice", func(t *testing.T) {
  105. s := NewSlice[int]()
  106. s.Append(1)
  107. s.Append(2)
  108. newItems := []int{10, 20, 30}
  109. s.SetSlice(newItems)
  110. require.Equal(t, 3, s.Len())
  111. require.Equal(t, newItems, slices.Collect(s.Seq()))
  112. // Verify it's a copy
  113. newItems[0] = 999
  114. val, ok := s.Get(0)
  115. require.True(t, ok)
  116. require.Equal(t, 10, val)
  117. })
  118. t.Run("Slice", func(t *testing.T) {
  119. original := []int{1, 2, 3}
  120. s := NewSliceFrom(original)
  121. copied := slices.Collect(s.Seq())
  122. require.Equal(t, original, copied)
  123. // Verify it's a copy
  124. copied[0] = 999
  125. val, ok := s.Get(0)
  126. require.True(t, ok)
  127. require.Equal(t, 1, val)
  128. })
  129. t.Run("Seq", func(t *testing.T) {
  130. s := NewSliceFrom([]int{1, 2, 3})
  131. var result []int
  132. for v := range s.Seq() {
  133. result = append(result, v)
  134. }
  135. require.Equal(t, []int{1, 2, 3}, result)
  136. })
  137. t.Run("SeqWithIndex", func(t *testing.T) {
  138. s := NewSliceFrom([]string{"a", "b", "c"})
  139. var indices []int
  140. var values []string
  141. for i, v := range s.Seq2() {
  142. indices = append(indices, i)
  143. values = append(values, v)
  144. }
  145. require.Equal(t, []int{0, 1, 2}, indices)
  146. require.Equal(t, []string{"a", "b", "c"}, values)
  147. })
  148. t.Run("ConcurrentAccess", func(t *testing.T) {
  149. s := NewSlice[int]()
  150. const numGoroutines = 100
  151. const itemsPerGoroutine = 10
  152. var wg sync.WaitGroup
  153. // Concurrent appends
  154. for i := range numGoroutines {
  155. wg.Add(2)
  156. go func(start int) {
  157. defer wg.Done()
  158. for j := range itemsPerGoroutine {
  159. s.Append(start*itemsPerGoroutine + j)
  160. }
  161. }(i)
  162. go func() {
  163. defer wg.Done()
  164. for range itemsPerGoroutine {
  165. s.Len() // Just read the length
  166. }
  167. }()
  168. }
  169. wg.Wait()
  170. // Should have all items
  171. require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
  172. })
  173. }