github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/tracker/tracker_test.go (about)

     1  package tracker
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/onflow/flow-go/consensus/hotstuff/helper"
    10  	"github.com/onflow/flow-go/consensus/hotstuff/model"
    11  	"github.com/onflow/flow-go/model/flow"
    12  )
    13  
    14  // TestNewNewestQCTracker checks that new instance returns nil tracked value.
    15  func TestNewNewestQCTracker(t *testing.T) {
    16  	tracker := NewNewestQCTracker()
    17  	require.Nil(t, tracker.NewestQC())
    18  }
    19  
    20  // TestNewestQCTracker_Track this test is needed to make sure that concurrent updates on NewestQCTracker are performed correctly,
    21  // and it always tracks the newest QC, especially in scenario of shared access. This test is structured in a way that it
    22  // starts multiple goroutines that will try to submit their QCs simultaneously to the tracker. Once all goroutines are started
    23  // we will use a wait group to execute all operations as concurrent as possible, after that we will observe if resulted value
    24  // is indeed expected. This test will run multiple times.
    25  func TestNewestQCTracker_Track(t *testing.T) {
    26  	tracker := NewNewestQCTracker()
    27  	samples := 20 // number of concurrent updates per test case
    28  	times := 20   // number of times we run the test case
    29  
    30  	// setup initial value
    31  	tracker.Track(helper.MakeQC(helper.WithQCView(0)))
    32  
    33  	for i := 0; i < times; i++ {
    34  		startView := tracker.NewestQC().View
    35  		var readyWg, startWg, doneWg sync.WaitGroup
    36  		startWg.Add(1)
    37  		readyWg.Add(samples)
    38  		doneWg.Add(samples)
    39  		for s := 0; s < samples; s++ {
    40  			qc := helper.MakeQC(helper.WithQCView(startView + uint64(s+1)))
    41  			go func(newestQC *flow.QuorumCertificate) {
    42  				defer doneWg.Done()
    43  				readyWg.Done()
    44  				startWg.Wait()
    45  				tracker.Track(newestQC)
    46  			}(qc)
    47  		}
    48  
    49  		// wait for all goroutines to be ready
    50  		readyWg.Wait()
    51  		// since we have waited for all goroutines to be ready this `Done` will start all goroutines
    52  		startWg.Done()
    53  		// wait for all of them to finish execution
    54  		doneWg.Wait()
    55  
    56  		// at this point tracker MUST have the newest QC
    57  		require.Equal(t, startView+uint64(samples), tracker.NewestQC().View)
    58  	}
    59  }
    60  
    61  // TestNewNewestTCTracker checks that new instance returns nil tracked value.
    62  func TestNewNewestTCTracker(t *testing.T) {
    63  	tracker := NewNewestTCTracker()
    64  	require.Nil(t, tracker.NewestTC())
    65  }
    66  
    67  // TestNewestTCTracker_Track this test is needed to make sure that concurrent updates on NewestTCTracker are performed correctly,
    68  // and it always tracks the newest TC, especially in scenario of shared access. This test is structured in a way that it
    69  // starts multiple goroutines that will try to submit their TCs simultaneously to the tracker. Once all goroutines are started
    70  // we will use a wait group to execute all operations as concurrent as possible, after that we will observe if resulted value
    71  // is indeed expected. This test will run multiple times.
    72  func TestNewestTCTracker_Track(t *testing.T) {
    73  	tracker := NewNewestTCTracker()
    74  	samples := 20
    75  	times := 20
    76  
    77  	// setup initial value
    78  	tracker.Track(helper.MakeTC(helper.WithTCView(0)))
    79  
    80  	for i := 0; i < times; i++ {
    81  		startView := tracker.NewestTC().View
    82  		var readyWg, startWg, doneWg sync.WaitGroup
    83  		startWg.Add(1)
    84  		readyWg.Add(samples)
    85  		doneWg.Add(samples)
    86  		for s := 0; s < samples; s++ {
    87  			tc := helper.MakeTC(helper.WithTCView(startView + uint64(s+1)))
    88  			go func(newestTC *flow.TimeoutCertificate) {
    89  				defer doneWg.Done()
    90  				readyWg.Done()
    91  				startWg.Wait()
    92  				tracker.Track(newestTC)
    93  			}(tc)
    94  		}
    95  
    96  		// wait for all goroutines to be ready
    97  		readyWg.Wait()
    98  		// since we have waited for all goroutines to be ready this `Done` will start all goroutines
    99  		startWg.Done()
   100  		// wait for all of them to finish execution
   101  		doneWg.Wait()
   102  
   103  		// at this point tracker MUST have the newest TC
   104  		require.Equal(t, startView+uint64(samples), tracker.NewestTC().View)
   105  	}
   106  }
   107  
   108  // TestNewNewestBlockTracker checks that new instance returns nil tracked value.
   109  func TestNewNewestBlockTracker(t *testing.T) {
   110  	tracker := NewNewestBlockTracker()
   111  	require.Nil(t, tracker.NewestBlock())
   112  }
   113  
   114  // TestNewestBlockTracker_Track this test is needed to make sure that concurrent updates on NewestBlockTracker are performed correctly,
   115  // and it always tracks the newest block, especially in scenario of shared access. This test is structured in a way that it
   116  // starts multiple goroutines that will try to submit their blocks simultaneously to the tracker. Once all goroutines are started
   117  // we will use a wait group to execute all operations as concurrent as possible, after that we will observe if resulted value
   118  // is indeed expected. This test will run multiple times.
   119  func TestNewestBlockTracker_Track(t *testing.T) {
   120  	tracker := NewNewestBlockTracker()
   121  	samples := 20 // number of concurrent updates per test case
   122  	times := 20   // number of times we run the test case
   123  
   124  	// setup initial value
   125  	tracker.Track(helper.MakeBlock(helper.WithBlockView(0)))
   126  
   127  	for i := 0; i < times; i++ {
   128  		startView := tracker.NewestBlock().View
   129  		var readyWg, startWg, doneWg sync.WaitGroup
   130  		startWg.Add(1)
   131  		readyWg.Add(samples)
   132  		doneWg.Add(samples)
   133  		for s := 0; s < samples; s++ {
   134  			block := helper.MakeBlock(helper.WithBlockView(startView + uint64(s+1)))
   135  			go func(newestBlock *model.Block) {
   136  				defer doneWg.Done()
   137  				readyWg.Done()
   138  				startWg.Wait()
   139  				tracker.Track(newestBlock)
   140  			}(block)
   141  		}
   142  
   143  		// wait for all goroutines to be ready
   144  		readyWg.Wait()
   145  		// since we have waited for all goroutines to be ready this `Done` will start all goroutines
   146  		startWg.Done()
   147  		// wait for all of them to finish execution
   148  		doneWg.Wait()
   149  
   150  		// at this point tracker MUST have the newest block
   151  		require.Equal(t, startView+uint64(samples), tracker.NewestBlock().View)
   152  	}
   153  }