github.com/decred/dcrlnd@v0.7.6/lntest/wait/wait.go (about)

     1  package wait
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  )
     7  
     8  // PollInterval is a constant specifying a 200 ms interval.
     9  const PollInterval = 200 * time.Millisecond
    10  
    11  // Predicate is a helper test function that will wait for a timeout period of
    12  // time until the passed predicate returns true. This function is helpful as
    13  // timing doesn't always line up well when running integration tests with
    14  // several running lnd nodes. This function gives callers a way to assert that
    15  // some property is upheld within a particular time frame.
    16  func Predicate(pred func() bool, timeout time.Duration) error {
    17  	exitTimer := time.After(timeout)
    18  	for {
    19  		<-time.After(PollInterval)
    20  
    21  		select {
    22  		case <-exitTimer:
    23  			return fmt.Errorf("predicate not satisfied after time out")
    24  		default:
    25  		}
    26  
    27  		if pred() {
    28  			return nil
    29  		}
    30  	}
    31  }
    32  
    33  // NoError is a wrapper around Predicate that waits for the passed method f to
    34  // execute without error, and returns the last error encountered if this doesn't
    35  // happen within the timeout.
    36  func NoError(f func() error, timeout time.Duration) error {
    37  	var predErr error
    38  	pred := func() bool {
    39  		if err := f(); err != nil {
    40  			predErr = err
    41  			return false
    42  		}
    43  		return true
    44  	}
    45  
    46  	// If f() doesn't succeed within the timeout, return the last
    47  	// encountered error.
    48  	if err := Predicate(pred, timeout); err != nil {
    49  		return predErr
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  // Invariant is a helper test function that will wait for a timeout period of
    56  // time, verifying that a statement remains true for the entire duration.  This
    57  // function is helpful as timing doesn't always line up well when running
    58  // integration tests with several running lnd nodes. This function gives callers
    59  // a way to assert that some property is maintained over a particular time
    60  // frame.
    61  func Invariant(statement func() bool, timeout time.Duration) error {
    62  	const pollInterval = 20 * time.Millisecond
    63  
    64  	exitTimer := time.After(timeout)
    65  	for {
    66  		<-time.After(pollInterval)
    67  
    68  		// Fail if the invariant is broken while polling.
    69  		if !statement() {
    70  			return fmt.Errorf("invariant broken before time out")
    71  		}
    72  
    73  		select {
    74  		case <-exitTimer:
    75  			return nil
    76  		default:
    77  		}
    78  	}
    79  }
    80  
    81  // InvariantNoError is a wrapper around Invariant that waits out the duration
    82  // specified by timeout. It fails if the predicate ever returns an error during
    83  // that time.
    84  func InvariantNoError(f func() error, timeout time.Duration) error {
    85  	var predErr error
    86  	pred := func() bool {
    87  		if err := f(); err != nil {
    88  			predErr = err
    89  			return false
    90  		}
    91  		return true
    92  	}
    93  
    94  	if err := Invariant(pred, timeout); err != nil {
    95  		return predErr
    96  	}
    97  
    98  	return nil
    99  }