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