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  }