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