github.com/theQRL/go-zond@v0.1.1/event/feedof_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  	"sync"
    21  	"testing"
    22  	"time"
    23  )
    24  
    25  func TestFeedOf(t *testing.T) {
    26  	var feed FeedOf[int]
    27  	var done, subscribed sync.WaitGroup
    28  	subscriber := func(i int) {
    29  		defer done.Done()
    30  
    31  		subchan := make(chan int)
    32  		sub := feed.Subscribe(subchan)
    33  		timeout := time.NewTimer(2 * time.Second)
    34  		defer timeout.Stop()
    35  		subscribed.Done()
    36  
    37  		select {
    38  		case v := <-subchan:
    39  			if v != 1 {
    40  				t.Errorf("%d: received value %d, want 1", i, v)
    41  			}
    42  		case <-timeout.C:
    43  			t.Errorf("%d: receive timeout", i)
    44  		}
    45  
    46  		sub.Unsubscribe()
    47  		select {
    48  		case _, ok := <-sub.Err():
    49  			if ok {
    50  				t.Errorf("%d: error channel not closed after unsubscribe", i)
    51  			}
    52  		case <-timeout.C:
    53  			t.Errorf("%d: unsubscribe timeout", i)
    54  		}
    55  	}
    56  
    57  	const n = 1000
    58  	done.Add(n)
    59  	subscribed.Add(n)
    60  	for i := 0; i < n; i++ {
    61  		go subscriber(i)
    62  	}
    63  	subscribed.Wait()
    64  	if nsent := feed.Send(1); nsent != n {
    65  		t.Errorf("first send delivered %d times, want %d", nsent, n)
    66  	}
    67  	if nsent := feed.Send(2); nsent != 0 {
    68  		t.Errorf("second send delivered %d times, want 0", nsent)
    69  	}
    70  	done.Wait()
    71  }
    72  
    73  func TestFeedOfSubscribeSameChannel(t *testing.T) {
    74  	var (
    75  		feed FeedOf[int]
    76  		done sync.WaitGroup
    77  		ch   = make(chan int)
    78  		sub1 = feed.Subscribe(ch)
    79  		sub2 = feed.Subscribe(ch)
    80  		_    = feed.Subscribe(ch)
    81  	)
    82  	expectSends := func(value, n int) {
    83  		if nsent := feed.Send(value); nsent != n {
    84  			t.Errorf("send delivered %d times, want %d", nsent, n)
    85  		}
    86  		done.Done()
    87  	}
    88  	expectRecv := func(wantValue, n int) {
    89  		for i := 0; i < n; i++ {
    90  			if v := <-ch; v != wantValue {
    91  				t.Errorf("received %d, want %d", v, wantValue)
    92  			}
    93  		}
    94  	}
    95  
    96  	done.Add(1)
    97  	go expectSends(1, 3)
    98  	expectRecv(1, 3)
    99  	done.Wait()
   100  
   101  	sub1.Unsubscribe()
   102  
   103  	done.Add(1)
   104  	go expectSends(2, 2)
   105  	expectRecv(2, 2)
   106  	done.Wait()
   107  
   108  	sub2.Unsubscribe()
   109  
   110  	done.Add(1)
   111  	go expectSends(3, 1)
   112  	expectRecv(3, 1)
   113  	done.Wait()
   114  }
   115  
   116  func TestFeedOfSubscribeBlockedPost(t *testing.T) {
   117  	var (
   118  		feed   FeedOf[int]
   119  		nsends = 2000
   120  		ch1    = make(chan int)
   121  		ch2    = make(chan int)
   122  		wg     sync.WaitGroup
   123  	)
   124  	defer wg.Wait()
   125  
   126  	feed.Subscribe(ch1)
   127  	wg.Add(nsends)
   128  	for i := 0; i < nsends; i++ {
   129  		go func() {
   130  			feed.Send(99)
   131  			wg.Done()
   132  		}()
   133  	}
   134  
   135  	sub2 := feed.Subscribe(ch2)
   136  	defer sub2.Unsubscribe()
   137  
   138  	// We're done when ch1 has received N times.
   139  	// The number of receives on ch2 depends on scheduling.
   140  	for i := 0; i < nsends; {
   141  		select {
   142  		case <-ch1:
   143  			i++
   144  		case <-ch2:
   145  		}
   146  	}
   147  }
   148  
   149  func TestFeedOfUnsubscribeBlockedPost(t *testing.T) {
   150  	var (
   151  		feed   FeedOf[int]
   152  		nsends = 200
   153  		chans  = make([]chan int, 2000)
   154  		subs   = make([]Subscription, len(chans))
   155  		bchan  = make(chan int)
   156  		bsub   = feed.Subscribe(bchan)
   157  		wg     sync.WaitGroup
   158  	)
   159  	for i := range chans {
   160  		chans[i] = make(chan int, nsends)
   161  	}
   162  
   163  	// Queue up some Sends. None of these can make progress while bchan isn't read.
   164  	wg.Add(nsends)
   165  	for i := 0; i < nsends; i++ {
   166  		go func() {
   167  			feed.Send(99)
   168  			wg.Done()
   169  		}()
   170  	}
   171  	// Subscribe the other channels.
   172  	for i, ch := range chans {
   173  		subs[i] = feed.Subscribe(ch)
   174  	}
   175  	// Unsubscribe them again.
   176  	for _, sub := range subs {
   177  		sub.Unsubscribe()
   178  	}
   179  	// Unblock the Sends.
   180  	bsub.Unsubscribe()
   181  	wg.Wait()
   182  }
   183  
   184  // Checks that unsubscribing a channel during Send works even if that
   185  // channel has already been sent on.
   186  func TestFeedOfUnsubscribeSentChan(t *testing.T) {
   187  	var (
   188  		feed FeedOf[int]
   189  		ch1  = make(chan int)
   190  		ch2  = make(chan int)
   191  		sub1 = feed.Subscribe(ch1)
   192  		sub2 = feed.Subscribe(ch2)
   193  		wg   sync.WaitGroup
   194  	)
   195  	defer sub2.Unsubscribe()
   196  
   197  	wg.Add(1)
   198  	go func() {
   199  		feed.Send(0)
   200  		wg.Done()
   201  	}()
   202  
   203  	// Wait for the value on ch1.
   204  	<-ch1
   205  	// Unsubscribe ch1, removing it from the send cases.
   206  	sub1.Unsubscribe()
   207  
   208  	// Receive ch2, finishing Send.
   209  	<-ch2
   210  	wg.Wait()
   211  
   212  	// Send again. This should send to ch2 only, so the wait group will unblock
   213  	// as soon as a value is received on ch2.
   214  	wg.Add(1)
   215  	go func() {
   216  		feed.Send(0)
   217  		wg.Done()
   218  	}()
   219  	<-ch2
   220  	wg.Wait()
   221  }
   222  
   223  func TestFeedOfUnsubscribeFromInbox(t *testing.T) {
   224  	var (
   225  		feed FeedOf[int]
   226  		ch1  = make(chan int)
   227  		ch2  = make(chan int)
   228  		sub1 = feed.Subscribe(ch1)
   229  		sub2 = feed.Subscribe(ch1)
   230  		sub3 = feed.Subscribe(ch2)
   231  	)
   232  	if len(feed.inbox) != 3 {
   233  		t.Errorf("inbox length != 3 after subscribe")
   234  	}
   235  	if len(feed.sendCases) != 1 {
   236  		t.Errorf("sendCases is non-empty after unsubscribe")
   237  	}
   238  
   239  	sub1.Unsubscribe()
   240  	sub2.Unsubscribe()
   241  	sub3.Unsubscribe()
   242  	if len(feed.inbox) != 0 {
   243  		t.Errorf("inbox is non-empty after unsubscribe")
   244  	}
   245  	if len(feed.sendCases) != 1 {
   246  		t.Errorf("sendCases is non-empty after unsubscribe")
   247  	}
   248  }
   249  
   250  func BenchmarkFeedOfSend1000(b *testing.B) {
   251  	var (
   252  		done  sync.WaitGroup
   253  		feed  FeedOf[int]
   254  		nsubs = 1000
   255  	)
   256  	subscriber := func(ch <-chan int) {
   257  		for i := 0; i < b.N; i++ {
   258  			<-ch
   259  		}
   260  		done.Done()
   261  	}
   262  	done.Add(nsubs)
   263  	for i := 0; i < nsubs; i++ {
   264  		ch := make(chan int, 200)
   265  		feed.Subscribe(ch)
   266  		go subscriber(ch)
   267  	}
   268  
   269  	// The actual benchmark.
   270  	b.ResetTimer()
   271  	for i := 0; i < b.N; i++ {
   272  		if feed.Send(i) != nsubs {
   273  			panic("wrong number of sends")
   274  		}
   275  	}
   276  
   277  	b.StopTimer()
   278  	done.Wait()
   279  }