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  }