github.com/klaytn/klaytn@v1.12.1/event/feed_test.go (about)

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