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