github.com/core-coin/go-core/v2@v2.1.9/event/feed_test.go (about) 1 // Copyright 2016 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package event 18 19 import ( 20 "fmt" 21 "reflect" 22 "sync" 23 "testing" 24 "time" 25 ) 26 27 func TestFeedPanics(t *testing.T) { 28 { 29 var f Feed 30 f.Send(2) 31 want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(0)} 32 if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { 33 t.Error(err) 34 } 35 } 36 { 37 var f Feed 38 ch := make(chan int) 39 f.Subscribe(ch) 40 want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(0)} 41 if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { 42 t.Error(err) 43 } 44 } 45 { 46 var f Feed 47 f.Send(2) 48 want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))} 49 if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil { 50 t.Error(err) 51 } 52 } 53 { 54 var f Feed 55 if err := checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) }); err != nil { 56 t.Error(err) 57 } 58 } 59 { 60 var f Feed 61 if err := checkPanic(errBadChannel, func() { f.Subscribe(0) }); err != nil { 62 t.Error(err) 63 } 64 } 65 } 66 67 func checkPanic(want error, fn func()) (err error) { 68 defer func() { 69 panic := recover() 70 if panic == nil { 71 err = fmt.Errorf("didn't panic") 72 } else if !reflect.DeepEqual(panic, want) { 73 err = fmt.Errorf("panicked with wrong error: got %q, want %q", panic, want) 74 } 75 }() 76 fn() 77 return nil 78 } 79 80 func TestFeed(t *testing.T) { 81 var feed Feed 82 var done, subscribed sync.WaitGroup 83 subscriber := func(i int) { 84 defer done.Done() 85 86 subchan := make(chan int) 87 sub := feed.Subscribe(subchan) 88 timeout := time.NewTimer(2 * time.Second) 89 defer timeout.Stop() 90 subscribed.Done() 91 92 select { 93 case v := <-subchan: 94 if v != 1 { 95 t.Errorf("%d: received value %d, want 1", i, v) 96 } 97 case <-timeout.C: 98 t.Errorf("%d: receive timeout", i) 99 } 100 101 sub.Unsubscribe() 102 select { 103 case _, ok := <-sub.Err(): 104 if ok { 105 t.Errorf("%d: error channel not closed after unsubscribe", i) 106 } 107 case <-timeout.C: 108 t.Errorf("%d: unsubscribe timeout", i) 109 } 110 } 111 112 const n = 1000 113 done.Add(n) 114 subscribed.Add(n) 115 for i := 0; i < n; i++ { 116 go subscriber(i) 117 } 118 subscribed.Wait() 119 if nsent := feed.Send(1); nsent != n { 120 t.Errorf("first send delivered %d times, want %d", nsent, n) 121 } 122 if nsent := feed.Send(2); nsent != 0 { 123 t.Errorf("second send delivered %d times, want 0", nsent) 124 } 125 done.Wait() 126 } 127 128 func TestFeedSubscribeSameChannel(t *testing.T) { 129 var ( 130 feed Feed 131 done sync.WaitGroup 132 ch = make(chan int) 133 sub1 = feed.Subscribe(ch) 134 sub2 = feed.Subscribe(ch) 135 _ = feed.Subscribe(ch) 136 ) 137 expectSends := func(value, n int) { 138 if nsent := feed.Send(value); nsent != n { 139 t.Errorf("send delivered %d times, want %d", nsent, n) 140 } 141 done.Done() 142 } 143 expectRecv := func(wantValue, n int) { 144 for i := 0; i < n; i++ { 145 if v := <-ch; v != wantValue { 146 t.Errorf("received %d, want %d", v, wantValue) 147 } 148 } 149 } 150 151 done.Add(1) 152 go expectSends(1, 3) 153 expectRecv(1, 3) 154 done.Wait() 155 156 sub1.Unsubscribe() 157 158 done.Add(1) 159 go expectSends(2, 2) 160 expectRecv(2, 2) 161 done.Wait() 162 163 sub2.Unsubscribe() 164 165 done.Add(1) 166 go expectSends(3, 1) 167 expectRecv(3, 1) 168 done.Wait() 169 } 170 171 func TestFeedSubscribeBlockedPost(t *testing.T) { 172 var ( 173 feed Feed 174 nsends = 2000 175 ch1 = make(chan int) 176 ch2 = make(chan int) 177 wg sync.WaitGroup 178 ) 179 defer wg.Wait() 180 181 feed.Subscribe(ch1) 182 wg.Add(nsends) 183 for i := 0; i < nsends; i++ { 184 go func() { 185 feed.Send(99) 186 wg.Done() 187 }() 188 } 189 190 sub2 := feed.Subscribe(ch2) 191 defer sub2.Unsubscribe() 192 193 // We're done when ch1 has received N times. 194 // The number of receives on ch2 depends on scheduling. 195 for i := 0; i < nsends; { 196 select { 197 case <-ch1: 198 i++ 199 case <-ch2: 200 } 201 } 202 } 203 204 func TestFeedUnsubscribeBlockedPost(t *testing.T) { 205 var ( 206 feed Feed 207 nsends = 200 208 chans = make([]chan int, 2000) 209 subs = make([]Subscription, len(chans)) 210 bchan = make(chan int) 211 bsub = feed.Subscribe(bchan) 212 wg sync.WaitGroup 213 ) 214 for i := range chans { 215 chans[i] = make(chan int, nsends) 216 } 217 218 // Queue up some Sends. None of these can make progress while bchan isn't read. 219 wg.Add(nsends) 220 for i := 0; i < nsends; i++ { 221 go func() { 222 feed.Send(99) 223 wg.Done() 224 }() 225 } 226 // Subscribe the other channels. 227 for i, ch := range chans { 228 subs[i] = feed.Subscribe(ch) 229 } 230 // Unsubscribe them again. 231 for _, sub := range subs { 232 sub.Unsubscribe() 233 } 234 // Unblock the Sends. 235 bsub.Unsubscribe() 236 wg.Wait() 237 } 238 239 // Checks that unsubscribing a channel during Send works even if that 240 // channel has already been sent on. 241 func TestFeedUnsubscribeSentChan(t *testing.T) { 242 var ( 243 feed Feed 244 ch1 = make(chan int) 245 ch2 = make(chan int) 246 sub1 = feed.Subscribe(ch1) 247 sub2 = feed.Subscribe(ch2) 248 wg sync.WaitGroup 249 ) 250 defer sub2.Unsubscribe() 251 252 wg.Add(1) 253 go func() { 254 feed.Send(0) 255 wg.Done() 256 }() 257 258 // Wait for the value on ch1. 259 <-ch1 260 // Unsubscribe ch1, removing it from the send cases. 261 sub1.Unsubscribe() 262 263 // Receive ch2, finishing Send. 264 <-ch2 265 wg.Wait() 266 267 // Send again. This should send to ch2 only, so the wait group will unblock 268 // as soon as a value is received on ch2. 269 wg.Add(1) 270 go func() { 271 feed.Send(0) 272 wg.Done() 273 }() 274 <-ch2 275 wg.Wait() 276 } 277 278 func TestFeedUnsubscribeFromInbox(t *testing.T) { 279 var ( 280 feed Feed 281 ch1 = make(chan int) 282 ch2 = make(chan int) 283 sub1 = feed.Subscribe(ch1) 284 sub2 = feed.Subscribe(ch1) 285 sub3 = feed.Subscribe(ch2) 286 ) 287 if len(feed.inbox) != 3 { 288 t.Errorf("inbox length != 3 after subscribe") 289 } 290 if len(feed.sendCases) != 1 { 291 t.Errorf("sendCases is non-empty after unsubscribe") 292 } 293 294 sub1.Unsubscribe() 295 sub2.Unsubscribe() 296 sub3.Unsubscribe() 297 if len(feed.inbox) != 0 { 298 t.Errorf("inbox is non-empty after unsubscribe") 299 } 300 if len(feed.sendCases) != 1 { 301 t.Errorf("sendCases is non-empty after unsubscribe") 302 } 303 } 304 305 func BenchmarkFeedSend1000(b *testing.B) { 306 var ( 307 done sync.WaitGroup 308 feed Feed 309 nsubs = 1000 310 ) 311 subscriber := func(ch <-chan int) { 312 for i := 0; i < b.N; i++ { 313 <-ch 314 } 315 done.Done() 316 } 317 done.Add(nsubs) 318 for i := 0; i < nsubs; i++ { 319 ch := make(chan int, 200) 320 feed.Subscribe(ch) 321 go subscriber(ch) 322 } 323 324 // The actual benchmark. 325 b.ResetTimer() 326 for i := 0; i < b.N; i++ { 327 if feed.Send(i) != nsubs { 328 panic("wrong number of sends") 329 } 330 } 331 332 b.StopTimer() 333 done.Wait() 334 }