github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/event/feed_test.go (about)

     1  package event
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  func TestFeedPanics(t *testing.T) {
    12  	{
    13  		var f Feed
    14  		f.Send(int(2))
    15  		want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))}
    16  		if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil {
    17  			t.Error(err)
    18  		}
    19  	}
    20  	{
    21  		var f Feed
    22  		ch := make(chan int)
    23  		f.Subscribe(ch)
    24  		want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))}
    25  		if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil {
    26  			t.Error(err)
    27  		}
    28  	}
    29  	{
    30  		var f Feed
    31  		f.Send(int(2))
    32  		want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))}
    33  		if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil {
    34  			t.Error(err)
    35  		}
    36  	}
    37  	{
    38  		var f Feed
    39  		if err := checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) }); err != nil {
    40  			t.Error(err)
    41  		}
    42  	}
    43  	{
    44  		var f Feed
    45  		if err := checkPanic(errBadChannel, func() { f.Subscribe(int(0)) }); err != nil {
    46  			t.Error(err)
    47  		}
    48  	}
    49  }
    50  
    51  func checkPanic(want error, fn func()) (err error) {
    52  	defer func() {
    53  		panic := recover()
    54  		if panic == nil {
    55  			err = fmt.Errorf("didn't panic")
    56  		} else if !reflect.DeepEqual(panic, want) {
    57  			err = fmt.Errorf("panicked with wrong error: got %q, want %q", panic, want)
    58  		}
    59  	}()
    60  	fn()
    61  	return nil
    62  }
    63  
    64  func TestFeed(t *testing.T) {
    65  	var feed Feed
    66  	var done, subscribed sync.WaitGroup
    67  	subscriber := func(i int) {
    68  		defer done.Done()
    69  
    70  		subchan := make(chan int)
    71  		sub := feed.Subscribe(subchan)
    72  		timeout := time.NewTimer(2 * time.Second)
    73  		subscribed.Done()
    74  
    75  		select {
    76  		case v := <-subchan:
    77  			if v != 1 {
    78  				t.Errorf("%d: received value %d, want 1", i, v)
    79  			}
    80  		case <-timeout.C:
    81  			t.Errorf("%d: receive timeout", i)
    82  		}
    83  
    84  		sub.Unsubscribe()
    85  		select {
    86  		case _, ok := <-sub.Err():
    87  			if ok {
    88  				t.Errorf("%d: error channel not closed after unsubscribe", i)
    89  			}
    90  		case <-timeout.C:
    91  			t.Errorf("%d: unsubscribe timeout", i)
    92  		}
    93  	}
    94  
    95  	const n = 1000
    96  	done.Add(n)
    97  	subscribed.Add(n)
    98  	for i := 0; i < n; i++ {
    99  		go subscriber(i)
   100  	}
   101  	subscribed.Wait()
   102  	if nsent := feed.Send(1); nsent != n {
   103  		t.Errorf("first send delivered %d times, want %d", nsent, n)
   104  	}
   105  	if nsent := feed.Send(2); nsent != 0 {
   106  		t.Errorf("second send delivered %d times, want 0", nsent)
   107  	}
   108  	done.Wait()
   109  }
   110  
   111  func TestFeedSubscribeSameChannel(t *testing.T) {
   112  	var (
   113  		feed Feed
   114  		done sync.WaitGroup
   115  		ch   = make(chan int)
   116  		sub1 = feed.Subscribe(ch)
   117  		sub2 = feed.Subscribe(ch)
   118  		_    = feed.Subscribe(ch)
   119  	)
   120  	expectSends := func(value, n int) {
   121  		if nsent := feed.Send(value); nsent != n {
   122  			t.Errorf("send delivered %d times, want %d", nsent, n)
   123  		}
   124  		done.Done()
   125  	}
   126  	expectRecv := func(wantValue, n int) {
   127  		for i := 0; i < n; i++ {
   128  			if v := <-ch; v != wantValue {
   129  				t.Errorf("received %d, want %d", v, wantValue)
   130  			}
   131  		}
   132  	}
   133  
   134  	done.Add(1)
   135  	go expectSends(1, 3)
   136  	expectRecv(1, 3)
   137  	done.Wait()
   138  
   139  	sub1.Unsubscribe()
   140  
   141  	done.Add(1)
   142  	go expectSends(2, 2)
   143  	expectRecv(2, 2)
   144  	done.Wait()
   145  
   146  	sub2.Unsubscribe()
   147  
   148  	done.Add(1)
   149  	go expectSends(3, 1)
   150  	expectRecv(3, 1)
   151  	done.Wait()
   152  }
   153  
   154  func TestFeedSubscribeBlockedPost(t *testing.T) {
   155  	var (
   156  		feed   Feed
   157  		nsends = 2000
   158  		ch1    = make(chan int)
   159  		ch2    = make(chan int)
   160  		wg     sync.WaitGroup
   161  	)
   162  	defer wg.Wait()
   163  
   164  	feed.Subscribe(ch1)
   165  	wg.Add(nsends)
   166  	for i := 0; i < nsends; i++ {
   167  		go func() {
   168  			feed.Send(99)
   169  			wg.Done()
   170  		}()
   171  	}
   172  
   173  	sub2 := feed.Subscribe(ch2)
   174  	defer sub2.Unsubscribe()
   175  
   176  	for i := 0; i < nsends; {
   177  		select {
   178  		case <-ch1:
   179  			i++
   180  		case <-ch2:
   181  		}
   182  	}
   183  }
   184  
   185  func TestFeedUnsubscribeBlockedPost(t *testing.T) {
   186  	var (
   187  		feed   Feed
   188  		nsends = 200
   189  		chans  = make([]chan int, 2000)
   190  		subs   = make([]Subscription, len(chans))
   191  		bchan  = make(chan int)
   192  		bsub   = feed.Subscribe(bchan)
   193  		wg     sync.WaitGroup
   194  	)
   195  	for i := range chans {
   196  		chans[i] = make(chan int, nsends)
   197  	}
   198  
   199  	wg.Add(nsends)
   200  	for i := 0; i < nsends; i++ {
   201  		go func() {
   202  			feed.Send(99)
   203  			wg.Done()
   204  		}()
   205  	}
   206  
   207  	for i, ch := range chans {
   208  		subs[i] = feed.Subscribe(ch)
   209  	}
   210  
   211  	for _, sub := range subs {
   212  		sub.Unsubscribe()
   213  	}
   214  
   215  	bsub.Unsubscribe()
   216  	wg.Wait()
   217  }
   218  
   219  func TestFeedUnsubscribeSentChan(t *testing.T) {
   220  	var (
   221  		feed Feed
   222  		ch1  = make(chan int)
   223  		ch2  = make(chan int)
   224  		sub1 = feed.Subscribe(ch1)
   225  		sub2 = feed.Subscribe(ch2)
   226  		wg   sync.WaitGroup
   227  	)
   228  	defer sub2.Unsubscribe()
   229  
   230  	wg.Add(1)
   231  	go func() {
   232  		feed.Send(0)
   233  		wg.Done()
   234  	}()
   235  
   236  	<-ch1
   237  
   238  	sub1.Unsubscribe()
   239  
   240  	<-ch2
   241  	wg.Wait()
   242  
   243  	wg.Add(1)
   244  	go func() {
   245  		feed.Send(0)
   246  		wg.Done()
   247  	}()
   248  	<-ch2
   249  	wg.Wait()
   250  }
   251  
   252  func TestFeedUnsubscribeFromInbox(t *testing.T) {
   253  	var (
   254  		feed Feed
   255  		ch1  = make(chan int)
   256  		ch2  = make(chan int)
   257  		sub1 = feed.Subscribe(ch1)
   258  		sub2 = feed.Subscribe(ch1)
   259  		sub3 = feed.Subscribe(ch2)
   260  	)
   261  	if len(feed.inbox) != 3 {
   262  		t.Errorf("inbox length != 3 after subscribe")
   263  	}
   264  	if len(feed.sendCases) != 1 {
   265  		t.Errorf("sendCases is non-empty after unsubscribe")
   266  	}
   267  
   268  	sub1.Unsubscribe()
   269  	sub2.Unsubscribe()
   270  	sub3.Unsubscribe()
   271  	if len(feed.inbox) != 0 {
   272  		t.Errorf("inbox is non-empty after unsubscribe")
   273  	}
   274  	if len(feed.sendCases) != 1 {
   275  		t.Errorf("sendCases is non-empty after unsubscribe")
   276  	}
   277  }
   278  
   279  func BenchmarkFeedSend1000(b *testing.B) {
   280  	var (
   281  		done  sync.WaitGroup
   282  		feed  Feed
   283  		nsubs = 1000
   284  	)
   285  	subscriber := func(ch <-chan int) {
   286  		for i := 0; i < b.N; i++ {
   287  			<-ch
   288  		}
   289  		done.Done()
   290  	}
   291  	done.Add(nsubs)
   292  	for i := 0; i < nsubs; i++ {
   293  		ch := make(chan int, 200)
   294  		feed.Subscribe(ch)
   295  		go subscriber(ch)
   296  	}
   297  
   298  	b.ResetTimer()
   299  	for i := 0; i < b.N; i++ {
   300  		if feed.Send(i) != nsubs {
   301  			panic("wrong number of sends")
   302  		}
   303  	}
   304  
   305  	b.StopTimer()
   306  	done.Wait()
   307  }