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