github.com/filecoin-project/lassie@v0.23.0/pkg/internal/candidatebuffer/candidatebuffer_test.go (about)

     1  package candidatebuffer_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/benbjohnson/clock"
    11  	"github.com/filecoin-project/lassie/pkg/internal/candidatebuffer"
    12  	"github.com/filecoin-project/lassie/pkg/internal/testutil"
    13  	"github.com/filecoin-project/lassie/pkg/types"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestCandidateBuffer(t *testing.T) {
    18  	ctx := context.Background()
    19  	candidates := testutil.GenerateRetrievalCandidates(t, 10)
    20  
    21  	type candidateResultAt struct {
    22  		timeDelta time.Duration
    23  		cancel    bool
    24  		result    types.FindCandidatesResult
    25  	}
    26  	testCases := []struct {
    27  		name                  string
    28  		bufferingTime         time.Duration
    29  		incomingResults       []candidateResultAt
    30  		expectedCandidateSets [][]types.RetrievalCandidate
    31  		expectedErr           error
    32  	}{
    33  
    34  		{
    35  			name:          "two time windows of candidates",
    36  			bufferingTime: 200 * time.Millisecond,
    37  			incomingResults: []candidateResultAt{
    38  				{
    39  					timeDelta: 50 * time.Millisecond,
    40  					result:    types.FindCandidatesResult{Candidate: candidates[0]},
    41  				},
    42  				{
    43  					timeDelta: 5 * time.Millisecond,
    44  					result:    types.FindCandidatesResult{Candidate: candidates[1]},
    45  				},
    46  				{
    47  					timeDelta: 5 * time.Millisecond,
    48  					result:    types.FindCandidatesResult{Candidate: candidates[2]},
    49  				},
    50  				{
    51  					timeDelta: 5 * time.Millisecond,
    52  					result:    types.FindCandidatesResult{Candidate: candidates[3]},
    53  				},
    54  				{
    55  					timeDelta: 500 * time.Millisecond,
    56  					result:    types.FindCandidatesResult{Candidate: candidates[4]},
    57  				},
    58  				{
    59  					timeDelta: 5 * time.Millisecond,
    60  					result:    types.FindCandidatesResult{Candidate: candidates[5]},
    61  				},
    62  				{
    63  					timeDelta: 5 * time.Millisecond,
    64  					result:    types.FindCandidatesResult{Candidate: candidates[6]},
    65  				},
    66  				{
    67  					timeDelta: 5 * time.Millisecond,
    68  					result:    types.FindCandidatesResult{Candidate: candidates[7]},
    69  				},
    70  				{
    71  					timeDelta: 5 * time.Millisecond,
    72  					result:    types.FindCandidatesResult{Candidate: candidates[8]},
    73  				},
    74  				{
    75  					timeDelta: 5 * time.Millisecond,
    76  					result:    types.FindCandidatesResult{Candidate: candidates[9]},
    77  				},
    78  			},
    79  			expectedCandidateSets: [][]types.RetrievalCandidate{
    80  				candidates[:4],
    81  				candidates[4:],
    82  			},
    83  		},
    84  		{
    85  			name:          "with error, stops consuming, returns error",
    86  			bufferingTime: 200 * time.Millisecond,
    87  			incomingResults: []candidateResultAt{
    88  				{
    89  					timeDelta: 50 * time.Millisecond,
    90  					result:    types.FindCandidatesResult{Candidate: candidates[0]},
    91  				},
    92  				{
    93  					timeDelta: 5 * time.Millisecond,
    94  					result:    types.FindCandidatesResult{Candidate: candidates[1]},
    95  				},
    96  				{
    97  					timeDelta: 5 * time.Millisecond,
    98  					result:    types.FindCandidatesResult{Candidate: candidates[2]},
    99  				},
   100  				{
   101  					timeDelta: 5 * time.Millisecond,
   102  					result:    types.FindCandidatesResult{Candidate: candidates[3]},
   103  				},
   104  				{
   105  					timeDelta: 500 * time.Millisecond,
   106  					result:    types.FindCandidatesResult{Candidate: candidates[4]},
   107  				},
   108  				{
   109  					timeDelta: 5 * time.Millisecond,
   110  					result:    types.FindCandidatesResult{Candidate: candidates[5]},
   111  				},
   112  				{
   113  					timeDelta: 5 * time.Millisecond,
   114  					result:    types.FindCandidatesResult{Candidate: candidates[6]},
   115  				},
   116  				{
   117  					timeDelta: 5 * time.Millisecond,
   118  					result:    types.FindCandidatesResult{Err: errors.New("something went wrong")},
   119  				},
   120  				{
   121  					timeDelta: 5 * time.Millisecond,
   122  					result:    types.FindCandidatesResult{Candidate: candidates[8]},
   123  				},
   124  				{
   125  					timeDelta: 5 * time.Millisecond,
   126  					result:    types.FindCandidatesResult{Candidate: candidates[9]},
   127  				},
   128  			},
   129  			expectedCandidateSets: [][]types.RetrievalCandidate{
   130  				candidates[:4],
   131  				candidates[4:7],
   132  			},
   133  			expectedErr: errors.New("something went wrong"),
   134  		},
   135  		{
   136  			name:          "with cancel, stops consuming, does not return buffer",
   137  			bufferingTime: 200 * time.Millisecond,
   138  			incomingResults: []candidateResultAt{
   139  				{
   140  					timeDelta: 50 * time.Millisecond,
   141  					result:    types.FindCandidatesResult{Candidate: candidates[0]},
   142  				},
   143  				{
   144  					timeDelta: 5 * time.Millisecond,
   145  					result:    types.FindCandidatesResult{Candidate: candidates[1]},
   146  				},
   147  				{
   148  					timeDelta: 5 * time.Millisecond,
   149  					result:    types.FindCandidatesResult{Candidate: candidates[2]},
   150  				},
   151  				{
   152  					timeDelta: 5 * time.Millisecond,
   153  					result:    types.FindCandidatesResult{Candidate: candidates[3]},
   154  				},
   155  				{
   156  					timeDelta: 500 * time.Millisecond,
   157  					result:    types.FindCandidatesResult{Candidate: candidates[4]},
   158  				},
   159  				{
   160  					timeDelta: 5 * time.Millisecond,
   161  					result:    types.FindCandidatesResult{Candidate: candidates[5]},
   162  				},
   163  				{
   164  					timeDelta: 5 * time.Millisecond,
   165  					result:    types.FindCandidatesResult{Candidate: candidates[6]},
   166  				},
   167  				{
   168  					timeDelta: 5 * time.Millisecond,
   169  					cancel:    true,
   170  				},
   171  				{
   172  					timeDelta: 5 * time.Millisecond,
   173  					result:    types.FindCandidatesResult{Candidate: candidates[8]},
   174  				},
   175  				{
   176  					timeDelta: 5 * time.Millisecond,
   177  					result:    types.FindCandidatesResult{Candidate: candidates[9]},
   178  				},
   179  			},
   180  			expectedCandidateSets: [][]types.RetrievalCandidate{
   181  				candidates[:4],
   182  			},
   183  			expectedErr: context.Canceled,
   184  		},
   185  	}
   186  	for _, testCase := range testCases {
   187  		t.Run(testCase.name, func(t *testing.T) {
   188  			ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
   189  			defer cancel()
   190  			req := require.New(t)
   191  			var receivedCandidatesLk sync.Mutex
   192  			var receivedCandidates [][]types.RetrievalCandidate
   193  			clock := clock.NewMock()
   194  			candididateBuffer := candidatebuffer.NewCandidateBuffer(func(next []types.RetrievalCandidate) {
   195  				receivedCandidatesLk.Lock()
   196  				receivedCandidates = append(receivedCandidates, next)
   197  				receivedCandidatesLk.Unlock()
   198  			}, clock)
   199  			queueCandidates := func(ctx context.Context, onNextCandidate candidatebuffer.OnNextCandidate) error {
   200  				for _, nextCandidateResultAt := range testCase.incomingResults {
   201  					clock.Add(nextCandidateResultAt.timeDelta)
   202  					if nextCandidateResultAt.cancel {
   203  						cancel()
   204  						return context.Canceled
   205  					}
   206  					select {
   207  					case <-ctx.Done():
   208  					default:
   209  					}
   210  					if nextCandidateResultAt.result.Err != nil {
   211  						return nextCandidateResultAt.result.Err
   212  					}
   213  					onNextCandidate(nextCandidateResultAt.result.Candidate)
   214  				}
   215  				return nil
   216  			}
   217  			err := candididateBuffer.BufferStream(ctx, queueCandidates, testCase.bufferingTime)
   218  			if testCase.expectedErr != nil {
   219  				req.EqualError(err, testCase.expectedErr.Error())
   220  			} else {
   221  				req.NoError(err)
   222  			}
   223  			receivedCandidatesLk.Lock()
   224  			req.Equal(testCase.expectedCandidateSets, receivedCandidates)
   225  			receivedCandidatesLk.Unlock()
   226  		})
   227  	}
   228  }