github.com/prysmaticlabs/prysm@v1.4.4/shared/event/feed_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum 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 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 27 ) 28 29 func TestFeedPanics(t *testing.T) { 30 { 31 var f Feed 32 f.Send(2) 33 want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(0)} 34 assert.NoError(t, checkPanic(want, func() { f.Send(uint64(2)) })) 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 assert.NoError(t, checkPanic(want, func() { f.Send(uint64(2)) })) 42 } 43 { 44 var f Feed 45 f.Send(2) 46 want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))} 47 assert.NoError(t, checkPanic(want, func() { f.Subscribe(make(chan uint64)) })) 48 } 49 { 50 var f Feed 51 assert.NoError(t, checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) })) 52 } 53 { 54 var f Feed 55 assert.NoError(t, checkPanic(errBadChannel, func() { f.Subscribe(0) })) 56 } 57 } 58 59 func checkPanic(want error, fn func()) (err error) { 60 defer func() { 61 panicResult := recover() 62 if panicResult == nil { 63 err = fmt.Errorf("didn't panic") 64 } else if !reflect.DeepEqual(panicResult, want) { 65 err = fmt.Errorf("panicked with wrong error: got %q, want %q", panicResult, want) 66 } 67 }() 68 fn() 69 return nil 70 } 71 72 func TestFeed(t *testing.T) { 73 var feed Feed 74 var done, subscribed sync.WaitGroup 75 subscriber := func(i int) { 76 defer done.Done() 77 78 subchan := make(chan int) 79 sub := feed.Subscribe(subchan) 80 timeout := time.NewTimer(2 * time.Second) 81 subscribed.Done() 82 83 select { 84 case v := <-subchan: 85 if v != 1 { 86 t.Errorf("%d: received value %d, want 1", i, v) 87 } 88 case <-timeout.C: 89 t.Errorf("%d: receive timeout", i) 90 } 91 92 sub.Unsubscribe() 93 select { 94 case _, ok := <-sub.Err(): 95 if ok { 96 t.Errorf("%d: error channel not closed after unsubscribe", i) 97 } 98 case <-timeout.C: 99 t.Errorf("%d: unsubscribe timeout", i) 100 } 101 } 102 103 const n = 1000 104 done.Add(n) 105 subscribed.Add(n) 106 for i := 0; i < n; i++ { 107 go subscriber(i) 108 } 109 subscribed.Wait() 110 if nsent := feed.Send(1); nsent != n { 111 t.Errorf("first send delivered %d times, want %d", nsent, n) 112 } 113 if nsent := feed.Send(2); nsent != 0 { 114 t.Errorf("second send delivered %d times, want 0", nsent) 115 } 116 done.Wait() 117 } 118 119 func TestFeedSubscribeSameChannel(t *testing.T) { 120 var ( 121 feed Feed 122 done sync.WaitGroup 123 ch = make(chan int) 124 sub1 = feed.Subscribe(ch) 125 sub2 = feed.Subscribe(ch) 126 _ = feed.Subscribe(ch) 127 ) 128 expectSends := func(value, n int) { 129 if nsent := feed.Send(value); nsent != n { 130 t.Errorf("send delivered %d times, want %d", nsent, n) 131 } 132 done.Done() 133 } 134 expectRecv := func(wantValue, n int) { 135 for i := 0; i < n; i++ { 136 if v := <-ch; v != wantValue { 137 t.Errorf("received %d, want %d", v, wantValue) 138 } 139 } 140 } 141 142 done.Add(1) 143 go expectSends(1, 3) 144 expectRecv(1, 3) 145 done.Wait() 146 147 sub1.Unsubscribe() 148 149 done.Add(1) 150 go expectSends(2, 2) 151 expectRecv(2, 2) 152 done.Wait() 153 154 sub2.Unsubscribe() 155 156 done.Add(1) 157 go expectSends(3, 1) 158 expectRecv(3, 1) 159 done.Wait() 160 } 161 162 func TestFeedSubscribeBlockedPost(t *testing.T) { 163 var ( 164 feed Feed 165 nsends = 2000 166 ch1 = make(chan int) 167 ch2 = make(chan int) 168 wg sync.WaitGroup 169 ) 170 defer wg.Wait() 171 172 feed.Subscribe(ch1) 173 wg.Add(nsends) 174 for i := 0; i < nsends; i++ { 175 go func() { 176 feed.Send(99) 177 wg.Done() 178 }() 179 } 180 181 sub2 := feed.Subscribe(ch2) 182 defer sub2.Unsubscribe() 183 184 // We're done when ch1 has received N times. 185 // The number of receives on ch2 depends on scheduling. 186 for i := 0; i < nsends; { 187 select { 188 case <-ch1: 189 i++ 190 case <-ch2: 191 } 192 } 193 } 194 195 func TestFeedUnsubscribeBlockedPost(t *testing.T) { 196 var ( 197 feed Feed 198 nsends = 200 199 chans = make([]chan int, 2000) 200 subs = make([]Subscription, len(chans)) 201 bchan = make(chan int) 202 bsub = feed.Subscribe(bchan) 203 wg sync.WaitGroup 204 ) 205 for i := range chans { 206 chans[i] = make(chan int, nsends) 207 } 208 209 // Queue up some Sends. None of these can make progress while bchan isn't read. 210 wg.Add(nsends) 211 for i := 0; i < nsends; i++ { 212 go func() { 213 feed.Send(99) 214 wg.Done() 215 }() 216 } 217 // Subscribe the other channels. 218 for i, ch := range chans { 219 subs[i] = feed.Subscribe(ch) 220 } 221 // Unsubscribe them again. 222 for _, sub := range subs { 223 sub.Unsubscribe() 224 } 225 // Unblock the Sends. 226 bsub.Unsubscribe() 227 wg.Wait() 228 } 229 230 // Checks that unsubscribing a channel during Send works even if that 231 // channel has already been sent on. 232 func TestFeedUnsubscribeSentChan(t *testing.T) { 233 var ( 234 feed Feed 235 ch1 = make(chan int) 236 ch2 = make(chan int) 237 sub1 = feed.Subscribe(ch1) 238 sub2 = feed.Subscribe(ch2) 239 wg sync.WaitGroup 240 ) 241 defer sub2.Unsubscribe() 242 243 wg.Add(1) 244 go func() { 245 feed.Send(0) 246 wg.Done() 247 }() 248 249 // Wait for the value on ch1. 250 <-ch1 251 // Unsubscribe ch1, removing it from the send cases. 252 sub1.Unsubscribe() 253 254 // Receive ch2, finishing Send. 255 <-ch2 256 wg.Wait() 257 258 // Send again. This should send to ch2 only, so the wait group will unblock 259 // as soon as a value is received on ch2. 260 wg.Add(1) 261 go func() { 262 feed.Send(0) 263 wg.Done() 264 }() 265 <-ch2 266 wg.Wait() 267 } 268 269 func TestFeedUnsubscribeFromInbox(t *testing.T) { 270 var ( 271 feed Feed 272 ch1 = make(chan int) 273 ch2 = make(chan int) 274 sub1 = feed.Subscribe(ch1) 275 sub2 = feed.Subscribe(ch1) 276 sub3 = feed.Subscribe(ch2) 277 ) 278 assert.Equal(t, 3, len(feed.inbox)) 279 assert.Equal(t, 1, len(feed.sendCases), "sendCases is non-empty after unsubscribe") 280 281 sub1.Unsubscribe() 282 sub2.Unsubscribe() 283 sub3.Unsubscribe() 284 assert.Equal(t, 0, len(feed.inbox), "Inbox is non-empty after unsubscribe") 285 assert.Equal(t, 1, len(feed.sendCases), "sendCases is non-empty after unsubscribe") 286 } 287 288 func BenchmarkFeedSend1000(b *testing.B) { 289 var ( 290 done sync.WaitGroup 291 feed Feed 292 nsubs = 1000 293 ) 294 subscriber := func(ch <-chan int) { 295 for i := 0; i < b.N; i++ { 296 <-ch 297 } 298 done.Done() 299 } 300 done.Add(nsubs) 301 for i := 0; i < nsubs; i++ { 302 ch := make(chan int, 200) 303 feed.Subscribe(ch) 304 go subscriber(ch) 305 } 306 307 // The actual benchmark. 308 b.ResetTimer() 309 for i := 0; i < b.N; i++ { 310 if feed.Send(i) != nsubs { 311 panic("wrong number of sends") 312 } 313 } 314 315 b.StopTimer() 316 done.Wait() 317 }