slices_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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("Prepend", func(t *testing.T) {
  93. s := NewSlice[string]()
  94. s.Append("world")
  95. s.Prepend("hello")
  96. require.Equal(t, 2, s.Len())
  97. val, ok := s.Get(0)
  98. require.True(t, ok)
  99. require.Equal(t, "hello", val)
  100. val, ok = s.Get(1)
  101. require.True(t, ok)
  102. require.Equal(t, "world", val)
  103. })
  104. t.Run("Delete", func(t *testing.T) {
  105. s := NewSliceFrom([]int{1, 2, 3, 4, 5})
  106. // Delete middle element
  107. ok := s.Delete(2)
  108. require.True(t, ok)
  109. require.Equal(t, 4, s.Len())
  110. expected := []int{1, 2, 4, 5}
  111. actual := slices.Collect(s.Seq())
  112. require.Equal(t, expected, actual)
  113. // Delete out of bounds
  114. ok = s.Delete(10)
  115. require.False(t, ok)
  116. require.Equal(t, 4, s.Len())
  117. // Delete negative index
  118. ok = s.Delete(-1)
  119. require.False(t, ok)
  120. require.Equal(t, 4, s.Len())
  121. })
  122. t.Run("Get", func(t *testing.T) {
  123. s := NewSliceFrom([]string{"a", "b", "c"})
  124. val, ok := s.Get(1)
  125. require.True(t, ok)
  126. require.Equal(t, "b", val)
  127. // Out of bounds
  128. _, ok = s.Get(10)
  129. require.False(t, ok)
  130. // Negative index
  131. _, ok = s.Get(-1)
  132. require.False(t, ok)
  133. })
  134. t.Run("Set", func(t *testing.T) {
  135. s := NewSliceFrom([]string{"a", "b", "c"})
  136. ok := s.Set(1, "modified")
  137. require.True(t, ok)
  138. val, ok := s.Get(1)
  139. require.True(t, ok)
  140. require.Equal(t, "modified", val)
  141. // Out of bounds
  142. ok = s.Set(10, "invalid")
  143. require.False(t, ok)
  144. // Negative index
  145. ok = s.Set(-1, "invalid")
  146. require.False(t, ok)
  147. })
  148. t.Run("SetSlice", func(t *testing.T) {
  149. s := NewSlice[int]()
  150. s.Append(1)
  151. s.Append(2)
  152. newItems := []int{10, 20, 30}
  153. s.SetSlice(newItems)
  154. require.Equal(t, 3, s.Len())
  155. require.Equal(t, newItems, slices.Collect(s.Seq()))
  156. // Verify it's a copy
  157. newItems[0] = 999
  158. val, ok := s.Get(0)
  159. require.True(t, ok)
  160. require.Equal(t, 10, val)
  161. })
  162. t.Run("Slice", func(t *testing.T) {
  163. original := []int{1, 2, 3}
  164. s := NewSliceFrom(original)
  165. copied := slices.Collect(s.Seq())
  166. require.Equal(t, original, copied)
  167. // Verify it's a copy
  168. copied[0] = 999
  169. val, ok := s.Get(0)
  170. require.True(t, ok)
  171. require.Equal(t, 1, val)
  172. })
  173. t.Run("Seq", func(t *testing.T) {
  174. s := NewSliceFrom([]int{1, 2, 3})
  175. var result []int
  176. for v := range s.Seq() {
  177. result = append(result, v)
  178. }
  179. require.Equal(t, []int{1, 2, 3}, result)
  180. })
  181. t.Run("SeqWithIndex", func(t *testing.T) {
  182. s := NewSliceFrom([]string{"a", "b", "c"})
  183. var indices []int
  184. var values []string
  185. for i, v := range s.Seq2() {
  186. indices = append(indices, i)
  187. values = append(values, v)
  188. }
  189. require.Equal(t, []int{0, 1, 2}, indices)
  190. require.Equal(t, []string{"a", "b", "c"}, values)
  191. })
  192. t.Run("ConcurrentAccess", func(t *testing.T) {
  193. s := NewSlice[int]()
  194. const numGoroutines = 100
  195. const itemsPerGoroutine = 10
  196. var wg sync.WaitGroup
  197. // Concurrent appends
  198. for i := range numGoroutines {
  199. wg.Add(2)
  200. go func(start int) {
  201. defer wg.Done()
  202. for j := range itemsPerGoroutine {
  203. s.Append(start*itemsPerGoroutine + j)
  204. }
  205. }(i)
  206. go func() {
  207. defer wg.Done()
  208. for range itemsPerGoroutine {
  209. s.Len() // Just read the length
  210. }
  211. }()
  212. }
  213. wg.Wait()
  214. // Should have all items
  215. require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
  216. })
  217. }