github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/event/feed_test.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum 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  func TestFeedUnsubscribeFromInbox(t *testing.T) {
   239  	var (
   240  		feed Feed
   241  		ch1  = make(chan int)
   242  		ch2  = make(chan int)
   243  		sub1 = feed.Subscribe(ch1)
   244  		sub2 = feed.Subscribe(ch1)
   245  		sub3 = feed.Subscribe(ch2)
   246  	)
   247  	if len(feed.inbox) != 3 {
   248  		t.Errorf("inbox length != 3 after subscribe")
   249  	}
   250  	if len(feed.sendCases) != 1 {
   251  		t.Errorf("sendCases is non-empty after unsubscribe")
   252  	}
   253  
   254  	sub1.Unsubscribe()
   255  	sub2.Unsubscribe()
   256  	sub3.Unsubscribe()
   257  	if len(feed.inbox) != 0 {
   258  		t.Errorf("inbox is non-empty after unsubscribe")
   259  	}
   260  	if len(feed.sendCases) != 1 {
   261  		t.Errorf("sendCases is non-empty after unsubscribe")
   262  	}
   263  }
   264  
   265  func BenchmarkFeedSend1000(b *testing.B) {
   266  	var (
   267  		done  sync.WaitGroup
   268  		feed  Feed
   269  		nsubs = 1000
   270  	)
   271  	subscriber := func(ch <-chan int) {
   272  		for i := 0; i < b.N; i++ {
   273  			<-ch
   274  		}
   275  		done.Done()
   276  	}
   277  	done.Add(nsubs)
   278  	for i := 0; i < nsubs; i++ {
   279  		ch := make(chan int, 200)
   280  		feed.Subscribe(ch)
   281  		go subscriber(ch)
   282  	}
   283  
   284  	// The actual benchmark.
   285  	b.ResetTimer()
   286  	for i := 0; i < b.N; i++ {
   287  		if feed.Send(i) != nsubs {
   288  			panic("wrong number of sends")
   289  		}
   290  	}
   291  
   292  	b.StopTimer()
   293  	done.Wait()
   294  }