github.com/haraldrudell/parl@v0.4.176/nb-rare-chan_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 "testing" 10 "time" 11 ) 12 13 func TestNBRareChan(t *testing.T) { 14 var value1, value2 = 1, 2 15 const shortTime = time.Millisecond 16 var expLength = 2 17 18 var ch <-chan int 19 var actual int 20 var actualValues []int 21 var emptyAwaitable AwaitableCh 22 var isClose, ok bool 23 var timer *time.Timer 24 var timerC = func() (C <-chan time.Time) { 25 if timer == nil { 26 timer = time.NewTimer(shortTime) 27 } else { 28 timer.Stop() 29 if len(timer.C) > 0 { 30 <-timer.C 31 } 32 timer.Reset(shortTime) 33 } 34 C = timer.C 35 return 36 } 37 38 // Ch Close IsClose PanicCh Send StopSend 39 var nbChan *NBRareChan[int] 40 var reset = func() { 41 nbChan = &NBRareChan[int]{} 42 } 43 44 // Send one value should work 45 // - also tests Ch for returning non-nil value 46 reset() 47 nbChan.Send(value1) 48 select { 49 case actual = <-nbChan.Ch(): 50 case <-timerC(): 51 t.Fatal("nbChan.Ch timeout") 52 } 53 if actual != value1 { 54 t.Errorf("Send: %d exp %d", actual, value1) 55 } 56 57 // Send two values should work 58 reset() 59 nbChan.Send(value1) 60 nbChan.Send(value2) 61 select { 62 case actual = <-nbChan.Ch(): 63 case <-timerC(): 64 t.Fatal("nbChan.Ch timeout") 65 } 66 if actual != value1 { 67 t.Errorf("Send: %d exp %d", actual, value1) 68 } 69 select { 70 case actual = <-nbChan.Ch(): 71 case <-timerC(): 72 t.Fatal("nbChan.Ch timeout") 73 } 74 if actual != value2 { 75 t.Errorf("Send: %d exp %d", actual, value2) 76 } 77 78 // StopSend should be effective 79 reset() 80 emptyAwaitable = nbChan.StopSend() 81 _ = emptyAwaitable 82 nbChan.Send(value1) 83 select { 84 case <-nbChan.Ch(): 85 t.Error("StopSend not disabling Send") 86 case <-timerC(): 87 } 88 89 // StopSend: emptyAwaitable should work 90 reset() 91 nbChan.Send(value1) 92 emptyAwaitable = nbChan.StopSend() 93 // StopSend should return non-nil channel 94 if emptyAwaitable == nil { 95 t.Fatal("StopSend emptyawaitable nil") 96 } 97 // StopSend should return untriggered open awaitable for non-empty channel 98 select { 99 case <-emptyAwaitable: 100 t.Error("StopSend emptyAwaitable closed before channel empty") 101 default: 102 } 103 select { 104 case <-nbChan.Ch(): 105 case <-timerC(): 106 t.Fatal("nbChan.Ch timeout") 107 } 108 // StopSend-provided awaitable should close upon channel emptying 109 // - in race condition with exiting sendThread 110 // - use sync.WaitGroup to provide synchronization 111 nbChan.threadWait.Wait() 112 select { 113 case <-emptyAwaitable: 114 default: 115 t.Error("StopSend emptyAwaitable not closing on empty channel") 116 } 117 118 // Close should not hang 119 reset() 120 nbChan.Close(nil, nil) 121 // Close should close underlying channel 122 ch = nbChan.Ch() 123 // if ch is closed: 124 // - channel receive will happen, not default 125 // - ok will be false 126 ok = true 127 select { 128 case actual, ok = <-ch: 129 default: 130 t.Error("nbChanClose ch receive default") 131 } 132 if ok { 133 t.Error("nbChanClose ch ok true") 134 } 135 136 // Close should return thread-value and queue 137 reset() 138 nbChan.Send(value1) 139 nbChan.Send(value2) 140 nbChan.Close(&actualValues, nil) 141 if len(actualValues) != expLength { 142 t.Fatalf("Close bad length %d exp %d", len(actualValues), expLength) 143 } 144 if actualValues[0] != value1 { 145 t.Errorf("Close first value %d exp %d", actualValues[0], value1) 146 } 147 if actualValues[1] != value2 { 148 t.Errorf("Close second value %d exp %d", actualValues[1], value2) 149 } 150 151 // IsClose should start false 152 reset() 153 isClose = nbChan.IsClose() 154 if isClose { 155 t.Error("IsClose started true") 156 } 157 nbChan.Close(nil, nil) 158 // IsClose should be true after Close 159 isClose = nbChan.IsClose() 160 if !isClose { 161 t.Error("IsClose ineffective") 162 } 163 164 // PanicCh should return non-nil emptyAwaitable 165 reset() 166 emptyAwaitable = nbChan.PanicCh() 167 if emptyAwaitable == nil { 168 t.Fatal("PanicCh nil emptyAwaitable") 169 } 170 // PanicCh emptyAwaitable should not be triggered 171 select { 172 case <-emptyAwaitable: 173 t.Error("PanicCh emptyAwaitable triggered") 174 default: 175 } 176 }