github.com/number571/tendermint@v0.34.11-gost/internal/libs/clist/clist_test.go (about)

     1  package clist
     2  
     3  import (
     4  	"fmt"
     5  	mrand "math/rand"
     6  	"runtime"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    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  func TestGCFifo(t *testing.T) {
    69  
    70  	const numElements = 1000000
    71  	l := New()
    72  	gcCount := 0
    73  
    74  	// SetFinalizer doesn't work well with circular structures,
    75  	// so we construct a trivial non-circular structure to
    76  	// track.
    77  	type value struct {
    78  		Int int
    79  	}
    80  
    81  	gcCh := make(chan struct{})
    82  	for i := 0; i < numElements; i++ {
    83  		v := new(value)
    84  		v.Int = i
    85  		l.PushBack(v)
    86  		runtime.SetFinalizer(v, func(v *value) {
    87  			gcCh <- struct{}{}
    88  		})
    89  	}
    90  
    91  	for el := l.Front(); el != nil; {
    92  		l.Remove(el)
    93  		// oldEl := el
    94  		el = el.Next()
    95  		// oldEl.DetachPrev()
    96  		// oldEl.DetachNext()
    97  	}
    98  
    99  	tickerQuitCh := make(chan struct{})
   100  	tickerDoneCh := make(chan struct{})
   101  	go func() {
   102  		defer close(tickerDoneCh)
   103  		ticker := time.NewTicker(time.Second)
   104  		for {
   105  			select {
   106  			case <-ticker.C:
   107  				runtime.GC()
   108  			case <-tickerQuitCh:
   109  				return
   110  			}
   111  		}
   112  	}()
   113  
   114  	for i := 0; i < numElements; i++ {
   115  		<-gcCh
   116  		gcCount++
   117  	}
   118  
   119  	close(tickerQuitCh)
   120  	<-tickerDoneCh
   121  
   122  	if gcCount != numElements {
   123  		t.Errorf("expected gcCount to be %v, got %v", numElements,
   124  			gcCount)
   125  	}
   126  }
   127  
   128  func TestGCRandom(t *testing.T) {
   129  
   130  	const numElements = 1000000
   131  	l := New()
   132  	gcCount := 0
   133  
   134  	// SetFinalizer doesn't work well with circular structures,
   135  	// so we construct a trivial non-circular structure to
   136  	// track.
   137  	type value struct {
   138  		Int int
   139  	}
   140  
   141  	gcCh := make(chan struct{})
   142  	for i := 0; i < numElements; i++ {
   143  		v := new(value)
   144  		v.Int = i
   145  		l.PushBack(v)
   146  		runtime.SetFinalizer(v, func(v *value) {
   147  			gcCh <- struct{}{}
   148  		})
   149  	}
   150  
   151  	els := make([]*CElement, 0, numElements)
   152  	for el := l.Front(); el != nil; el = el.Next() {
   153  		els = append(els, el)
   154  	}
   155  
   156  	for _, i := range mrand.Perm(numElements) {
   157  		el := els[i]
   158  		l.Remove(el)
   159  		_ = el.Next()
   160  	}
   161  
   162  	tickerQuitCh := make(chan struct{})
   163  	tickerDoneCh := make(chan struct{})
   164  	go func() {
   165  		defer close(tickerDoneCh)
   166  		ticker := time.NewTicker(time.Second)
   167  		for {
   168  			select {
   169  			case <-ticker.C:
   170  				runtime.GC()
   171  			case <-tickerQuitCh:
   172  				return
   173  			}
   174  		}
   175  	}()
   176  
   177  	for i := 0; i < numElements; i++ {
   178  		<-gcCh
   179  		gcCount++
   180  	}
   181  
   182  	close(tickerQuitCh)
   183  	<-tickerDoneCh
   184  
   185  	if gcCount != numElements {
   186  		t.Errorf("expected gcCount to be %v, got %v", numElements,
   187  			gcCount)
   188  	}
   189  }
   190  
   191  func TestScanRightDeleteRandom(t *testing.T) {
   192  
   193  	const numElements = 1000
   194  	const numTimes = 100
   195  	const numScanners = 10
   196  
   197  	l := New()
   198  	stop := make(chan struct{})
   199  
   200  	els := make([]*CElement, numElements)
   201  	for i := 0; i < numElements; i++ {
   202  		el := l.PushBack(i)
   203  		els[i] = el
   204  	}
   205  
   206  	// Launch scanner routines that will rapidly iterate over elements.
   207  	for i := 0; i < numScanners; i++ {
   208  		go func(scannerID int) {
   209  			var el *CElement
   210  			restartCounter := 0
   211  			counter := 0
   212  		FOR_LOOP:
   213  			for {
   214  				select {
   215  				case <-stop:
   216  					fmt.Println("stopped")
   217  					break FOR_LOOP
   218  				default:
   219  				}
   220  				if el == nil {
   221  					el = l.FrontWait()
   222  					restartCounter++
   223  				}
   224  				el = el.Next()
   225  				counter++
   226  			}
   227  			fmt.Printf("Scanner %v restartCounter: %v counter: %v\n", scannerID, restartCounter, counter)
   228  		}(i)
   229  	}
   230  
   231  	// Remove an element, push back an element.
   232  	for i := 0; i < numTimes; i++ {
   233  		// Pick an element to remove
   234  		rmElIdx := mrand.Intn(len(els))
   235  		rmEl := els[rmElIdx]
   236  
   237  		// Remove it
   238  		l.Remove(rmEl)
   239  		// fmt.Print(".")
   240  
   241  		// Insert a new element
   242  		newEl := l.PushBack(-1*i - 1)
   243  		els[rmElIdx] = newEl
   244  
   245  		if i%100000 == 0 {
   246  			fmt.Printf("Pushed %vK elements so far...\n", i/1000)
   247  		}
   248  
   249  	}
   250  
   251  	// Stop scanners
   252  	close(stop)
   253  	// time.Sleep(time.Second * 1)
   254  
   255  	// And remove all the elements.
   256  	for el := l.Front(); el != nil; el = el.Next() {
   257  		l.Remove(el)
   258  	}
   259  	if l.Len() != 0 {
   260  		t.Fatal("Failed to remove all elements from CList")
   261  	}
   262  }
   263  
   264  func TestWaitChan(t *testing.T) {
   265  	l := New()
   266  	ch := l.WaitChan()
   267  
   268  	// 1) add one element to an empty list
   269  	go l.PushBack(1)
   270  	<-ch
   271  
   272  	// 2) and remove it
   273  	el := l.Front()
   274  	v := l.Remove(el)
   275  	if v != 1 {
   276  		t.Fatal("where is 1 coming from?")
   277  	}
   278  
   279  	// 3) test iterating forward and waiting for Next (NextWaitChan and Next)
   280  	el = l.PushBack(0)
   281  
   282  	done := make(chan struct{})
   283  	pushed := 0
   284  	go func() {
   285  		for i := 1; i < 100; i++ {
   286  			l.PushBack(i)
   287  			pushed++
   288  			time.Sleep(time.Duration(mrand.Intn(25)) * time.Millisecond)
   289  		}
   290  		// apply a deterministic pause so the counter has time to catch up
   291  		time.Sleep(25 * time.Millisecond)
   292  		close(done)
   293  	}()
   294  
   295  	next := el
   296  	seen := 0
   297  FOR_LOOP:
   298  	for {
   299  		select {
   300  		case <-next.NextWaitChan():
   301  			next = next.Next()
   302  			seen++
   303  			if next == nil {
   304  				t.Fatal("Next should not be nil when waiting on NextWaitChan")
   305  			}
   306  		case <-done:
   307  			break FOR_LOOP
   308  		case <-time.After(10 * time.Second):
   309  			t.Fatal("max execution time")
   310  		}
   311  	}
   312  
   313  	if pushed != seen {
   314  		t.Fatalf("number of pushed items (%d) not equal to number of seen items (%d)", pushed, seen)
   315  	}
   316  
   317  	// 4) test iterating backwards (PrevWaitChan and Prev)
   318  	prev := next
   319  	seen = 0
   320  FOR_LOOP2:
   321  	for {
   322  		select {
   323  		case <-prev.PrevWaitChan():
   324  			prev = prev.Prev()
   325  			seen++
   326  			if prev == nil {
   327  				t.Fatal("expected PrevWaitChan to block forever on nil when reached first elem")
   328  			}
   329  		case <-time.After(3 * time.Second):
   330  			break FOR_LOOP2
   331  		}
   332  	}
   333  
   334  	if pushed != seen {
   335  		t.Fatalf("number of pushed items (%d) not equal to number of seen items (%d)", pushed, seen)
   336  	}
   337  }
   338  
   339  func TestRemoved(t *testing.T) {
   340  	l := New()
   341  	el1 := l.PushBack(1)
   342  	el2 := l.PushBack(2)
   343  	l.Remove(el1)
   344  	require.True(t, el1.Removed())
   345  	require.False(t, el2.Removed())
   346  }
   347  
   348  func TestNextWaitChan(t *testing.T) {
   349  	l := New()
   350  	el1 := l.PushBack(1)
   351  	t.Run("tail element should not have a closed nextWaitChan", func(t *testing.T) {
   352  		select {
   353  		case <-el1.NextWaitChan():
   354  			t.Fatal("nextWaitChan should not have been closed")
   355  		default:
   356  		}
   357  	})
   358  
   359  	el2 := l.PushBack(2)
   360  	t.Run("adding element should close tail nextWaitChan", func(t *testing.T) {
   361  		select {
   362  		case <-el1.NextWaitChan():
   363  			require.NotNil(t, el1.Next())
   364  		default:
   365  			t.Fatal("nextWaitChan should have been closed")
   366  		}
   367  
   368  		select {
   369  		case <-el2.NextWaitChan():
   370  			t.Fatal("nextWaitChan should not have been closed")
   371  		default:
   372  		}
   373  	})
   374  
   375  	t.Run("removing element should close its nextWaitChan", func(t *testing.T) {
   376  		l.Remove(el2)
   377  		select {
   378  		case <-el2.NextWaitChan():
   379  			require.Nil(t, el2.Next())
   380  		default:
   381  			t.Fatal("nextWaitChan should have been closed")
   382  		}
   383  	})
   384  }