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 }