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 }