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