slices_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. package csync
  2. import (
  3. "slices"
  4. "sync"
  5. "sync/atomic"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestLazySlice_Seq(t *testing.T) {
  11. t.Parallel()
  12. data := []string{"a", "b", "c"}
  13. s := NewLazySlice(func() []string {
  14. // TODO: use synctest when new Go is out.
  15. time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
  16. return data
  17. })
  18. var result []string
  19. for v := range s.Seq() {
  20. result = append(result, v)
  21. }
  22. require.Equal(t, data, result)
  23. }
  24. func TestLazySlice_SeqWaitsForLoading(t *testing.T) {
  25. t.Parallel()
  26. var loaded atomic.Bool
  27. data := []string{"x", "y", "z"}
  28. s := NewLazySlice(func() []string {
  29. // TODO: use synctest when new Go is out.
  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. var result []string
  36. for v := range s.Seq() {
  37. result = append(result, v)
  38. }
  39. require.True(t, loaded.Load(), "should be loaded after Seq")
  40. require.Equal(t, data, result)
  41. }
  42. func TestLazySlice_EmptySlice(t *testing.T) {
  43. t.Parallel()
  44. s := NewLazySlice(func() []string {
  45. return []string{}
  46. })
  47. var result []string
  48. for v := range s.Seq() {
  49. result = append(result, v)
  50. }
  51. require.Empty(t, result)
  52. }
  53. func TestLazySlice_EarlyBreak(t *testing.T) {
  54. t.Parallel()
  55. data := []string{"a", "b", "c", "d", "e"}
  56. s := NewLazySlice(func() []string {
  57. // TODO: use synctest when new Go is out.
  58. time.Sleep(10 * time.Millisecond) // Small delay to ensure loading happens
  59. return data
  60. })
  61. var result []string
  62. for v := range s.Seq() {
  63. result = append(result, v)
  64. if len(result) == 2 {
  65. break
  66. }
  67. }
  68. require.Equal(t, []string{"a", "b"}, result)
  69. }
  70. func TestSlice(t *testing.T) {
  71. t.Run("NewSlice", func(t *testing.T) {
  72. s := NewSlice[int]()
  73. require.Equal(t, 0, s.Len())
  74. })
  75. t.Run("NewSliceFrom", func(t *testing.T) {
  76. original := []int{1, 2, 3}
  77. s := NewSliceFrom(original)
  78. require.Equal(t, 3, s.Len())
  79. // Verify it's a copy, not a reference
  80. original[0] = 999
  81. val, ok := s.Get(0)
  82. require.True(t, ok)
  83. require.Equal(t, 1, val)
  84. })
  85. t.Run("Append", func(t *testing.T) {
  86. s := NewSlice[string]()
  87. s.Append("hello")
  88. s.Append("world")
  89. require.Equal(t, 2, s.Len())
  90. val, ok := s.Get(0)
  91. require.True(t, ok)
  92. require.Equal(t, "hello", val)
  93. val, ok = s.Get(1)
  94. require.True(t, ok)
  95. require.Equal(t, "world", val)
  96. })
  97. t.Run("Prepend", func(t *testing.T) {
  98. s := NewSlice[string]()
  99. s.Append("world")
  100. s.Prepend("hello")
  101. require.Equal(t, 2, s.Len())
  102. val, ok := s.Get(0)
  103. require.True(t, ok)
  104. require.Equal(t, "hello", val)
  105. val, ok = s.Get(1)
  106. require.True(t, ok)
  107. require.Equal(t, "world", val)
  108. })
  109. t.Run("Delete", func(t *testing.T) {
  110. s := NewSliceFrom([]int{1, 2, 3, 4, 5})
  111. // Delete middle element
  112. ok := s.Delete(2)
  113. require.True(t, ok)
  114. require.Equal(t, 4, s.Len())
  115. expected := []int{1, 2, 4, 5}
  116. actual := slices.Collect(s.Seq())
  117. require.Equal(t, expected, actual)
  118. // Delete out of bounds
  119. ok = s.Delete(10)
  120. require.False(t, ok)
  121. require.Equal(t, 4, s.Len())
  122. // Delete negative index
  123. ok = s.Delete(-1)
  124. require.False(t, ok)
  125. require.Equal(t, 4, s.Len())
  126. })
  127. t.Run("Get", func(t *testing.T) {
  128. s := NewSliceFrom([]string{"a", "b", "c"})
  129. val, ok := s.Get(1)
  130. require.True(t, ok)
  131. require.Equal(t, "b", val)
  132. // Out of bounds
  133. _, ok = s.Get(10)
  134. require.False(t, ok)
  135. // Negative index
  136. _, ok = s.Get(-1)
  137. require.False(t, ok)
  138. })
  139. t.Run("Set", func(t *testing.T) {
  140. s := NewSliceFrom([]string{"a", "b", "c"})
  141. ok := s.Set(1, "modified")
  142. require.True(t, ok)
  143. val, ok := s.Get(1)
  144. require.True(t, ok)
  145. require.Equal(t, "modified", val)
  146. // Out of bounds
  147. ok = s.Set(10, "invalid")
  148. require.False(t, ok)
  149. // Negative index
  150. ok = s.Set(-1, "invalid")
  151. require.False(t, ok)
  152. })
  153. t.Run("SetSlice", func(t *testing.T) {
  154. s := NewSlice[int]()
  155. s.Append(1)
  156. s.Append(2)
  157. newItems := []int{10, 20, 30}
  158. s.SetSlice(newItems)
  159. require.Equal(t, 3, s.Len())
  160. require.Equal(t, newItems, slices.Collect(s.Seq()))
  161. // Verify it's a copy
  162. newItems[0] = 999
  163. val, ok := s.Get(0)
  164. require.True(t, ok)
  165. require.Equal(t, 10, val)
  166. })
  167. t.Run("Slice", func(t *testing.T) {
  168. original := []int{1, 2, 3}
  169. s := NewSliceFrom(original)
  170. copied := slices.Collect(s.Seq())
  171. require.Equal(t, original, copied)
  172. // Verify it's a copy
  173. copied[0] = 999
  174. val, ok := s.Get(0)
  175. require.True(t, ok)
  176. require.Equal(t, 1, val)
  177. })
  178. t.Run("Seq", func(t *testing.T) {
  179. s := NewSliceFrom([]int{1, 2, 3})
  180. var result []int
  181. for v := range s.Seq() {
  182. result = append(result, v)
  183. }
  184. require.Equal(t, []int{1, 2, 3}, result)
  185. })
  186. t.Run("SeqWithIndex", func(t *testing.T) {
  187. s := NewSliceFrom([]string{"a", "b", "c"})
  188. var indices []int
  189. var values []string
  190. for i, v := range s.Seq2() {
  191. indices = append(indices, i)
  192. values = append(values, v)
  193. }
  194. require.Equal(t, []int{0, 1, 2}, indices)
  195. require.Equal(t, []string{"a", "b", "c"}, values)
  196. })
  197. t.Run("ConcurrentAccess", func(t *testing.T) {
  198. s := NewSlice[int]()
  199. const numGoroutines = 100
  200. const itemsPerGoroutine = 10
  201. var wg sync.WaitGroup
  202. // Concurrent appends
  203. for i := range numGoroutines {
  204. wg.Add(2)
  205. go func(start int) {
  206. defer wg.Done()
  207. for j := range itemsPerGoroutine {
  208. s.Append(start*itemsPerGoroutine + j)
  209. }
  210. }(i)
  211. go func() {
  212. defer wg.Done()
  213. for range itemsPerGoroutine {
  214. s.Len() // Just read the length
  215. }
  216. }()
  217. }
  218. wg.Wait()
  219. // Should have all items
  220. require.Equal(t, numGoroutines*itemsPerGoroutine, s.Len())
  221. })
  222. }