github.com/koko1123/flow-go-1@v0.29.6/module/util/util_test.go (about)

     1  package util_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	realmodule "github.com/koko1123/flow-go-1/module"
    13  	module "github.com/koko1123/flow-go-1/module/mock"
    14  	"github.com/koko1123/flow-go-1/module/util"
    15  	"github.com/koko1123/flow-go-1/utils/unittest"
    16  )
    17  
    18  // TestAllReady tests that AllReady closes its returned Ready channel only once
    19  // all input ReadyDone instances close their Ready channel.
    20  func TestAllReady(t *testing.T) {
    21  	cases := []int{0, 1, 100}
    22  	for _, n := range cases {
    23  		t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) {
    24  			testAllReady(n, t)
    25  		})
    26  	}
    27  }
    28  
    29  // TestAllDone tests that AllDone closes its returned Done channel only once
    30  // all input ReadyDone instances close their Done channel.
    31  func TestAllDone(t *testing.T) {
    32  	cases := []int{0, 1, 100}
    33  	for _, n := range cases {
    34  		t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) {
    35  			testAllDone(n, t)
    36  		})
    37  	}
    38  }
    39  
    40  func testAllDone(n int, t *testing.T) {
    41  
    42  	components := make([]realmodule.ReadyDoneAware, n)
    43  	for i := 0; i < n; i++ {
    44  		components[i] = new(module.ReadyDoneAware)
    45  		unittest.ReadyDoneify(components[i])
    46  	}
    47  
    48  	unittest.AssertClosesBefore(t, util.AllReady(components...), time.Second)
    49  
    50  	for _, component := range components {
    51  		mock := component.(*module.ReadyDoneAware)
    52  		mock.AssertCalled(t, "Ready")
    53  		mock.AssertNotCalled(t, "Done")
    54  	}
    55  }
    56  
    57  func testAllReady(n int, t *testing.T) {
    58  
    59  	components := make([]realmodule.ReadyDoneAware, n)
    60  	for i := 0; i < n; i++ {
    61  		components[i] = new(module.ReadyDoneAware)
    62  		unittest.ReadyDoneify(components[i])
    63  	}
    64  
    65  	unittest.AssertClosesBefore(t, util.AllDone(components...), time.Second)
    66  
    67  	for _, component := range components {
    68  		mock := component.(*module.ReadyDoneAware)
    69  		mock.AssertCalled(t, "Done")
    70  		mock.AssertNotCalled(t, "Ready")
    71  	}
    72  }
    73  
    74  func TestMergeChannels(t *testing.T) {
    75  	t.Run("empty slice", func(t *testing.T) {
    76  		t.Parallel()
    77  		channels := make([]<-chan int, 0)
    78  		merged := util.MergeChannels(channels).(<-chan int)
    79  		_, ok := <-merged
    80  		assert.False(t, ok)
    81  	})
    82  	t.Run("empty array", func(t *testing.T) {
    83  		t.Parallel()
    84  		channels := []<-chan int{}
    85  		merged := util.MergeChannels(channels).(<-chan int)
    86  		_, ok := <-merged
    87  		assert.False(t, ok)
    88  	})
    89  	t.Run("nil slice", func(t *testing.T) {
    90  		t.Parallel()
    91  		var channels []<-chan int
    92  		merged := util.MergeChannels(channels).(<-chan int)
    93  		_, ok := <-merged
    94  		assert.False(t, ok)
    95  	})
    96  	t.Run("nil", func(t *testing.T) {
    97  		t.Parallel()
    98  		assert.Panics(t, func() {
    99  			util.MergeChannels(nil)
   100  		})
   101  	})
   102  	t.Run("map", func(t *testing.T) {
   103  		t.Parallel()
   104  		channels := make(map[string]<-chan int)
   105  		assert.Panics(t, func() {
   106  			util.MergeChannels(channels)
   107  		})
   108  	})
   109  	t.Run("string", func(t *testing.T) {
   110  		t.Parallel()
   111  		channels := "abcde"
   112  		assert.Panics(t, func() {
   113  			util.MergeChannels(channels)
   114  		})
   115  	})
   116  	t.Run("array of non-channel", func(t *testing.T) {
   117  		t.Parallel()
   118  		channels := []int{1, 2, 3}
   119  		assert.Panics(t, func() {
   120  			util.MergeChannels(channels)
   121  		})
   122  	})
   123  	t.Run("send channel", func(t *testing.T) {
   124  		t.Parallel()
   125  		channels := []chan<- int{make(chan int), make(chan int)}
   126  		assert.Panics(t, func() {
   127  			util.MergeChannels(channels)
   128  		})
   129  	})
   130  	t.Run("cast returned channel to send channel", func(t *testing.T) {
   131  		t.Parallel()
   132  		channels := []<-chan int{make(<-chan int), make(<-chan int)}
   133  		_, ok := util.MergeChannels(channels).(chan int)
   134  		assert.False(t, ok)
   135  	})
   136  	t.Run("happy path", func(t *testing.T) {
   137  		t.Parallel()
   138  		channels := []chan int{make(chan int), make(chan int), make(chan int)}
   139  		merged := util.MergeChannels(channels).(<-chan int)
   140  		for i, ch := range channels {
   141  			i := i
   142  			ch := ch
   143  			go func() {
   144  				ch <- i
   145  				close(ch)
   146  			}()
   147  		}
   148  		var elements []int
   149  		for i := range merged {
   150  			elements = append(elements, i)
   151  		}
   152  		assert.ElementsMatch(t, elements, []int{0, 1, 2})
   153  	})
   154  }
   155  
   156  func TestWaitClosed(t *testing.T) {
   157  	ctx, cancel := context.WithCancel(context.Background())
   158  	defer cancel()
   159  
   160  	t.Run("channel closed returns nil", func(t *testing.T) {
   161  		finished := make(chan struct{})
   162  		ch := make(chan struct{})
   163  		go func() {
   164  			err := util.WaitClosed(ctx, ch)
   165  			assert.NoError(t, err)
   166  			close(finished)
   167  		}()
   168  		close(ch)
   169  
   170  		select {
   171  		case <-finished:
   172  		case <-time.After(100 * time.Millisecond):
   173  			t.Error("timed out")
   174  		}
   175  	})
   176  
   177  	t.Run("context cancelled returns error", func(t *testing.T) {
   178  		testCtx, testCancel := context.WithCancel(ctx)
   179  		finished := make(chan struct{})
   180  		ch := make(chan struct{})
   181  		go func() {
   182  			err := util.WaitClosed(testCtx, ch)
   183  			assert.ErrorIs(t, err, context.Canceled)
   184  			close(finished)
   185  		}()
   186  		testCancel()
   187  
   188  		select {
   189  		case <-finished:
   190  		case <-time.After(100 * time.Millisecond):
   191  			t.Error("timed out")
   192  		}
   193  	})
   194  
   195  	t.Run("both conditions triggered returns nil", func(t *testing.T) {
   196  		// both conditions are met when WaitClosed is called. Since one is randomly selected,
   197  		// there is a 99.9% probability that each condition will be picked first at least once
   198  		// during this test.
   199  		for i := 0; i < 10; i++ {
   200  			testCtx, testCancel := context.WithCancel(ctx)
   201  			finished := make(chan struct{})
   202  			ch := make(chan struct{})
   203  			close(ch)
   204  			testCancel()
   205  
   206  			go func() {
   207  				err := util.WaitClosed(testCtx, ch)
   208  				assert.NoError(t, err)
   209  				close(finished)
   210  			}()
   211  
   212  			select {
   213  			case <-finished:
   214  			case <-time.After(100 * time.Millisecond):
   215  				t.Error("timed out")
   216  			}
   217  		}
   218  	})
   219  }
   220  
   221  func TestCheckClosed(t *testing.T) {
   222  	done := make(chan struct{})
   223  	assert.False(t, util.CheckClosed(done))
   224  	close(done)
   225  	assert.True(t, util.CheckClosed(done))
   226  }
   227  
   228  func TestWaitError(t *testing.T) {
   229  	ctx, cancel := context.WithCancel(context.Background())
   230  	defer cancel()
   231  
   232  	testErr := errors.New("test error channel")
   233  	t.Run("error received returns error", func(t *testing.T) {
   234  		finished := make(chan struct{})
   235  		ch := make(chan error)
   236  
   237  		go func() {
   238  			err := util.WaitError(ch, ctx.Done())
   239  			assert.ErrorIs(t, err, testErr)
   240  			close(finished)
   241  		}()
   242  		ch <- testErr
   243  
   244  		select {
   245  		case <-finished:
   246  		case <-time.After(100 * time.Millisecond):
   247  			t.Error("timed out")
   248  		}
   249  	})
   250  
   251  	t.Run("context cancelled returns error", func(t *testing.T) {
   252  		testCtx, testCancel := context.WithCancel(ctx)
   253  		finished := make(chan struct{})
   254  		ch := make(chan error)
   255  		go func() {
   256  			err := util.WaitError(ch, testCtx.Done())
   257  			assert.NoError(t, err)
   258  			close(finished)
   259  		}()
   260  		testCancel()
   261  
   262  		select {
   263  		case <-finished:
   264  		case <-time.After(100 * time.Millisecond):
   265  			t.Error("timed out")
   266  		}
   267  	})
   268  
   269  	t.Run("both conditions triggered returns error", func(t *testing.T) {
   270  		// both conditions are met when WaitError is called. Since one is randomly selected,
   271  		// there is a 99.9% probability that each condition will be picked first at least once
   272  		// during this test.
   273  		for i := 0; i < 10; i++ {
   274  			finished := make(chan struct{})
   275  			ch := make(chan error, 1) // buffered so we can add before starting
   276  			done := make(chan struct{})
   277  
   278  			ch <- testErr
   279  			close(done)
   280  
   281  			go func() {
   282  				err := util.WaitError(ch, done)
   283  				assert.ErrorIs(t, err, testErr)
   284  				close(finished)
   285  			}()
   286  
   287  			select {
   288  			case <-finished:
   289  			case <-time.After(100 * time.Millisecond):
   290  				t.Error("timed out")
   291  			}
   292  		}
   293  	})
   294  }
   295  
   296  // TestDetypeSlice tests that DetypeSlice returns a slice which is identical
   297  // besides the element type information.
   298  func TestDetypeSlice(t *testing.T) {
   299  	slice := []int{1, 2, 5, 3, 53, 1234}
   300  	detyped := util.DetypeSlice(slice)
   301  	assert.Equal(t, len(slice), len(detyped))
   302  	for i := range slice {
   303  		assert.Equal(t, slice[i], detyped[i].(int))
   304  	}
   305  }