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