github.com/core-coin/go-core/v2@v2.1.9/event/feed_test.go (about)

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