diff --git a/coverage_badge.png b/coverage_badge.png index c55ef78..1e2e77a 100644 Binary files a/coverage_badge.png and b/coverage_badge.png differ diff --git a/internal/internalpipe/constructor_test.go b/internal/internalpipe/constructor_test.go index bac54e3..5982688 100644 --- a/internal/internalpipe/constructor_test.go +++ b/internal/internalpipe/constructor_test.go @@ -48,6 +48,7 @@ func Test_Cycle(t *testing.T) { } t.Run("happy", func(t *testing.T) { + t.Parallel() p := Cycle([]string{"a", "b", "c", "d"}) require.Equal( t, @@ -57,6 +58,7 @@ func Test_Cycle(t *testing.T) { }) t.Run("empty res", func(t *testing.T) { + t.Parallel() p := Cycle([]string{"a", "b", "c", "d"}) require.Equal( t, @@ -66,6 +68,7 @@ func Test_Cycle(t *testing.T) { }) t.Run("empty cycle", func(t *testing.T) { + t.Parallel() p := Cycle([]string{}) require.Equal( t, @@ -82,6 +85,7 @@ func Test_Range(t *testing.T) { t.Parallel() t.Run("happy", func(t *testing.T) { + t.Parallel() p := Range(0, 10, 1) require.Equal(t, p.Len, 10) require.Equal(t, p.ValLim, notSet) @@ -92,6 +96,7 @@ func Test_Range(t *testing.T) { } }) t.Run("single step owerflow", func(t *testing.T) { + t.Parallel() p := Range(1, 10, 100) require.Equal(t, 1, p.Len) require.Equal(t, notSet, p.ValLim) @@ -100,6 +105,7 @@ func Test_Range(t *testing.T) { require.Equal(t, 1, res[0]) }) t.Run("finish less than start", func(t *testing.T) { + t.Parallel() p := Range(100, 10, 100) require.Equal(t, 0, p.Len) require.Equal(t, notSet, p.ValLim) diff --git a/internal/internalpipe/promices.go b/internal/internalpipe/promices.go index c481aa7..b1ab909 100644 --- a/internal/internalpipe/promices.go +++ b/internal/internalpipe/promices.go @@ -1,9 +1,10 @@ package internalpipe func (p Pipe[T]) Promices() []func() (T, bool) { - proms := make([]func() (T, bool), p.limit()) + limit := p.limit() + proms := make([]func() (T, bool), limit) var empty T - for i := 0; i < p.limit(); i++ { + for i := 0; i < limit; i++ { cpi := i proms[i] = func() (T, bool) { obj, skipped := p.Fn(cpi) diff --git a/internal/internalpipe/promices_test.go b/internal/internalpipe/promices_test.go index 06937a2..5095431 100644 --- a/internal/internalpipe/promices_test.go +++ b/internal/internalpipe/promices_test.go @@ -10,13 +10,17 @@ func TestPromices(t *testing.T) { t.Parallel() a := Func(func(i int) (int, bool) { - return i, true - }).Gen(100) + return i, i != 100 + }).Take(100) proms := a.Promices() for i := 0; i < 100; i++ { res, ok := proms[i]() require.True(t, ok) + if i == 100 { + require.Equal(t, i+1, res) + continue + } require.Equal(t, i, res) } } diff --git a/internal/internalpipe/snag_test.go b/internal/internalpipe/snag_test.go new file mode 100644 index 0000000..6269668 --- /dev/null +++ b/internal/internalpipe/snag_test.go @@ -0,0 +1,67 @@ +package internalpipe + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPipe_Snag(t *testing.T) { + t.Parallel() + + t.Run("empty yeti", func(t *testing.T) { + t.Parallel() + + handlerCalled := false + _ = Func(func(i int) (int, bool) { + return i, true + }).Take(1000).Snag(func(_ error) { handlerCalled = true }).Do() + require.False(t, handlerCalled) + }) + + t.Run("happy yeti snag", func(t *testing.T) { + t.Parallel() + + yeti := NewYeti() + handlerCalled := false + _ = Func(func(i int) (int, bool) { + if i == 10 { + yeti.Yeet(fmt.Errorf("failed on %d", i)) + return i, false + } + return i, true + }).Yeti(yeti).Snag(func(_ error) { handlerCalled = true }).Take(1000).Do() + require.True(t, handlerCalled) + }) + + t.Run("happy double yeti snag", func(t *testing.T) { + t.Parallel() + + yeti, yeti2 := NewYeti(), NewYeti() + handlerCalled := false + _ = Func(func(i int) (int, bool) { + if i == 10 { + yeti.Yeet(fmt.Errorf("failed on %d", i)) + return i, false + } + return i, true + }).Yeti(yeti2).Yeti(yeti).Snag(func(_ error) { handlerCalled = true }).Take(1000).Do() + require.True(t, handlerCalled) + }) + + t.Run("yeti not set no snag handled", func(t *testing.T) { + t.Parallel() + + yeti := NewYeti() + handlerCalled := false + _ = Func(func(i int) (int, bool) { + if i == 10 { + yeti.Yeet(fmt.Errorf("failed on %d", i)) + return i, false + } + return i, true + }).Snag(func(_ error) { handlerCalled = true }).Take(1000).Do() + require.False(t, handlerCalled) + }) +} diff --git a/pkg/pipies/filters.go b/pkg/pipies/filters.go index 42944ee..5aa0022 100644 --- a/pkg/pipies/filters.go +++ b/pkg/pipies/filters.go @@ -57,7 +57,7 @@ func LessThan[T constraints.Ordered](x T) pipe.Predicate[T] { // The result function is rather slow since it takes a lock on each element. // You should use Pipe.Distinct() to get better performance. func Distinct[T any, C comparable](getKey func(x *T) C) pipe.Predicate[T] { - var set map[C]struct{} + set := make(map[C]struct{}) var mx sync.Mutex return func(y *T) bool { diff --git a/pkg/pipies/pipies_test.go b/pkg/pipies/pipies_test.go index dc48d0e..541f68a 100644 --- a/pkg/pipies/pipies_test.go +++ b/pkg/pipies/pipies_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/koss-null/funcfrog/internal/primitive/pointer" + "github.com/koss-null/funcfrog/pkg/pipe" ) type testT string @@ -14,6 +15,7 @@ func Test_Predicates(t *testing.T) { t.Parallel() t.Run("NotNil", func(t *testing.T) { + t.Parallel() require.True(t, NotNil(pointer.To(1))) require.False(t, NotNil[int](nil)) require.False(t, NotNil[int](nil)) @@ -23,9 +25,10 @@ func Test_Predicates(t *testing.T) { empty = nil require.False(t, NotNil(empty)) var e any = empty - require.True(t, NotNil[any](&e)) + require.True(t, NotNil(&e)) }) t.Run("IsNil", func(t *testing.T) { + t.Parallel() require.False(t, IsNil(pointer.To(1))) require.True(t, IsNil[int](nil)) require.True(t, IsNil[int](nil)) @@ -38,6 +41,7 @@ func Test_Predicates(t *testing.T) { require.False(t, IsNil[any](&e)) }) t.Run("NotZero", func(t *testing.T) { + t.Parallel() require.True(t, NotZero(pointer.To(1))) require.False(t, NotZero(pointer.To(0))) require.False(t, NotZero(pointer.To[float32](0.0))) @@ -50,6 +54,7 @@ func Test_PredicateBuilders(t *testing.T) { t.Parallel() t.Run("Eq", func(t *testing.T) { + t.Parallel() eq5 := Eq(5) require.True(t, eq5(pointer.To(5))) require.False(t, eq5(pointer.To(4))) @@ -59,6 +64,7 @@ func Test_PredicateBuilders(t *testing.T) { }) t.Run("NotEq", func(t *testing.T) { + t.Parallel() neq5 := NotEq(5) require.False(t, neq5(pointer.To(5))) require.True(t, neq5(pointer.To(4))) @@ -68,6 +74,7 @@ func Test_PredicateBuilders(t *testing.T) { }) t.Run("LessThan", func(t *testing.T) { + t.Parallel() lt5 := LessThan(5) require.True(t, lt5(pointer.To(4))) require.False(t, lt5(pointer.To(5))) @@ -80,6 +87,7 @@ func Test_PredicateBuilders(t *testing.T) { } func Test_Comparator(t *testing.T) { + t.Parallel() require.True(t, Less(pointer.To(4), pointer.To(5))) require.False(t, Less(pointer.To(5), pointer.To(5))) require.False(t, Less(pointer.To(6), pointer.To(5))) @@ -89,11 +97,13 @@ func Test_Comparator(t *testing.T) { } func Test_Accum(t *testing.T) { + t.Parallel() require.Equal(t, Sum(pointer.To(10), pointer.To(20)), 30) require.Equal(t, Sum(pointer.To(10.0), pointer.To(20.0)), 30.0) } func Test_Not(t *testing.T) { + t.Parallel() require.Equal(t, Not(func(a bool) bool { return a })(true), false) require.Equal(t, Nott(func(a, b bool) bool { return a && b })(true, true), false) require.Equal(t, Nottt(func(a, b, c bool) bool { return a && b && c })(true, true, true), false) @@ -102,3 +112,18 @@ func Test_Not(t *testing.T) { require.Equal(t, Nott(func(a, b bool) bool { return a && b })(false, false), true) require.Equal(t, Nottt(func(a, b, c bool) bool { return a && b && c })(false, false, false), true) } + +func Test_Distinct(t *testing.T) { + t.Parallel() + + getKey := func(x *int) int { return *x } + predicate := Distinct(getKey) + filtered := pipe.Slice([]int{1, 2, 3, 3, 2, 2, 1, 5, 2, 3, 1}).Filter(predicate).Do() + + found := make(map[int]struct{}) + for _, f := range filtered { + _, ok := found[f] + require.False(t, ok, "Distinct element is duplicated") + found[f] = struct{}{} + } +}