github.com/decred/dcrlnd@v0.7.6/routing/validation_barrier_test.go (about) 1 package routing_test 2 3 import ( 4 "encoding/binary" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrlnd/lnwire" 9 "github.com/decred/dcrlnd/routing" 10 ) 11 12 // TestValidationBarrierSemaphore checks basic properties of the validation 13 // barrier's semaphore wrt. enqueuing/dequeuing. 14 func TestValidationBarrierSemaphore(t *testing.T) { 15 t.Parallel() 16 17 const ( 18 numTasks = 8 19 numPendingTasks = 8 20 timeout = 50 * time.Millisecond 21 ) 22 23 quit := make(chan struct{}) 24 barrier := routing.NewValidationBarrier(numTasks, quit) 25 26 // Saturate the semaphore with jobs. 27 for i := 0; i < numTasks; i++ { 28 barrier.InitJobDependencies(nil) 29 } 30 31 // Spawn additional tasks that will signal completion when added. 32 jobAdded := make(chan struct{}) 33 for i := 0; i < numPendingTasks; i++ { 34 go func() { 35 barrier.InitJobDependencies(nil) 36 jobAdded <- struct{}{} 37 }() 38 } 39 40 // Check that no jobs are added while semaphore is full. 41 select { 42 case <-time.After(timeout): 43 // Expected since no slots open. 44 case <-jobAdded: 45 t.Fatalf("job should not have been added") 46 } 47 48 // Complete jobs one at a time and verify that they get added. 49 for i := 0; i < numPendingTasks; i++ { 50 barrier.CompleteJob() 51 52 select { 53 case <-time.After(timeout): 54 t.Fatalf("timeout waiting for job to be added") 55 case <-jobAdded: 56 // Expected since one slot opened up. 57 } 58 } 59 } 60 61 // TestValidationBarrierQuit checks that pending validation tasks will return an 62 // error from WaitForDependants if the barrier's quit signal is canceled. 63 func TestValidationBarrierQuit(t *testing.T) { 64 t.Parallel() 65 66 const ( 67 numTasks = 8 68 timeout = 50 * time.Millisecond 69 ) 70 71 quit := make(chan struct{}) 72 barrier := routing.NewValidationBarrier(2*numTasks, quit) 73 74 // Create a set of unique channel announcements that we will prep for 75 // validation. 76 anns := make([]*lnwire.ChannelAnnouncement, 0, numTasks) 77 for i := 0; i < numTasks; i++ { 78 anns = append(anns, &lnwire.ChannelAnnouncement{ 79 ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)), 80 NodeID1: nodeIDFromInt(uint64(2 * i)), 81 NodeID2: nodeIDFromInt(uint64(2*i + 1)), 82 }) 83 barrier.InitJobDependencies(anns[i]) 84 } 85 86 // Create a set of channel updates, that must wait until their 87 // associated channel announcement has been verified. 88 chanUpds := make([]*lnwire.ChannelUpdate, 0, numTasks) 89 for i := 0; i < numTasks; i++ { 90 chanUpds = append(chanUpds, &lnwire.ChannelUpdate{ 91 ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)), 92 }) 93 barrier.InitJobDependencies(chanUpds[i]) 94 } 95 96 // Spawn additional tasks that will send the error returned after 97 // waiting for the announcements to finish. In the background, we will 98 // iteratively queue the channel updates, which will send back the error 99 // returned from waiting. 100 jobErrs := make(chan error) 101 for i := 0; i < numTasks; i++ { 102 go func(ii int) { 103 jobErrs <- barrier.WaitForDependants(chanUpds[ii]) 104 }(i) 105 } 106 107 // Check that no jobs are added while semaphore is full. 108 select { 109 case <-time.After(timeout): 110 // Expected since no slots open. 111 case <-jobErrs: 112 t.Fatalf("job should not have been signaled") 113 } 114 115 // Complete the first half of jobs, one at a time, verifying that they 116 // get signaled. Then, quit the barrier and check that all others exit 117 // with the correct error. 118 for i := 0; i < numTasks; i++ { 119 switch { 120 // Signal completion for the first half of tasks, but only allow 121 // dependents to be processed as well for the second quarter. 122 case i < numTasks/4: 123 barrier.SignalDependants(anns[i], false) 124 barrier.CompleteJob() 125 126 case i < numTasks/2: 127 barrier.SignalDependants(anns[i], true) 128 barrier.CompleteJob() 129 130 // At midpoint, quit the validation barrier. 131 case i == numTasks/2: 132 close(quit) 133 } 134 135 var err error 136 select { 137 case <-time.After(timeout): 138 t.Fatalf("timeout waiting for job to be signaled") 139 case err = <-jobErrs: 140 } 141 142 switch { 143 // First half should return without failure. 144 case i < numTasks/4 && !routing.IsError( 145 err, routing.ErrParentValidationFailed, 146 ): 147 t.Fatalf("unexpected failure while waiting: %v", err) 148 149 case i >= numTasks/4 && i < numTasks/2 && err != nil: 150 t.Fatalf("unexpected failure while waiting: %v", err) 151 152 // Last half should return the shutdown error. 153 case i >= numTasks/2 && !routing.IsError( 154 err, routing.ErrVBarrierShuttingDown, 155 ): 156 t.Fatalf("expected failure after quitting: want %v, "+ 157 "got %v", routing.ErrVBarrierShuttingDown, err) 158 } 159 } 160 } 161 162 // nodeIDFromInt creates a node ID by writing a uint64 to the first 8 bytes. 163 func nodeIDFromInt(i uint64) [33]byte { 164 var nodeID [33]byte 165 binary.BigEndian.PutUint64(nodeID[:8], i) 166 return nodeID 167 }