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 }