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  }