github.com/haraldrudell/parl@v0.4.176/awaitable-slice_test.go (about) 1 /* 2 © 2024–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "slices" 10 "sync" 11 "testing" 12 ) 13 14 func TestAwaitableSlice(t *testing.T) { 15 var value1, value2, value3 = 1, 2, 3 16 var values = []int{value1, value2, value3} 17 var size = 25 18 19 var actual int 20 var actuals []int 21 var hasValue, isOpen bool 22 var ch AwaitableCh 23 24 // DataWaitCh EmptyCh Get Get1 GetAll Send SendSlice SetSize 25 var slice *AwaitableSlice[int] 26 var reset = func() { 27 slice = &AwaitableSlice[int]{} 28 } 29 30 // Get1 should return one value at a a time 31 // - Send SendSlice should work 32 reset() 33 slice.Send(value1) 34 slice.SendSlice([]int{value2}) 35 slice.Send(value3) 36 // populated Slice: q: [1] slices: [[2] [3]] 37 t.Logf("populated Slice: q: %v slices: %v", slice.queue, slice.slices) 38 for i, v := range values { 39 actual, hasValue = slice.Get() 40 if !hasValue { 41 t.Errorf("Get1#%d hasValue false", i) 42 } 43 if actual != v { 44 t.Errorf("Get1#%d %d exp %d", i, actual, v) 45 } 46 } 47 // Get1 empty should returns hasValue false 48 actual, hasValue = slice.Get() 49 _ = actual 50 if hasValue { 51 t.Error("Get1 hasValue true") 52 } 53 54 // Get should return one slice at a a time 55 reset() 56 slice.Send(value1) 57 slice.SendSlice([]int{value2}) 58 slice.Send(value3) 59 for i, v := range values { 60 actuals = slice.GetSlice() 61 if len(actuals) != 1 { 62 t.Fatalf("Get#%d hasValue false", i) 63 } 64 if actuals[0] != v { 65 t.Errorf("Get#%d %d exp %d", i, actuals[0], v) 66 } 67 } 68 // Get empty returns nil 69 actuals = slice.GetSlice() 70 if actuals != nil { 71 t.Errorf("Get actuals not nil: %d%v", len(actuals), actuals) 72 } 73 74 // GetAll should return all values in a single slice 75 reset() 76 slice.Send(value1) 77 slice.SendSlice([]int{value2}) 78 slice.Send(value3) 79 actuals = slice.GetAll() 80 if !slices.Equal(actuals, values) { 81 t.Errorf("GetAll %v exp %v", actuals, values) 82 } 83 actuals = slice.GetAll() 84 if actuals != nil { 85 t.Errorf("GetAll empty not nil: %d%v", len(actuals), actuals) 86 } 87 88 // SetSize should be effective 89 reset() 90 slice.SetSize(size) 91 slice.Send(value1) 92 actuals = slice.GetSlice() 93 if cap(actuals) != size { 94 t.Errorf("SetSize %d exp %d", cap(actuals), size) 95 } 96 97 // DataWaitCh 98 reset() 99 // DataWaitCh on creation should return non-nil, open channel 100 ch = slice.DataWaitCh() 101 if ch == nil { 102 t.Error("DataWaitCh nil") 103 } 104 isOpen = true 105 select { 106 case <-ch: 107 isOpen = false 108 default: 109 } 110 if !isOpen { 111 t.Error("DataWaitCh ch not open") 112 } 113 // hasData true should close the returned channel 114 slice.Send(value1) 115 isOpen = true 116 select { 117 case <-ch: 118 isOpen = false 119 default: 120 } 121 if isOpen { 122 t.Error("DataWaitCh hasData ch not closed") 123 } 124 125 // EndCh on creation should return non-nil closed channel 126 reset() 127 ch = slice.EmptyCh() 128 isOpen = true 129 select { 130 case <-ch: 131 isOpen = false 132 default: 133 } 134 if isOpen { 135 t.Error("EndCh empty ch not closed") 136 } 137 138 // EndCh for hasData true returns open channel 139 reset() 140 slice.Send(value1) 141 ch = slice.EmptyCh() 142 isOpen = true 143 select { 144 case <-ch: 145 isOpen = false 146 default: 147 } 148 if !isOpen { 149 t.Error("EmptyCh hasData ch closed") 150 } 151 // hasData to false should close the returned channel 152 actual, hasValue = slice.Get() 153 _ = actual 154 _ = hasValue 155 isOpen = true 156 select { 157 case <-ch: 158 isOpen = false 159 default: 160 } 161 if isOpen { 162 t.Error("EmptyCh empty ch not closed") 163 } 164 165 // EmptyCh CloseAwaiter should defer empty detection 166 reset() 167 ch = slice.EmptyCh(CloseAwaiter) 168 isOpen = true 169 select { 170 case <-ch: 171 isOpen = false 172 default: 173 } 174 if !isOpen { 175 t.Error("EmptyCh CloseAwaiter doe not defer empty detection") 176 } 177 // EmptyCh without CloseAwaiter should close the returned channel 178 _ = slice.EmptyCh() 179 isOpen = true 180 select { 181 case <-ch: 182 isOpen = false 183 default: 184 } 185 if isOpen { 186 t.Error("subsequent EmptyCh does not close the channel") 187 } 188 } 189 190 func TestAwaitableSliceFor(t *testing.T) { 191 var value1 = 1 192 var expValue1 = []int{value1} 193 194 var actual, zeroValue int 195 var a *AwaitableForTester 196 var actuals []int 197 198 var slice *AwaitableSlice[int] 199 var reset = func() { 200 slice = &AwaitableSlice[int]{} 201 } 202 203 // Init should return zero-value 204 reset() 205 actual = slice.Init() 206 if actual != zeroValue { 207 t.Errorf("Init %d exp %d", actual, zeroValue) 208 } 209 210 // Condition active on value appearing and slice closing 211 reset() 212 // start for loop in other thread 213 a = NewAwaitableForTester(slice) 214 go a.GoFor() 215 <-a.IsReady.Ch() 216 // Condition should block not receiving value 217 if a.IsValues.IsClosed() { 218 t.Fatal("Condition unexpectedly received value") 219 } 220 // Condition should not end due to close 221 if a.IsClosed.IsClosed() { 222 t.Fatal("Condition IsClosed") 223 } 224 // Condition should receive appearing values 225 slice.Send(value1) 226 // Condition should receive value 227 <-a.IsValues.Ch() 228 actuals = a.Values() 229 if !slices.Equal(actuals, expValue1) { 230 t.Errorf("Condition %v exp %v", actuals, expValue1) 231 } 232 // Condition should not detect a close 233 if a.IsClosed.IsClosed() { 234 t.Fatal("Condition IsClosed") 235 } 236 // Condition should detect occuring close 237 slice.EmptyCh() 238 <-a.IsClosed.Ch() 239 240 // Condition deferred close 241 // - slice has value and is closed on entering Condition 242 reset() 243 slice.Send(value1) 244 slice.EmptyCh() 245 // start for loop in other thread 246 a = NewAwaitableForTester(slice) 247 go a.GoFor() 248 <-a.IsReady.Ch() 249 // Condition should detect close 250 <-a.IsClosed.Ch() 251 // Condition should have received value 252 actuals = a.Values() 253 if !slices.Equal(actuals, expValue1) { 254 t.Errorf("Condition %v exp %v", actuals, expValue1) 255 } 256 } 257 258 // cover 100% of Send 259 func TestAwaitableSliceSend(t *testing.T) { 260 //t.Error("loggin on") 261 var value = 1 262 var values = []int{value} 263 264 var slice *AwaitableSlice[int] 265 var reset = func() { 266 slice = &AwaitableSlice[int]{} 267 } 268 269 // use cachedInput in Send 270 reset() 271 // 2 Get should cover append to s.queue 272 slice.Send(value) 273 slice.Send(value) 274 // Get to empty initializes cachedInput 275 slice.GetAll() 276 // len(s.slices): 0 s.queue: true s.cachedInput: true 277 t.Logf("len(s.slices): %d s.queue: %t s.cachedInput: %t", 278 len(slice.slices), slice.queue != nil, slice.cachedInput != nil, 279 ) 280 slice.queue = nil 281 // Send should cover using cachedInput 282 slice.Send(value) 283 284 // SendSlice and 2 Sends should cover append to local slice 285 reset() 286 slice.SendSlice(slices.Clone(values)) 287 slice.Send(value) 288 slice.Send(value) 289 290 // use cachedInput for local slice 291 reset() 292 // Get to empty initializes cachedInput 293 slice.Send(value) 294 slice.Get() 295 // SendSlice then Send should creates a local slice from cachedInput 296 slice.SendSlice(values) 297 slice.Send(value) 298 } 299 300 // 100% coverage GetSlice 301 func TestAwaitableSliceGetSlice(t *testing.T) { 302 //t.Error("loggin on") 303 var value1, value2 = 1, 2 304 var value2Slice = []int{value2} 305 306 var actual int 307 var hasValue bool 308 var actuals []int 309 310 var slice *AwaitableSlice[int] 311 var reset = func() { 312 slice = &AwaitableSlice[int]{} 313 } 314 315 // use cachedInput in Send 316 reset() 317 // Send Send Get creates non-empty s.output 318 slice.Send(value1) 319 slice.Send(value2) 320 if actual, hasValue = slice.Get(); actual != value1 { 321 t.Errorf("Get %d exp %d", actual, value1) 322 } 323 _ = hasValue 324 // GetSlice should return s.output 325 if actuals = slice.GetSlice(); !slices.Equal(actuals, value2Slice) { 326 t.Errorf("GetSlice %v exp %v", actuals, value2Slice) 327 } 328 } 329 330 // 100% coverage GetAll 331 func TestAwaitableSliceGetAll(t *testing.T) { 332 //t.Error("loggin on") 333 // value1 1 334 var value1, value2, value3 = 1, 2, 3 335 // slice of value 1 336 var values1, values2, values3 = []int{value1}, []int{value2}, []int{value3} 337 var values23 = []int{value2, value3} 338 339 var actual int 340 var actuals []int 341 var hasValue bool 342 343 var slice *AwaitableSlice[int] 344 var reset = func() { 345 slice = &AwaitableSlice[int]{} 346 } 347 348 // aggregate outputs 349 reset() 350 // SendSlice SendSlice Get creates non-empty s.outputs 351 slice.SendSlice(slices.Clone(values1)) 352 slice.SendSlice(slices.Clone(values2)) 353 if actual, hasValue = slice.Get(); actual != value1 { 354 t.Errorf("Get %d exp %d", actual, value1) 355 } 356 _ = hasValue 357 // GetAll should return s.outputs 358 if actuals = slice.GetAll(); !slices.Equal(actuals, values2) { 359 t.Errorf("GetAll %v exp %v", actuals, values2) 360 } 361 362 // aggregate output and outputs 363 reset() 364 // Send Send SendSlice Get creates non-empty s.output s.outputs 365 slice.Send(value1) 366 slice.Send(value2) 367 slice.SendSlice(slices.Clone(values3)) 368 if actual, hasValue = slice.Get(); actual != value1 { 369 t.Errorf("Get %d exp %d", actual, value1) 370 } 371 _ = hasValue 372 // GetAll should aggregate output and outputs 373 if actuals = slice.GetAll(); !slices.Equal(actuals, values23) { 374 t.Errorf("GetAll %v exp %v", actuals, values23) 375 } 376 377 // GetAll only output 378 reset() 379 // Send Send Get creates non-empty output 380 slice.Send(value1) 381 slice.Send(value2) 382 if actual, hasValue = slice.Get(); actual != value1 { 383 t.Errorf("Get %d exp %d", actual, value1) 384 } 385 _ = hasValue 386 // GetAll should return output single-slice 387 if actuals = slice.GetAll(); !slices.Equal(actuals, values2) { 388 t.Errorf("GetAll %v exp %v", actuals, values2) 389 } 390 391 // only s.slices[0] 392 reset() 393 // SendSlice creates single-slice s.slices 394 slice.SendSlice(values1) 395 // GetAll should return s.slices[0] single-slice 396 if actuals = slice.GetAll(); !slices.Equal(actuals, values1) { 397 t.Errorf("GetAll %v exp %v", actuals, values1) 398 } 399 } 400 401 type AwaitableForTester struct { 402 slice *AwaitableSlice[int] 403 IsReady Awaitable 404 IsClosed Awaitable 405 IsValues CyclicAwaitable 406 valueLock sync.Mutex 407 values []int 408 } 409 410 func NewAwaitableForTester(slice *AwaitableSlice[int]) (a *AwaitableForTester) { 411 return &AwaitableForTester{slice: slice} 412 } 413 414 func (a *AwaitableForTester) GoFor() { 415 a.IsReady.Close() 416 for value := a.slice.Init(); a.slice.Condition(&value); { 417 a.addValue(value) 418 } 419 a.IsClosed.Close() 420 } 421 422 func (a *AwaitableForTester) addValue(value int) { 423 a.valueLock.Lock() 424 defer a.IsValues.Close() 425 defer a.valueLock.Unlock() 426 427 a.values = append(a.values, value) 428 } 429 430 func (a *AwaitableForTester) Values() (values []int) { 431 a.valueLock.Lock() 432 defer a.valueLock.Unlock() 433 434 values = slices.Clone(a.values) 435 return 436 }