github.com/lingyao2333/mo-zero@v1.4.1/core/fx/stream_test.go (about)

     1  package fx
     2  
     3  import (
     4  	"io"
     5  	"log"
     6  	"math/rand"
     7  	"reflect"
     8  	"runtime"
     9  	"sort"
    10  	"sync"
    11  	"sync/atomic"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/lingyao2333/mo-zero/core/stringx"
    16  	"github.com/stretchr/testify/assert"
    17  	"go.uber.org/goleak"
    18  )
    19  
    20  func TestBuffer(t *testing.T) {
    21  	runCheckedTest(t, func(t *testing.T) {
    22  		const N = 5
    23  		var count int32
    24  		var wait sync.WaitGroup
    25  		wait.Add(1)
    26  		From(func(source chan<- interface{}) {
    27  			ticker := time.NewTicker(10 * time.Millisecond)
    28  			defer ticker.Stop()
    29  
    30  			for i := 0; i < 2*N; i++ {
    31  				select {
    32  				case source <- i:
    33  					atomic.AddInt32(&count, 1)
    34  				case <-ticker.C:
    35  					wait.Done()
    36  					return
    37  				}
    38  			}
    39  		}).Buffer(N).ForAll(func(pipe <-chan interface{}) {
    40  			wait.Wait()
    41  			// why N+1, because take one more to wait for sending into the channel
    42  			assert.Equal(t, int32(N+1), atomic.LoadInt32(&count))
    43  		})
    44  	})
    45  }
    46  
    47  func TestBufferNegative(t *testing.T) {
    48  	runCheckedTest(t, func(t *testing.T) {
    49  		var result int
    50  		Just(1, 2, 3, 4).Buffer(-1).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
    51  			for item := range pipe {
    52  				result += item.(int)
    53  			}
    54  			return result, nil
    55  		})
    56  		assert.Equal(t, 10, result)
    57  	})
    58  }
    59  
    60  func TestCount(t *testing.T) {
    61  	runCheckedTest(t, func(t *testing.T) {
    62  		tests := []struct {
    63  			name     string
    64  			elements []interface{}
    65  		}{
    66  			{
    67  				name: "no elements with nil",
    68  			},
    69  			{
    70  				name:     "no elements",
    71  				elements: []interface{}{},
    72  			},
    73  			{
    74  				name:     "1 element",
    75  				elements: []interface{}{1},
    76  			},
    77  			{
    78  				name:     "multiple elements",
    79  				elements: []interface{}{1, 2, 3},
    80  			},
    81  		}
    82  
    83  		for _, test := range tests {
    84  			t.Run(test.name, func(t *testing.T) {
    85  				val := Just(test.elements...).Count()
    86  				assert.Equal(t, len(test.elements), val)
    87  			})
    88  		}
    89  	})
    90  }
    91  
    92  func TestDone(t *testing.T) {
    93  	runCheckedTest(t, func(t *testing.T) {
    94  		var count int32
    95  		Just(1, 2, 3).Walk(func(item interface{}, pipe chan<- interface{}) {
    96  			time.Sleep(time.Millisecond * 100)
    97  			atomic.AddInt32(&count, int32(item.(int)))
    98  		}).Done()
    99  		assert.Equal(t, int32(6), count)
   100  	})
   101  }
   102  
   103  func TestJust(t *testing.T) {
   104  	runCheckedTest(t, func(t *testing.T) {
   105  		var result int
   106  		Just(1, 2, 3, 4).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   107  			for item := range pipe {
   108  				result += item.(int)
   109  			}
   110  			return result, nil
   111  		})
   112  		assert.Equal(t, 10, result)
   113  	})
   114  }
   115  
   116  func TestDistinct(t *testing.T) {
   117  	runCheckedTest(t, func(t *testing.T) {
   118  		var result int
   119  		Just(4, 1, 3, 2, 3, 4).Distinct(func(item interface{}) interface{} {
   120  			return item
   121  		}).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   122  			for item := range pipe {
   123  				result += item.(int)
   124  			}
   125  			return result, nil
   126  		})
   127  		assert.Equal(t, 10, result)
   128  	})
   129  }
   130  
   131  func TestFilter(t *testing.T) {
   132  	runCheckedTest(t, func(t *testing.T) {
   133  		var result int
   134  		Just(1, 2, 3, 4).Filter(func(item interface{}) bool {
   135  			return item.(int)%2 == 0
   136  		}).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   137  			for item := range pipe {
   138  				result += item.(int)
   139  			}
   140  			return result, nil
   141  		})
   142  		assert.Equal(t, 6, result)
   143  	})
   144  }
   145  
   146  func TestFirst(t *testing.T) {
   147  	runCheckedTest(t, func(t *testing.T) {
   148  		assert.Nil(t, Just().First())
   149  		assert.Equal(t, "foo", Just("foo").First())
   150  		assert.Equal(t, "foo", Just("foo", "bar").First())
   151  	})
   152  }
   153  
   154  func TestForAll(t *testing.T) {
   155  	runCheckedTest(t, func(t *testing.T) {
   156  		var result int
   157  		Just(1, 2, 3, 4).Filter(func(item interface{}) bool {
   158  			return item.(int)%2 == 0
   159  		}).ForAll(func(pipe <-chan interface{}) {
   160  			for item := range pipe {
   161  				result += item.(int)
   162  			}
   163  		})
   164  		assert.Equal(t, 6, result)
   165  	})
   166  }
   167  
   168  func TestGroup(t *testing.T) {
   169  	runCheckedTest(t, func(t *testing.T) {
   170  		var groups [][]int
   171  		Just(10, 11, 20, 21).Group(func(item interface{}) interface{} {
   172  			v := item.(int)
   173  			return v / 10
   174  		}).ForEach(func(item interface{}) {
   175  			v := item.([]interface{})
   176  			var group []int
   177  			for _, each := range v {
   178  				group = append(group, each.(int))
   179  			}
   180  			groups = append(groups, group)
   181  		})
   182  
   183  		assert.Equal(t, 2, len(groups))
   184  		for _, group := range groups {
   185  			assert.Equal(t, 2, len(group))
   186  			assert.True(t, group[0]/10 == group[1]/10)
   187  		}
   188  	})
   189  }
   190  
   191  func TestHead(t *testing.T) {
   192  	runCheckedTest(t, func(t *testing.T) {
   193  		var result int
   194  		Just(1, 2, 3, 4).Head(2).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   195  			for item := range pipe {
   196  				result += item.(int)
   197  			}
   198  			return result, nil
   199  		})
   200  		assert.Equal(t, 3, result)
   201  	})
   202  }
   203  
   204  func TestHeadZero(t *testing.T) {
   205  	runCheckedTest(t, func(t *testing.T) {
   206  		assert.Panics(t, func() {
   207  			Just(1, 2, 3, 4).Head(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   208  				return nil, nil
   209  			})
   210  		})
   211  	})
   212  }
   213  
   214  func TestHeadMore(t *testing.T) {
   215  	runCheckedTest(t, func(t *testing.T) {
   216  		var result int
   217  		Just(1, 2, 3, 4).Head(6).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   218  			for item := range pipe {
   219  				result += item.(int)
   220  			}
   221  			return result, nil
   222  		})
   223  		assert.Equal(t, 10, result)
   224  	})
   225  }
   226  
   227  func TestLast(t *testing.T) {
   228  	runCheckedTest(t, func(t *testing.T) {
   229  		goroutines := runtime.NumGoroutine()
   230  		assert.Nil(t, Just().Last())
   231  		assert.Equal(t, "foo", Just("foo").Last())
   232  		assert.Equal(t, "bar", Just("foo", "bar").Last())
   233  		// let scheduler schedule first
   234  		runtime.Gosched()
   235  		assert.Equal(t, goroutines, runtime.NumGoroutine())
   236  	})
   237  }
   238  
   239  func TestMap(t *testing.T) {
   240  	runCheckedTest(t, func(t *testing.T) {
   241  		log.SetOutput(io.Discard)
   242  
   243  		tests := []struct {
   244  			mapper MapFunc
   245  			expect int
   246  		}{
   247  			{
   248  				mapper: func(item interface{}) interface{} {
   249  					v := item.(int)
   250  					return v * v
   251  				},
   252  				expect: 30,
   253  			},
   254  			{
   255  				mapper: func(item interface{}) interface{} {
   256  					v := item.(int)
   257  					if v%2 == 0 {
   258  						return 0
   259  					}
   260  					return v * v
   261  				},
   262  				expect: 10,
   263  			},
   264  			{
   265  				mapper: func(item interface{}) interface{} {
   266  					v := item.(int)
   267  					if v%2 == 0 {
   268  						panic(v)
   269  					}
   270  					return v * v
   271  				},
   272  				expect: 10,
   273  			},
   274  		}
   275  
   276  		// Map(...) works even WithWorkers(0)
   277  		for i, test := range tests {
   278  			t.Run(stringx.Rand(), func(t *testing.T) {
   279  				var result int
   280  				var workers int
   281  				if i%2 == 0 {
   282  					workers = 0
   283  				} else {
   284  					workers = runtime.NumCPU()
   285  				}
   286  				From(func(source chan<- interface{}) {
   287  					for i := 1; i < 5; i++ {
   288  						source <- i
   289  					}
   290  				}).Map(test.mapper, WithWorkers(workers)).Reduce(
   291  					func(pipe <-chan interface{}) (interface{}, error) {
   292  						for item := range pipe {
   293  							result += item.(int)
   294  						}
   295  						return result, nil
   296  					})
   297  
   298  				assert.Equal(t, test.expect, result)
   299  			})
   300  		}
   301  	})
   302  }
   303  
   304  func TestMerge(t *testing.T) {
   305  	runCheckedTest(t, func(t *testing.T) {
   306  		Just(1, 2, 3, 4).Merge().ForEach(func(item interface{}) {
   307  			assert.ElementsMatch(t, []interface{}{1, 2, 3, 4}, item.([]interface{}))
   308  		})
   309  	})
   310  }
   311  
   312  func TestParallelJust(t *testing.T) {
   313  	runCheckedTest(t, func(t *testing.T) {
   314  		var count int32
   315  		Just(1, 2, 3).Parallel(func(item interface{}) {
   316  			time.Sleep(time.Millisecond * 100)
   317  			atomic.AddInt32(&count, int32(item.(int)))
   318  		}, UnlimitedWorkers())
   319  		assert.Equal(t, int32(6), count)
   320  	})
   321  }
   322  
   323  func TestReverse(t *testing.T) {
   324  	runCheckedTest(t, func(t *testing.T) {
   325  		Just(1, 2, 3, 4).Reverse().Merge().ForEach(func(item interface{}) {
   326  			assert.ElementsMatch(t, []interface{}{4, 3, 2, 1}, item.([]interface{}))
   327  		})
   328  	})
   329  }
   330  
   331  func TestSort(t *testing.T) {
   332  	runCheckedTest(t, func(t *testing.T) {
   333  		var prev int
   334  		Just(5, 3, 7, 1, 9, 6, 4, 8, 2).Sort(func(a, b interface{}) bool {
   335  			return a.(int) < b.(int)
   336  		}).ForEach(func(item interface{}) {
   337  			next := item.(int)
   338  			assert.True(t, prev < next)
   339  			prev = next
   340  		})
   341  	})
   342  }
   343  
   344  func TestSplit(t *testing.T) {
   345  	runCheckedTest(t, func(t *testing.T) {
   346  		assert.Panics(t, func() {
   347  			Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(0).Done()
   348  		})
   349  		var chunks [][]interface{}
   350  		Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(4).ForEach(func(item interface{}) {
   351  			chunk := item.([]interface{})
   352  			chunks = append(chunks, chunk)
   353  		})
   354  		assert.EqualValues(t, [][]interface{}{
   355  			{1, 2, 3, 4},
   356  			{5, 6, 7, 8},
   357  			{9, 10},
   358  		}, chunks)
   359  	})
   360  }
   361  
   362  func TestTail(t *testing.T) {
   363  	runCheckedTest(t, func(t *testing.T) {
   364  		var result int
   365  		Just(1, 2, 3, 4).Tail(2).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   366  			for item := range pipe {
   367  				result += item.(int)
   368  			}
   369  			return result, nil
   370  		})
   371  		assert.Equal(t, 7, result)
   372  	})
   373  }
   374  
   375  func TestTailZero(t *testing.T) {
   376  	runCheckedTest(t, func(t *testing.T) {
   377  		assert.Panics(t, func() {
   378  			Just(1, 2, 3, 4).Tail(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
   379  				return nil, nil
   380  			})
   381  		})
   382  	})
   383  }
   384  
   385  func TestWalk(t *testing.T) {
   386  	runCheckedTest(t, func(t *testing.T) {
   387  		var result int
   388  		Just(1, 2, 3, 4, 5).Walk(func(item interface{}, pipe chan<- interface{}) {
   389  			if item.(int)%2 != 0 {
   390  				pipe <- item
   391  			}
   392  		}, UnlimitedWorkers()).ForEach(func(item interface{}) {
   393  			result += item.(int)
   394  		})
   395  		assert.Equal(t, 9, result)
   396  	})
   397  }
   398  
   399  func TestStream_AnyMach(t *testing.T) {
   400  	runCheckedTest(t, func(t *testing.T) {
   401  		assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
   402  			return item.(int) == 4
   403  		}))
   404  		assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
   405  			return item.(int) == 0
   406  		}))
   407  		assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
   408  			return item.(int) == 2
   409  		}))
   410  		assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
   411  			return item.(int) == 2
   412  		}))
   413  	})
   414  }
   415  
   416  func TestStream_AllMach(t *testing.T) {
   417  	runCheckedTest(t, func(t *testing.T) {
   418  		assetEqual(
   419  			t, true, Just(1, 2, 3).AllMach(func(item interface{}) bool {
   420  				return true
   421  			}),
   422  		)
   423  		assetEqual(
   424  			t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool {
   425  				return false
   426  			}),
   427  		)
   428  		assetEqual(
   429  			t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool {
   430  				return item.(int) == 1
   431  			}),
   432  		)
   433  	})
   434  }
   435  
   436  func TestStream_NoneMatch(t *testing.T) {
   437  	runCheckedTest(t, func(t *testing.T) {
   438  		assetEqual(
   439  			t, true, Just(1, 2, 3).NoneMatch(func(item interface{}) bool {
   440  				return false
   441  			}),
   442  		)
   443  		assetEqual(
   444  			t, false, Just(1, 2, 3).NoneMatch(func(item interface{}) bool {
   445  				return true
   446  			}),
   447  		)
   448  		assetEqual(
   449  			t, true, Just(1, 2, 3).NoneMatch(func(item interface{}) bool {
   450  				return item.(int) == 4
   451  			}),
   452  		)
   453  	})
   454  }
   455  
   456  func TestConcat(t *testing.T) {
   457  	runCheckedTest(t, func(t *testing.T) {
   458  		a1 := []interface{}{1, 2, 3}
   459  		a2 := []interface{}{4, 5, 6}
   460  		s1 := Just(a1...)
   461  		s2 := Just(a2...)
   462  		stream := Concat(s1, s2)
   463  		var items []interface{}
   464  		for item := range stream.source {
   465  			items = append(items, item)
   466  		}
   467  		sort.Slice(items, func(i, j int) bool {
   468  			return items[i].(int) < items[j].(int)
   469  		})
   470  		ints := make([]interface{}, 0)
   471  		ints = append(ints, a1...)
   472  		ints = append(ints, a2...)
   473  		assetEqual(t, ints, items)
   474  	})
   475  }
   476  
   477  func TestStream_Skip(t *testing.T) {
   478  	runCheckedTest(t, func(t *testing.T) {
   479  		assetEqual(t, 3, Just(1, 2, 3, 4).Skip(1).Count())
   480  		assetEqual(t, 1, Just(1, 2, 3, 4).Skip(3).Count())
   481  		assetEqual(t, 4, Just(1, 2, 3, 4).Skip(0).Count())
   482  		equal(t, Just(1, 2, 3, 4).Skip(3), []interface{}{4})
   483  		assert.Panics(t, func() {
   484  			Just(1, 2, 3, 4).Skip(-1)
   485  		})
   486  	})
   487  }
   488  
   489  func TestStream_Concat(t *testing.T) {
   490  	runCheckedTest(t, func(t *testing.T) {
   491  		stream := Just(1).Concat(Just(2), Just(3))
   492  		var items []interface{}
   493  		for item := range stream.source {
   494  			items = append(items, item)
   495  		}
   496  		sort.Slice(items, func(i, j int) bool {
   497  			return items[i].(int) < items[j].(int)
   498  		})
   499  		assetEqual(t, []interface{}{1, 2, 3}, items)
   500  
   501  		just := Just(1)
   502  		equal(t, just.Concat(just), []interface{}{1})
   503  	})
   504  }
   505  
   506  func BenchmarkParallelMapReduce(b *testing.B) {
   507  	b.ReportAllocs()
   508  
   509  	mapper := func(v interface{}) interface{} {
   510  		return v.(int64) * v.(int64)
   511  	}
   512  	reducer := func(input <-chan interface{}) (interface{}, error) {
   513  		var result int64
   514  		for v := range input {
   515  			result += v.(int64)
   516  		}
   517  		return result, nil
   518  	}
   519  	b.ResetTimer()
   520  	From(func(input chan<- interface{}) {
   521  		b.RunParallel(func(pb *testing.PB) {
   522  			for pb.Next() {
   523  				input <- int64(rand.Int())
   524  			}
   525  		})
   526  	}).Map(mapper).Reduce(reducer)
   527  }
   528  
   529  func BenchmarkMapReduce(b *testing.B) {
   530  	b.ReportAllocs()
   531  
   532  	mapper := func(v interface{}) interface{} {
   533  		return v.(int64) * v.(int64)
   534  	}
   535  	reducer := func(input <-chan interface{}) (interface{}, error) {
   536  		var result int64
   537  		for v := range input {
   538  			result += v.(int64)
   539  		}
   540  		return result, nil
   541  	}
   542  	b.ResetTimer()
   543  	From(func(input chan<- interface{}) {
   544  		for i := 0; i < b.N; i++ {
   545  			input <- int64(rand.Int())
   546  		}
   547  	}).Map(mapper).Reduce(reducer)
   548  }
   549  
   550  func assetEqual(t *testing.T, except, data interface{}) {
   551  	if !reflect.DeepEqual(except, data) {
   552  		t.Errorf(" %v, want %v", data, except)
   553  	}
   554  }
   555  
   556  func equal(t *testing.T, stream Stream, data []interface{}) {
   557  	items := make([]interface{}, 0)
   558  	for item := range stream.source {
   559  		items = append(items, item)
   560  	}
   561  	if !reflect.DeepEqual(items, data) {
   562  		t.Errorf(" %v, want %v", items, data)
   563  	}
   564  }
   565  
   566  func runCheckedTest(t *testing.T, fn func(t *testing.T)) {
   567  	defer goleak.VerifyNone(t)
   568  	fn(t)
   569  }