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.

384 lines
7.2 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 = 1000000
  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(time.Second)
  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 = 1000000
  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(time.Second)
  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. for i := 1; i < 100; i++ {
  237. l.PushBack(i)
  238. pushed++
  239. time.Sleep(time.Duration(mrand.Intn(25)) * time.Millisecond)
  240. }
  241. // apply a deterministic pause so the counter has time to catch up
  242. time.Sleep(25 * time.Millisecond)
  243. close(done)
  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(10 * 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. // 4) test iterating backwards (PrevWaitChan and Prev)
  266. prev := next
  267. seen = 0
  268. FOR_LOOP2:
  269. for {
  270. select {
  271. case <-prev.PrevWaitChan():
  272. prev = prev.Prev()
  273. seen++
  274. if prev == nil {
  275. t.Fatal("expected PrevWaitChan to block forever on nil when reached first elem")
  276. }
  277. case <-time.After(3 * time.Second):
  278. break FOR_LOOP2
  279. }
  280. }
  281. if pushed != seen {
  282. t.Fatalf("number of pushed items (%d) not equal to number of seen items (%d)", pushed, seen)
  283. }
  284. }
  285. func TestRemoved(t *testing.T) {
  286. l := New()
  287. el1 := l.PushBack(1)
  288. el2 := l.PushBack(2)
  289. l.Remove(el1)
  290. require.True(t, el1.Removed())
  291. require.False(t, el2.Removed())
  292. }
  293. func TestNextWaitChan(t *testing.T) {
  294. l := New()
  295. el1 := l.PushBack(1)
  296. t.Run("tail element should not have a closed nextWaitChan", func(t *testing.T) {
  297. select {
  298. case <-el1.NextWaitChan():
  299. t.Fatal("nextWaitChan should not have been closed")
  300. default:
  301. }
  302. })
  303. el2 := l.PushBack(2)
  304. t.Run("adding element should close tail nextWaitChan", func(t *testing.T) {
  305. select {
  306. case <-el1.NextWaitChan():
  307. require.NotNil(t, el1.Next())
  308. default:
  309. t.Fatal("nextWaitChan should have been closed")
  310. }
  311. select {
  312. case <-el2.NextWaitChan():
  313. t.Fatal("nextWaitChan should not have been closed")
  314. default:
  315. }
  316. })
  317. t.Run("removing element should close its nextWaitChan", func(t *testing.T) {
  318. l.Remove(el2)
  319. select {
  320. case <-el2.NextWaitChan():
  321. require.Nil(t, el2.Next())
  322. default:
  323. t.Fatal("nextWaitChan should have been closed")
  324. }
  325. })
  326. }