github.com/flashbots/go-ethereum@v1.9.7/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 27 func TestFeedPanics(t *testing.T) { 28 { 29 var f Feed 30 f.Send(int(2)) 31 want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(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(int(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(int(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(int(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 subscribed.Done() 90 91 select { 92 case v := <-subchan: 93 if v != 1 { 94 t.Errorf("%d: received value %d, want 1", i, v) 95 } 96 case <-timeout.C: 97 t.Errorf("%d: receive timeout", i) 98 } 99 100 sub.Unsubscribe() 101 select { 102 case _, ok := <-sub.Err(): 103 if ok { 104 t.Errorf("%d: error channel not closed after unsubscribe", i) 105 } 106 case <-timeout.C: 107 t.Errorf("%d: unsubscribe timeout", i) 108 } 109 } 110 111 const n = 1000 112 done.Add(n) 113 subscribed.Add(n) 114 for i := 0; i < n; i++ { 115 go subscriber(i) 116 } 117 subscribed.Wait() 118 if nsent := feed.Send(1); nsent != n { 119 t.Errorf("first send delivered %d times, want %d", nsent, n) 120 } 121 if nsent := feed.Send(2); nsent != 0 { 122 t.Errorf("second send delivered %d times, want 0", nsent) 123 } 124 done.Wait() 125 } 126 127 func TestFeedSubscribeSameChannel(t *testing.T) { 128 var ( 129 feed Feed 130 done sync.WaitGroup 131 ch = make(chan int) 132 sub1 = feed.Subscribe(ch) 133 sub2 = feed.Subscribe(ch) 134 _ = feed.Subscribe(ch) 135 ) 136 expectSends := func(value, n int) { 137 if nsent := feed.Send(value); nsent != n { 138 t.Errorf("send delivered %d times, want %d", nsent, n) 139 } 140 done.Done() 141 } 142 expectRecv := func(wantValue, n int) { 143 for i := 0; i < n; i++ { 144 if v := <-ch; v != wantValue { 145 t.Errorf("received %d, want %d", v, wantValue) 146 } 147 } 148 } 149 150 done.Add(1) 151 go expectSends(1, 3) 152 expectRecv(1, 3) 153 done.Wait() 154 155 sub1.Unsubscribe() 156 157 done.Add(1) 158 go expectSends(2, 2) 159 expectRecv(2, 2) 160 done.Wait() 161 162 sub2.Unsubscribe() 163 164 done.Add(1) 165 go expectSends(3, 1) 166 expectRecv(3, 1) 167 done.Wait() 168 } 169 170 func TestFeedSubscribeBlockedPost(t *testing.T) { 171 var ( 172 feed Feed 173 nsends = 2000 174 ch1 = make(chan int) 175 ch2 = make(chan int) 176 wg sync.WaitGroup 177 ) 178 defer wg.Wait() 179 180 feed.Subscribe(ch1) 181 wg.Add(nsends) 182 for i := 0; i < nsends; i++ { 183 go func() { 184 feed.Send(99) 185 wg.Done() 186 }() 187 } 188 189 sub2 := feed.Subscribe(ch2) 190 defer sub2.Unsubscribe() 191 192 // We're done when ch1 has received N times. 193 // The number of receives on ch2 depends on scheduling. 194 for i := 0; i < nsends; { 195 select { 196 case <-ch1: 197 i++ 198 case <-ch2: 199 } 200 } 201 } 202 203 func TestFeedUnsubscribeBlockedPost(t *testing.T) { 204 var ( 205 feed Feed 206 nsends = 200 207 chans = make([]chan int, 2000) 208 subs = make([]Subscription, len(chans)) 209 bchan = make(chan int) 210 bsub = feed.Subscribe(bchan) 211 wg sync.WaitGroup 212 ) 213 for i := range chans { 214 chans[i] = make(chan int, nsends) 215 } 216 217 // Queue up some Sends. None of these can make progress while bchan isn't read. 218 wg.Add(nsends) 219 for i := 0; i < nsends; i++ { 220 go func() { 221 feed.Send(99) 222 wg.Done() 223 }() 224 } 225 // Subscribe the other channels. 226 for i, ch := range chans { 227 subs[i] = feed.Subscribe(ch) 228 } 229 // Unsubscribe them again. 230 for _, sub := range subs { 231 sub.Unsubscribe() 232 } 233 // Unblock the Sends. 234 bsub.Unsubscribe() 235 wg.Wait() 236 } 237 238 // Checks that unsubscribing a channel during Send works even if that 239 // channel has already been sent on. 240 func TestFeedUnsubscribeSentChan(t *testing.T) { 241 var ( 242 feed Feed 243 ch1 = make(chan int) 244 ch2 = make(chan int) 245 sub1 = feed.Subscribe(ch1) 246 sub2 = feed.Subscribe(ch2) 247 wg sync.WaitGroup 248 ) 249 defer sub2.Unsubscribe() 250 251 wg.Add(1) 252 go func() { 253 feed.Send(0) 254 wg.Done() 255 }() 256 257 // Wait for the value on ch1. 258 <-ch1 259 // Unsubscribe ch1, removing it from the send cases. 260 sub1.Unsubscribe() 261 262 // Receive ch2, finishing Send. 263 <-ch2 264 wg.Wait() 265 266 // Send again. This should send to ch2 only, so the wait group will unblock 267 // as soon as a value is received on ch2. 268 wg.Add(1) 269 go func() { 270 feed.Send(0) 271 wg.Done() 272 }() 273 <-ch2 274 wg.Wait() 275 } 276 277 func TestFeedUnsubscribeFromInbox(t *testing.T) { 278 var ( 279 feed Feed 280 ch1 = make(chan int) 281 ch2 = make(chan int) 282 sub1 = feed.Subscribe(ch1) 283 sub2 = feed.Subscribe(ch1) 284 sub3 = feed.Subscribe(ch2) 285 ) 286 if len(feed.inbox) != 3 { 287 t.Errorf("inbox length != 3 after subscribe") 288 } 289 if len(feed.sendCases) != 1 { 290 t.Errorf("sendCases is non-empty after unsubscribe") 291 } 292 293 sub1.Unsubscribe() 294 sub2.Unsubscribe() 295 sub3.Unsubscribe() 296 if len(feed.inbox) != 0 { 297 t.Errorf("inbox is non-empty after unsubscribe") 298 } 299 if len(feed.sendCases) != 1 { 300 t.Errorf("sendCases is non-empty after unsubscribe") 301 } 302 } 303 304 func BenchmarkFeedSend1000(b *testing.B) { 305 var ( 306 done sync.WaitGroup 307 feed Feed 308 nsubs = 1000 309 ) 310 subscriber := func(ch <-chan int) { 311 for i := 0; i < b.N; i++ { 312 <-ch 313 } 314 done.Done() 315 } 316 done.Add(nsubs) 317 for i := 0; i < nsubs; i++ { 318 ch := make(chan int, 200) 319 feed.Subscribe(ch) 320 go subscriber(ch) 321 } 322 323 // The actual benchmark. 324 b.ResetTimer() 325 for i := 0; i < b.N; i++ { 326 if feed.Send(i) != nsubs { 327 panic("wrong number of sends") 328 } 329 } 330 331 b.StopTimer() 332 done.Wait() 333 }