You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
6.7 KiB

  1. package clist
  2. import (
  3. "fmt"
  4. mrand "math/rand"
  5. "runtime"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. )
  11. func TestPanicOnMaxLength(t *testing.T) {
  12. maxLength := 1000
  13. l := newWithMax(maxLength)
  14. for i := 0; i < maxLength; i++ {
  15. l.PushBack(1)
  16. }
  17. assert.Panics(t, func() {
  18. l.PushBack(1)
  19. })
  20. }
  21. func TestSmall(t *testing.T) {
  22. l := New()
  23. el1 := l.PushBack(1)
  24. el2 := l.PushBack(2)
  25. el3 := l.PushBack(3)
  26. if l.Len() != 3 {
  27. t.Error("Expected len 3, got ", l.Len())
  28. }
  29. // fmt.Printf("%p %v\n", el1, el1)
  30. // fmt.Printf("%p %v\n", el2, el2)
  31. // fmt.Printf("%p %v\n", el3, el3)
  32. r1 := l.Remove(el1)
  33. // fmt.Printf("%p %v\n", el1, el1)
  34. // fmt.Printf("%p %v\n", el2, el2)
  35. // fmt.Printf("%p %v\n", el3, el3)
  36. r2 := l.Remove(el2)
  37. // fmt.Printf("%p %v\n", el1, el1)
  38. // fmt.Printf("%p %v\n", el2, el2)
  39. // fmt.Printf("%p %v\n", el3, el3)
  40. r3 := l.Remove(el3)
  41. if r1 != 1 {
  42. t.Error("Expected 1, got ", r1)
  43. }
  44. if r2 != 2 {
  45. t.Error("Expected 2, got ", r2)
  46. }
  47. if r3 != 3 {
  48. t.Error("Expected 3, got ", r3)
  49. }
  50. if l.Len() != 0 {
  51. t.Error("Expected len 0, got ", l.Len())
  52. }
  53. }
  54. func TestGCFifo(t *testing.T) {
  55. const numElements = 10000
  56. l := New()
  57. gcCount := 0
  58. // SetFinalizer doesn't work well with circular structures,
  59. // so we construct a trivial non-circular structure to
  60. // track.
  61. type value struct {
  62. Int int
  63. }
  64. gcCh := make(chan struct{})
  65. for i := 0; i < numElements; i++ {
  66. v := new(value)
  67. v.Int = i
  68. l.PushBack(v)
  69. runtime.SetFinalizer(v, func(v *value) {
  70. gcCh <- struct{}{}
  71. })
  72. }
  73. for el := l.Front(); el != nil; {
  74. l.Remove(el)
  75. // oldEl := el
  76. el = el.Next()
  77. // oldEl.DetachPrev()
  78. // oldEl.DetachNext()
  79. }
  80. tickerQuitCh := make(chan struct{})
  81. tickerDoneCh := make(chan struct{})
  82. go func() {
  83. defer close(tickerDoneCh)
  84. ticker := time.NewTicker(250 * time.Millisecond)
  85. for {
  86. select {
  87. case <-ticker.C:
  88. runtime.GC()
  89. case <-tickerQuitCh:
  90. return
  91. }
  92. }
  93. }()
  94. for i := 0; i < numElements; i++ {
  95. <-gcCh
  96. gcCount++
  97. }
  98. close(tickerQuitCh)
  99. <-tickerDoneCh
  100. if gcCount != numElements {
  101. t.Errorf("expected gcCount to be %v, got %v", numElements,
  102. gcCount)
  103. }
  104. }
  105. func TestGCRandom(t *testing.T) {
  106. const numElements = 10000
  107. l := New()
  108. gcCount := 0
  109. // SetFinalizer doesn't work well with circular structures,
  110. // so we construct a trivial non-circular structure to
  111. // track.
  112. type value struct {
  113. Int int
  114. }
  115. gcCh := make(chan struct{})
  116. for i := 0; i < numElements; i++ {
  117. v := new(value)
  118. v.Int = i
  119. l.PushBack(v)
  120. runtime.SetFinalizer(v, func(v *value) {
  121. gcCh <- struct{}{}
  122. })
  123. }
  124. els := make([]*CElement, 0, numElements)
  125. for el := l.Front(); el != nil; el = el.Next() {
  126. els = append(els, el)
  127. }
  128. for _, i := range mrand.Perm(numElements) {
  129. el := els[i]
  130. l.Remove(el)
  131. _ = el.Next()
  132. }
  133. tickerQuitCh := make(chan struct{})
  134. tickerDoneCh := make(chan struct{})
  135. go func() {
  136. defer close(tickerDoneCh)
  137. ticker := time.NewTicker(250 * time.Millisecond)
  138. for {
  139. select {
  140. case <-ticker.C:
  141. runtime.GC()
  142. case <-tickerQuitCh:
  143. return
  144. }
  145. }
  146. }()
  147. for i := 0; i < numElements; i++ {
  148. <-gcCh
  149. gcCount++
  150. }
  151. close(tickerQuitCh)
  152. <-tickerDoneCh
  153. if gcCount != numElements {
  154. t.Errorf("expected gcCount to be %v, got %v", numElements,
  155. gcCount)
  156. }
  157. }
  158. func TestScanRightDeleteRandom(t *testing.T) {
  159. const numElements = 1000
  160. const numTimes = 100
  161. const numScanners = 10
  162. l := New()
  163. stop := make(chan struct{})
  164. els := make([]*CElement, numElements)
  165. for i := 0; i < numElements; i++ {
  166. el := l.PushBack(i)
  167. els[i] = el
  168. }
  169. // Launch scanner routines that will rapidly iterate over elements.
  170. for i := 0; i < numScanners; i++ {
  171. go func(scannerID int) {
  172. var el *CElement
  173. restartCounter := 0
  174. counter := 0
  175. FOR_LOOP:
  176. for {
  177. select {
  178. case <-stop:
  179. fmt.Println("stopped")
  180. break FOR_LOOP
  181. default:
  182. }
  183. if el == nil {
  184. el = l.frontWait()
  185. restartCounter++
  186. }
  187. el = el.Next()
  188. counter++
  189. }
  190. fmt.Printf("Scanner %v restartCounter: %v counter: %v\n", scannerID, restartCounter, counter)
  191. }(i)
  192. }
  193. // Remove an element, push back an element.
  194. for i := 0; i < numTimes; i++ {
  195. // Pick an element to remove
  196. rmElIdx := mrand.Intn(len(els))
  197. rmEl := els[rmElIdx]
  198. // Remove it
  199. l.Remove(rmEl)
  200. // fmt.Print(".")
  201. // Insert a new element
  202. newEl := l.PushBack(-1*i - 1)
  203. els[rmElIdx] = newEl
  204. if i%100000 == 0 {
  205. fmt.Printf("Pushed %vK elements so far...\n", i/1000)
  206. }
  207. }
  208. // Stop scanners
  209. close(stop)
  210. // time.Sleep(time.Second * 1)
  211. // And remove all the elements.
  212. for el := l.Front(); el != nil; el = el.Next() {
  213. l.Remove(el)
  214. }
  215. if l.Len() != 0 {
  216. t.Fatal("Failed to remove all elements from CList")
  217. }
  218. }
  219. func TestWaitChan(t *testing.T) {
  220. l := New()
  221. ch := l.WaitChan()
  222. // 1) add one element to an empty list
  223. go l.PushBack(1)
  224. <-ch
  225. // 2) and remove it
  226. el := l.Front()
  227. v := l.Remove(el)
  228. if v != 1 {
  229. t.Fatal("where is 1 coming from?")
  230. }
  231. // 3) test iterating forward and waiting for Next (NextWaitChan and Next)
  232. el = l.PushBack(0)
  233. done := make(chan struct{})
  234. pushed := 0
  235. go func() {
  236. defer close(done)
  237. for i := 1; i < 100; i++ {
  238. l.PushBack(i)
  239. pushed++
  240. time.Sleep(time.Duration(mrand.Intn(20)) * time.Millisecond)
  241. }
  242. // apply a deterministic pause so the counter has time to catch up
  243. time.Sleep(20 * time.Millisecond)
  244. }()
  245. next := el
  246. seen := 0
  247. FOR_LOOP:
  248. for {
  249. select {
  250. case <-next.NextWaitChan():
  251. next = next.Next()
  252. seen++
  253. if next == nil {
  254. t.Fatal("Next should not be nil when waiting on NextWaitChan")
  255. }
  256. case <-done:
  257. break FOR_LOOP
  258. case <-time.After(2 * time.Second):
  259. t.Fatal("max execution time")
  260. }
  261. }
  262. if pushed != seen {
  263. t.Fatalf("number of pushed items (%d) not equal to number of seen items (%d)", pushed, seen)
  264. }
  265. }
  266. func TestRemoved(t *testing.T) {
  267. l := New()
  268. el1 := l.PushBack(1)
  269. el2 := l.PushBack(2)
  270. l.Remove(el1)
  271. require.True(t, el1.Removed())
  272. require.False(t, el2.Removed())
  273. }
  274. func TestNextWaitChan(t *testing.T) {
  275. l := New()
  276. el1 := l.PushBack(1)
  277. t.Run("tail element should not have a closed nextWaitChan", func(t *testing.T) {
  278. select {
  279. case <-el1.NextWaitChan():
  280. t.Fatal("nextWaitChan should not have been closed")
  281. default:
  282. }
  283. })
  284. el2 := l.PushBack(2)
  285. t.Run("adding element should close tail nextWaitChan", func(t *testing.T) {
  286. select {
  287. case <-el1.NextWaitChan():
  288. require.NotNil(t, el1.Next())
  289. default:
  290. t.Fatal("nextWaitChan should have been closed")
  291. }
  292. select {
  293. case <-el2.NextWaitChan():
  294. t.Fatal("nextWaitChan should not have been closed")
  295. default:
  296. }
  297. })
  298. t.Run("removing element should close its nextWaitChan", func(t *testing.T) {
  299. l.Remove(el2)
  300. select {
  301. case <-el2.NextWaitChan():
  302. require.Nil(t, el2.Next())
  303. default:
  304. t.Fatal("nextWaitChan should have been closed")
  305. }
  306. })
  307. }