package fn import ( "fmt" "slices" "testing" "testing/quick" "time" "github.com/stretchr/testify/require" ) func even(a int) bool { return a%2 == 0 } func odd(a int) bool { return a%2 != 0 } func TestAll(t *testing.T) { x := []int{0, 2, 4, 6, 8} require.True(t, All(even, x)) require.False(t, All(odd, x)) y := []int{1, 3, 5, 7, 9} require.False(t, All(even, y)) require.True(t, All(odd, y)) z := []int{0, 2, 4, 6, 9} require.False(t, All(even, z)) require.False(t, All(odd, z)) } func TestAny(t *testing.T) { x := []int{1, 3, 5, 7, 9} require.False(t, Any(even, x)) require.True(t, Any(odd, x)) y := []int{0, 3, 5, 7, 9} require.True(t, Any(even, y)) require.True(t, Any(odd, y)) z := []int{0, 2, 4, 6, 8} require.True(t, Any(even, z)) require.False(t, Any(odd, z)) } func TestMap(t *testing.T) { inc := func(i int) int { return i + 1 } x := []int{0, 2, 4, 6, 8} y := Map(inc, x) z := []int{1, 3, 5, 7, 9} require.True(t, slices.Equal(y, z)) } func TestFilter(t *testing.T) { x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} y := Filter(even, x) require.True(t, All(even, y)) z := Filter(odd, y) require.Zero(t, len(z)) } func TestFoldl(t *testing.T) { seed := []int{} stupid := func(s []int, a int) []int { return append(s, a) } x := []int{0, 1, 2, 3, 4} r := Foldl(stupid, seed, x) require.True(t, slices.Equal(x, r)) } func TestFoldr(t *testing.T) { seed := []int{} stupid := func(a int, s []int) []int { return append(s, a) } x := []int{0, 1, 2, 3, 4} z := Foldr(stupid, seed, x) slices.Reverse[[]int](x) require.True(t, slices.Equal(x, z)) } func TestFind(t *testing.T) { x := []int{10, 11, 12, 13, 14, 15} div3 := func(a int) bool { return a%3 == 0 } div8 := func(a int) bool { return a%8 == 0 } require.Equal(t, Find(div3, x), Some(12)) require.Equal(t, Find(div8, x), None[int]()) } func TestFlatten(t *testing.T) { x := [][]int{{0}, {1}, {2}} y := Flatten(x) require.True(t, slices.Equal(y, []int{0, 1, 2})) } func TestReplicate(t *testing.T) { require.True(t, slices.Equal([]int{1, 1, 1, 1, 1}, Replicate(5, 1))) } func TestSpan(t *testing.T) { x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} lt5 := func(a int) bool { return a < 5 } low, high := Span(lt5, x) require.True(t, slices.Equal(low, []int{0, 1, 2, 3, 4})) require.True(t, slices.Equal(high, []int{5, 6, 7, 8, 9})) } func TestSplitAt(t *testing.T) { x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fst, snd := SplitAt(5, x) require.True(t, slices.Equal(fst, []int{0, 1, 2, 3, 4})) require.True(t, slices.Equal(snd, []int{5, 6, 7, 8, 9})) } func TestZipWith(t *testing.T) { eq := func(a, b int) bool { return a == b } x := []int{0, 1, 2, 3, 4} y := Replicate(5, 1) z := ZipWith(eq, x, y) require.True(t, slices.Equal( z, []bool{false, true, false, false, false}, )) } // TestSum checks if the Sum function correctly calculates the sum of the // numbers in the slice. func TestSum(t *testing.T) { tests := []struct { name string items interface{} result interface{} }{ { name: "Sum of positive integers", items: []int{1, 2, 3}, result: 6, }, { name: "Sum of negative integers", items: []int{-1, -2, -3}, result: -6, }, { name: "Sum of float numbers", items: []float64{1.1, 2.2, 3.3}, result: 6.6, }, { name: "Sum of complex numbers", items: []complex128{ complex(1, 1), complex(2, 2), complex(3, 3), }, result: complex(6, 6), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { switch v := tt.items.(type) { case []int: require.Equal(t, tt.result, Sum(v)) case []float64: require.Equal(t, tt.result, Sum(v)) case []complex128: require.Equal(t, tt.result, Sum(v)) } }) } } // TestSliceToMap tests the SliceToMap function. func TestSliceToMap(t *testing.T) { tests := []struct { name string slice []int keyFunc func(int) int valueFunc func(int) string expected map[int]string }{ { name: "Integers to string map", slice: []int{1, 2, 3}, keyFunc: func(a int) int { return a }, valueFunc: func(a int) string { return fmt.Sprintf("Value%d", a) }, expected: map[int]string{ 1: "Value1", 2: "Value2", 3: "Value3", }, }, { name: "Duplicates in slice", slice: []int{1, 2, 2, 3}, keyFunc: func(a int) int { return a }, valueFunc: func(a int) string { return fmt.Sprintf("Value%d", a) }, expected: map[int]string{ 1: "Value1", 2: "Value2", 3: "Value3", }, }, { name: "Empty slice", slice: []int{}, keyFunc: func(a int) int { return a }, valueFunc: func(a int) string { return fmt.Sprintf("Value%d", a) }, expected: map[int]string{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require.Equal( t, tt.expected, SliceToMap(tt.slice, tt.keyFunc, tt.valueFunc), ) }) } } // TestHasDuplicates tests the HasDuplicates function. func TestHasDuplicates(t *testing.T) { // Define test cases. testCases := []struct { name string items []int want bool }{ { name: "All unique", items: []int{1, 2, 3, 4, 5}, want: false, }, { name: "Some duplicates", items: []int{1, 2, 2, 3, 4}, want: true, }, { name: "No items", items: []int{}, want: false, }, { name: "All duplicates", items: []int{1, 1, 1, 1}, want: true, }, } // Execute each test case. for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got := HasDuplicates(tc.items) require.Equal(t, tc.want, got) }) } } // TestPropForEachConcMapIsomorphism ensures the property that ForEachConc and // Map always yield the same results. func TestPropForEachConcMapIsomorphism(t *testing.T) { f := func(incSize int, s []int) bool { inc := func(i int) int { return i + incSize } mapped := Map(inc, s) conc := ForEachConc(inc, s) return slices.Equal(mapped, conc) } if err := quick.Check(f, nil); err != nil { t.Fatal(err) } } // TestPropForEachConcOutperformsMapWhenExpensive ensures the property that // ForEachConc will beat Map in a race in circumstances where the computation in // the argument closure is expensive. func TestPropForEachConcOutperformsMapWhenExpensive(t *testing.T) { f := func(incSize int, s []int) bool { if len(s) < 2 { // Intuitively we don't expect the extra overhead of // ForEachConc to justify itself for list sizes of 1 or // 0. return true } inc := func(i int) int { time.Sleep(time.Millisecond) return i + incSize } c := make(chan bool, 1) go func() { Map(inc, s) select { case c <- false: default: } }() go func() { ForEachConc(inc, s) select { case c <- true: default: } }() return <-c } if err := quick.Check(f, nil); err != nil { t.Fatal(err) } } func TestPropFindIdxFindIdentity(t *testing.T) { f := func(div, mod uint8, s []uint8) bool { if div == 0 || div == 1 { return true } pred := func(i uint8) bool { return i%div == mod } foundIdx := FindIdx(pred, s) // onlyVal :: Option[T2[A, B]] -> Option[B] onlyVal := MapOption(func(t2 T2[int, uint8]) uint8 { return t2.Second() }) valuesEqual := Find(pred, s) == onlyVal(foundIdx) idxGetsVal := ElimOption( foundIdx, func() bool { return true }, func(t2 T2[int, uint8]) bool { return s[t2.First()] == t2.Second() }) return valuesEqual && idxGetsVal } if err := quick.Check(f, nil); err != nil { t.Fatal(err) } }