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