github.com/haraldrudell/parl@v0.4.176/wait-group-ch_test.go (about) 1 /* 2 © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "sync" 10 "sync/atomic" 11 "testing" 12 "time" 13 ) 14 15 func TestWaitGroupCh(t *testing.T) { 16 var adds0 = 2 17 var adds1 = 1 18 var currentCountExp0 = 1 19 var sExp = "waitGroupCh_count:0(adds:0)" 20 21 var awaitableCh AwaitableCh 22 var isClosed, isExit, isZero bool 23 var currentCount, totalAdds int 24 var s string 25 26 // Add() Ch() Count() Done() DoneBool() IsZero() Reset() String() 27 // Wait() 28 var w WaitGroupCh 29 var reset = func() { 30 w = WaitGroupCh{} 31 } 32 33 // Ch returns a non-closed channel 34 reset() 35 w.Add(adds1) 36 awaitableCh = w.Ch() 37 select { 38 case <-awaitableCh: 39 isClosed = true 40 default: 41 isClosed = false 42 } 43 if isClosed { 44 t.Error("Ch is closed") 45 } 46 47 // Ch channel closes 48 reset() 49 w.Add(adds1) 50 awaitableCh = w.Ch() 51 w.Done() 52 select { 53 case <-awaitableCh: 54 isClosed = true 55 default: 56 isClosed = false 57 } 58 if !isClosed { 59 t.Error("Ch not closed") 60 } 61 62 // Count returns zeroes 63 reset() 64 currentCount, totalAdds = w.Count() 65 if currentCount != 0 { 66 t.Errorf("Count currentCount %d exp 0", currentCount) 67 } 68 if totalAdds != 0 { 69 t.Errorf("Count totalAdds %d exp 0", totalAdds) 70 } 71 72 // Count reflect Adds Dones 73 // - Add 74 // - Done 75 reset() 76 w.Add(adds0) 77 w.Done() 78 currentCount, totalAdds = w.Count() 79 if currentCount != currentCountExp0 { 80 t.Errorf("Count currentCount %d exp %d", currentCount, currentCountExp0) 81 } 82 if totalAdds != adds0 { 83 t.Errorf("Count totalAdds %d exp %d", totalAdds, adds0) 84 } 85 86 // DoneBool 87 reset() 88 w.Add(adds0) 89 isExit = w.DoneBool() 90 if isExit { 91 t.Error("DoneBool isExit true") 92 } 93 isExit = w.DoneBool() 94 if !isExit { 95 t.Error("DoneBool isExit false") 96 } 97 98 // Reset 99 reset() 100 w.Add(adds0) 101 if w.p.Load() == nil { 102 t.Error("w.p.Load nil") 103 } 104 w.Reset() 105 if w.p.Load() != nil { 106 t.Error("w.p.Load not nil") 107 } 108 109 // IsZero 110 reset() 111 w.Add(adds1) 112 isZero = w.IsZero() 113 if isZero { 114 t.Error("IsZero true") 115 } 116 w.Done() 117 isZero = w.IsZero() 118 if !isZero { 119 t.Error("IsZero false") 120 } 121 122 // String() 123 reset() 124 s = w.String() 125 if s != sExp { 126 t.Errorf("String %q exp %q", s, sExp) 127 } 128 } 129 130 func TestWaitGroupChWait(t *testing.T) { 131 var adds1 = 1 132 var shortTime = time.Millisecond 133 134 var isReady sync.WaitGroup 135 var isDone chan struct{} 136 var isWaitReturn atomic.Bool 137 var timer *time.Timer 138 139 // Add() Ch() Count() Done() DoneBool() IsZero() Reset() String() 140 // Wait() 141 var w WaitGroupCh 142 var reset = func() { 143 w = WaitGroupCh{} 144 } 145 146 // Wait waits until counter zero 147 reset() 148 w.Add(adds1) 149 isWaitReturn.Store(false) 150 isReady = sync.WaitGroup{} 151 isReady.Add(1) 152 isDone = make(chan struct{}) 153 go waiter(&w, &isWaitReturn, &isReady, isDone) 154 isReady.Wait() 155 <-time.NewTimer(shortTime).C 156 if isWaitReturn.Load() { 157 t.Error("Wait returned prematurely") 158 } 159 w.Done() 160 // race condition between w.ch closing and 161 // waiter triggering isDone 162 timer = time.NewTimer(shortTime) 163 select { 164 case <-isDone: 165 case <-timer.C: 166 } 167 if !isWaitReturn.Load() { 168 t.Error("Wait did not return on Done") 169 } 170 171 } 172 173 // waiter tests WaitGroupCh.Wait() 174 func waiter( 175 w *WaitGroupCh, 176 isWaitReturn *atomic.Bool, 177 isReady Doneable, 178 isDone chan struct{}, 179 ) { 180 defer close(isDone) 181 defer isWaitReturn.Store(true) 182 183 isReady.Done() 184 w.Wait() 185 }