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