Files
lo/slice_test.go
2025-09-25 04:15:30 +02:00

1377 lines
36 KiB
Go

package lo
import (
"fmt"
"math"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFilter(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := Filter([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
is.Equal([]int{2, 4}, r1)
r2 := Filter([]string{"", "foo", "", "bar", ""}, func(x string, _ int) bool {
return len(x) > 0
})
is.Equal([]string{"foo", "bar"}, r2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Filter(allStrings, func(x string, _ int) bool {
return len(x) > 0
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Map([]int{1, 2, 3, 4}, func(x int, _ int) string {
return "Hello"
})
result2 := Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string {
return strconv.FormatInt(x, 10)
})
is.Equal([]string{"Hello", "Hello", "Hello", "Hello"}, result1)
is.Equal([]string{"1", "2", "3", "4"}, result2)
}
func TestUniqMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
type User struct {
Name string
age int
}
users := []User{{Name: "Alice", age: 20}, {Name: "Alex", age: 21}, {Name: "Alex", age: 22}}
result := UniqMap(users, func(item User, index int) string {
return item.Name
})
is.Equal([]string{"Alice", "Alex"}, result)
}
func TestFilterMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := FilterMap([]int64{1, 2, 3, 4}, func(x int64, _ int) (string, bool) {
if x%2 == 0 {
return strconv.FormatInt(x, 10), true
}
return "", false
})
r2 := FilterMap([]string{"cpu", "gpu", "mouse", "keyboard"}, func(x string, _ int) (string, bool) {
if strings.HasSuffix(x, "pu") {
return "xpu", true
}
return "", false
})
is.Equal([]string{"2", "4"}, r1)
is.Equal([]string{"xpu", "xpu"}, r2)
}
func TestFlatMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := FlatMap([]int{0, 1, 2, 3, 4}, func(x int, _ int) []string {
return []string{"Hello"}
})
result2 := FlatMap([]int64{0, 1, 2, 3, 4}, func(x int64, _ int) []string {
result := make([]string, 0, x)
for i := int64(0); i < x; i++ {
result = append(result, strconv.FormatInt(x, 10))
}
return result
})
is.Equal([]string{"Hello", "Hello", "Hello", "Hello", "Hello"}, result1)
is.Equal([]string{"1", "2", "2", "3", "3", "3", "4", "4", "4", "4"}, result2)
}
func TestTimes(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Times(3, func(i int) string {
return strconv.FormatInt(int64(i), 10)
})
is.Equal([]string{"0", "1", "2"}, result1)
}
func TestReduce(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
return agg + item
}, 0)
result2 := Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
return agg + item
}, 10)
is.Equal(10, result1)
is.Equal(20, result2)
}
func TestReduceRight(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item []int, _ int) []int {
return append(agg, item...)
}, []int{})
is.Equal([]int{4, 5, 2, 3, 0, 1}, result1)
type collection []int
result3 := ReduceRight(collection{1, 2, 3, 4}, func(agg int, item int, _ int) int {
return agg + item
}, 10)
is.Equal(20, result3)
}
func TestForEach(t *testing.T) {
t.Parallel()
is := assert.New(t)
// check of callback is called for every element and in proper order
callParams1 := []string{}
callParams2 := []int{}
ForEach([]string{"a", "b", "c"}, func(item string, i int) {
callParams1 = append(callParams1, item)
callParams2 = append(callParams2, i)
})
is.Equal([]string{"a", "b", "c"}, callParams1)
is.Equal([]int{0, 1, 2}, callParams2)
is.IsIncreasing(callParams2)
}
func TestForEachWhile(t *testing.T) {
t.Parallel()
is := assert.New(t)
// check of callback is called for every element and in proper order
var callParams1 []string
var callParams2 []int
ForEachWhile([]string{"a", "b", "c"}, func(item string, i int) bool {
if item == "c" {
return false
}
callParams1 = append(callParams1, item)
callParams2 = append(callParams2, i)
return true
})
is.Equal([]string{"a", "b"}, callParams1)
is.Equal([]int{0, 1}, callParams2)
is.IsIncreasing(callParams2)
}
func TestUniq(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Uniq([]int{1, 2, 2, 1})
is.Equal([]int{1, 2}, result1)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Uniq(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestUniqBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i % 3
})
is.Equal([]int{0, 1, 2}, result1)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := UniqBy(allStrings, func(i string) string {
return i
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestGroupBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i % 3
})
is.Equal(map[int][]int{
0: {0, 3},
1: {1, 4},
2: {2, 5},
}, result1)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := GroupBy(allStrings, func(i string) int {
return 42
})
is.IsType(nonempty[42], allStrings, "type preserved")
}
func TestGroupByMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, string) {
return i % 3, strconv.Itoa(i)
})
is.Equal(map[int][]string{
0: {"0", "3"},
1: {"1", "4"},
2: {"2", "5"},
}, result1)
type myInt int
type myInts []myInt
result2 := GroupByMap(myInts{1, 0, 2, 3, 4, 5}, func(i myInt) (int, string) {
return int(i % 3), strconv.Itoa(int(i))
})
is.Equal(map[int][]string{
0: {"0", "3"},
1: {"1", "4"},
2: {"2", "5"},
}, result2)
type product struct {
ID int64
CategoryID int64
}
products := []product{
{ID: 1, CategoryID: 1},
{ID: 2, CategoryID: 1},
{ID: 3, CategoryID: 2},
{ID: 4, CategoryID: 3},
{ID: 5, CategoryID: 3},
}
result3 := GroupByMap(products, func(item product) (int64, string) {
return item.CategoryID, "Product " + strconv.FormatInt(item.ID, 10)
})
is.Equal(map[int64][]string{
1: {"Product 1", "Product 2"},
2: {"Product 3"},
3: {"Product 4", "Product 5"},
}, result3)
}
func TestChunk(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Chunk([]int{0, 1, 2, 3, 4, 5}, 2)
result2 := Chunk([]int{0, 1, 2, 3, 4, 5, 6}, 2)
result3 := Chunk([]int{}, 2)
result4 := Chunk([]int{0}, 2)
is.Equal([][]int{{0, 1}, {2, 3}, {4, 5}}, result1)
is.Equal([][]int{{0, 1}, {2, 3}, {4, 5}, {6}}, result2)
is.Empty(result3)
is.Equal([][]int{{0}}, result4)
is.PanicsWithValue("lo.Chunk: size must be greater than 0", func() {
Chunk([]int{0}, 0)
})
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Chunk(allStrings, 2)
is.IsType(nonempty[0], allStrings, "type preserved")
// appending to a chunk should not affect original slice
original := []int{0, 1, 2, 3, 4, 5}
result5 := Chunk(original, 2)
result5[0] = append(result5[0], 6)
is.Equal([]int{0, 1, 2, 3, 4, 5}, original)
}
func TestPartitionBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
oddEven := func(x int) string {
if x < 0 {
return "negative"
} else if x%2 == 0 {
return "even"
}
return "odd"
}
result1 := PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, oddEven)
result2 := PartitionBy([]int{}, oddEven)
is.Equal([][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}, result1)
is.Empty(result2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := PartitionBy(allStrings, func(item string) int {
return len(item)
})
is.IsType(nonempty[0], allStrings, "type preserved")
}
func TestFlatten(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
is.Equal([]int{0, 1, 2, 3, 4, 5}, result1)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Flatten([]myStrings{allStrings})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestInterleave(t *testing.T) {
is := assert.New(t)
testCases := []struct {
name string
in [][]int
want []int
}{
{
"nil",
[][]int{nil},
[]int{},
},
{
"empty",
[][]int{},
[]int{},
},
{
"empties",
[][]int{{}, {}},
[]int{},
},
{
"same length",
[][]int{{1, 3, 5}, {2, 4, 6}},
[]int{1, 2, 3, 4, 5, 6},
},
{
"different length",
[][]int{{1, 3, 5, 6}, {2, 4}},
[]int{1, 2, 3, 4, 5, 6},
},
{
"many slices",
[][]int{{1}, {2, 5, 8}, {3, 6}, {4, 7, 9, 10}},
[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tc := tc
t.Parallel()
assert.Equal(t, tc.want, Interleave(tc.in...))
})
}
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Interleave(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestShuffle(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Shuffle([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
result2 := Shuffle([]int{})
is.NotEqual([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, result1)
is.Empty(result2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Shuffle(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestReverse(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Reverse([]int{0, 1, 2, 3, 4, 5})
result2 := Reverse([]int{0, 1, 2, 3, 4, 5, 6})
result3 := Reverse([]int{})
is.Equal([]int{5, 4, 3, 2, 1, 0}, result1)
is.Equal([]int{6, 5, 4, 3, 2, 1, 0}, result2)
is.Empty(result3)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Reverse(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestFill(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Fill([]foo{{"a"}, {"a"}}, foo{"b"})
result2 := Fill([]foo{}, foo{"a"})
is.Equal([]foo{{"b"}, {"b"}}, result1)
is.Empty(result2)
}
func TestRepeat(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Repeat(2, foo{"a"})
result2 := Repeat(0, foo{"a"})
is.Equal([]foo{{"a"}, {"a"}}, result1)
is.Empty(result2)
}
func TestRepeatBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
cb := func(i int) int {
return int(math.Pow(float64(i), 2))
}
result1 := RepeatBy(0, cb)
result2 := RepeatBy(2, cb)
result3 := RepeatBy(5, cb)
is.Empty(result1)
is.Equal([]int{0, 1}, result2)
is.Equal([]int{0, 1, 4, 9, 16}, result3)
}
func TestKeyBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := KeyBy([]string{"a", "aa", "aaa"}, func(str string) int {
return len(str)
})
is.Equal(map[int]string{1: "a", 2: "aa", 3: "aaa"}, result1)
}
func TestAssociate(t *testing.T) {
t.Parallel()
type foo struct {
baz string
bar int
}
transform := func(f *foo) (string, int) {
return f.baz, f.bar
}
testCases := []struct {
in []*foo
want map[string]int
}{
{
in: []*foo{{baz: "apple", bar: 1}},
want: map[string]int{"apple": 1},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}},
want: map[string]int{"apple": 1, "banana": 2},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "apple", bar: 2}},
want: map[string]int{"apple": 2},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
tc := tc
t.Parallel()
assert.Equal(t, tc.want, Associate(tc.in, transform))
})
}
}
func TestSliceToMap(t *testing.T) {
t.Parallel()
type foo struct {
baz string
bar int
}
transform := func(f *foo) (string, int) {
return f.baz, f.bar
}
testCases := []struct {
in []*foo
want map[string]int
}{
{
in: []*foo{{baz: "apple", bar: 1}},
want: map[string]int{"apple": 1},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}},
want: map[string]int{"apple": 1, "banana": 2},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "apple", bar: 2}},
want: map[string]int{"apple": 2},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
tc := tc
t.Parallel()
assert.Equal(t, tc.want, SliceToMap(tc.in, transform))
})
}
}
func TestFilterSliceToMap(t *testing.T) {
t.Parallel()
type foo struct {
baz string
bar int
}
transform := func(f *foo) (string, int, bool) {
return f.baz, f.bar, f.bar > 1
}
testCases := []struct {
in []*foo
want map[string]int
}{
{
in: []*foo{{baz: "apple", bar: 1}},
want: map[string]int{},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}},
want: map[string]int{"banana": 2},
},
{
in: []*foo{{baz: "apple", bar: 1}, {baz: "apple", bar: 2}},
want: map[string]int{"apple": 2},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
tc := tc
t.Parallel()
assert.Equal(t, tc.want, FilterSliceToMap(tc.in, transform))
})
}
}
func TestKeyify(t *testing.T) {
t.Parallel()
is := assert.New(t)
result1 := Keyify([]int{1, 2, 3, 4})
result2 := Keyify([]int{1, 1, 1, 2})
result3 := Keyify([]int{})
is.Equal(map[int]struct{}{1: {}, 2: {}, 3: {}, 4: {}}, result1)
is.Equal(map[int]struct{}{1: {}, 2: {}}, result2)
is.Empty(result3)
}
func TestDrop(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Equal([]int{1, 2, 3, 4}, Drop([]int{0, 1, 2, 3, 4}, 1))
is.Equal([]int{2, 3, 4}, Drop([]int{0, 1, 2, 3, 4}, 2))
is.Equal([]int{3, 4}, Drop([]int{0, 1, 2, 3, 4}, 3))
is.Equal([]int{4}, Drop([]int{0, 1, 2, 3, 4}, 4))
is.Empty(Drop([]int{0, 1, 2, 3, 4}, 5))
is.Empty(Drop([]int{0, 1, 2, 3, 4}, 6))
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Drop(allStrings, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestDropRight(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Equal([]int{0, 1, 2, 3}, DropRight([]int{0, 1, 2, 3, 4}, 1))
is.Equal([]int{0, 1, 2}, DropRight([]int{0, 1, 2, 3, 4}, 2))
is.Equal([]int{0, 1}, DropRight([]int{0, 1, 2, 3, 4}, 3))
is.Equal([]int{0}, DropRight([]int{0, 1, 2, 3, 4}, 4))
is.Empty(DropRight([]int{0, 1, 2, 3, 4}, 5))
is.Empty(DropRight([]int{0, 1, 2, 3, 4}, 6))
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := DropRight(allStrings, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestDropWhile(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Equal([]int{4, 5, 6}, DropWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t != 4
}))
is.Empty(DropWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return true
}))
is.Equal([]int{0, 1, 2, 3, 4, 5, 6}, DropWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t == 10
}))
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := DropWhile(allStrings, func(t string) bool {
return t != "foo"
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestDropRightWhile(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Equal([]int{0, 1, 2, 3}, DropRightWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t != 3
}))
is.Equal([]int{0, 1}, DropRightWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t != 1
}))
is.Equal([]int{0, 1, 2, 3, 4, 5, 6}, DropRightWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t == 10
}))
is.Empty(DropRightWhile([]int{0, 1, 2, 3, 4, 5, 6}, func(t int) bool {
return t != 10
}))
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := DropRightWhile(allStrings, func(t string) bool {
return t != "foo"
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestDropByIndex(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Equal([]int{1, 2, 3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 0))
is.Equal([]int{3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 0, 1, 2))
is.Equal([]int{0, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, -4, -2, -3))
is.Equal([]int{0, 2, 3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, -4, -4))
is.Equal([]int{2, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 3, 1, 0))
is.Equal([]int{0, 1, 3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 2))
is.Equal([]int{0, 1, 2, 3}, DropByIndex([]int{0, 1, 2, 3, 4}, 4))
is.Equal([]int{0, 1, 2, 3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 5))
is.Equal([]int{0, 1, 2, 3, 4}, DropByIndex([]int{0, 1, 2, 3, 4}, 100))
is.Equal([]int{0, 1, 2, 3}, DropByIndex([]int{0, 1, 2, 3, 4}, -1))
is.Empty(DropByIndex([]int{}, 0, 1))
is.Empty(DropByIndex([]int{42}, 0, 1))
is.Empty(DropByIndex([]int{42}, 1, 0))
is.Empty(DropByIndex([]int{}, 1))
is.Empty(DropByIndex([]int{1}, 0))
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := DropByIndex(allStrings, 0)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestReject(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
is.Equal([]int{1, 3}, r1)
r2 := Reject([]string{"Smith", "foo", "Domin", "bar", "Olivia"}, func(x string, _ int) bool {
return len(x) > 3
})
is.Equal([]string{"foo", "bar"}, r2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Reject(allStrings, func(x string, _ int) bool {
return len(x) > 0
})
is.IsType(nonempty, allStrings, "type preserved")
}
func TestRejectMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := RejectMap([]int64{1, 2, 3, 4}, func(x int64, _ int) (string, bool) {
if x%2 == 0 {
return strconv.FormatInt(x, 10), false
}
return "", true
})
r2 := RejectMap([]string{"cpu", "gpu", "mouse", "keyboard"}, func(x string, _ int) (string, bool) {
if strings.HasSuffix(x, "pu") {
return "xpu", false
}
return "", true
})
is.Equal([]string{"2", "4"}, r1)
is.Equal([]string{"xpu", "xpu"}, r2)
}
func TestFilterReject(t *testing.T) {
t.Parallel()
is := assert.New(t)
left1, right1 := FilterReject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
is.Equal([]int{2, 4}, left1)
is.Equal([]int{1, 3}, right1)
left2, right2 := FilterReject([]string{"Smith", "foo", "Domin", "bar", "Olivia"}, func(x string, _ int) bool {
return len(x) > 3
})
is.Equal([]string{"Smith", "Domin", "Olivia"}, left2)
is.Equal([]string{"foo", "bar"}, right2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
a, b := FilterReject(allStrings, func(x string, _ int) bool {
return len(x) > 0
})
is.IsType(a, allStrings, "type preserved")
is.IsType(b, allStrings, "type preserved")
}
func TestCount(t *testing.T) {
t.Parallel()
is := assert.New(t)
count1 := Count([]int{1, 2, 1}, 1)
count2 := Count([]int{1, 2, 1}, 3)
count3 := Count([]int{}, 1)
is.Equal(2, count1)
is.Zero(count2)
is.Zero(count3)
}
func TestCountBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
count1 := CountBy([]int{1, 2, 1}, func(i int) bool {
return i < 2
})
count2 := CountBy([]int{1, 2, 1}, func(i int) bool {
return i > 2
})
count3 := CountBy([]int{}, func(i int) bool {
return i <= 2
})
is.Equal(2, count1)
is.Zero(count2)
is.Zero(count3)
}
func TestCountValues(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.Empty(CountValues([]int{}))
is.Equal(map[int]int{1: 1, 2: 1}, CountValues([]int{1, 2}))
is.Equal(map[int]int{1: 1, 2: 2}, CountValues([]int{1, 2, 2}))
is.Equal(map[string]int{"": 1, "foo": 1, "bar": 1}, CountValues([]string{"foo", "bar", ""}))
is.Equal(map[string]int{"foo": 1, "bar": 2}, CountValues([]string{"foo", "bar", "bar"}))
}
func TestCountValuesBy(t *testing.T) {
t.Parallel()
is := assert.New(t)
oddEven := func(v int) bool {
return v%2 == 0
}
length := func(v string) int {
return len(v)
}
result1 := CountValuesBy([]int{}, oddEven)
result2 := CountValuesBy([]int{1, 2}, oddEven)
result3 := CountValuesBy([]int{1, 2, 2}, oddEven)
result4 := CountValuesBy([]string{"foo", "bar", ""}, length)
result5 := CountValuesBy([]string{"foo", "bar", "bar"}, length)
is.Empty(result1)
is.Equal(map[bool]int{true: 1, false: 1}, result2)
is.Equal(map[bool]int{true: 2, false: 1}, result3)
is.Equal(map[int]int{0: 1, 3: 2}, result4)
is.Equal(map[int]int{3: 3}, result5)
}
func TestSubset(t *testing.T) {
t.Parallel()
is := assert.New(t)
in := []int{0, 1, 2, 3, 4}
out1 := Subset(in, 0, 0)
out2 := Subset(in, 10, 2)
out3 := Subset(in, -10, 2)
out4 := Subset(in, 0, 10)
out5 := Subset(in, 0, 2)
out6 := Subset(in, 2, 2)
out7 := Subset(in, 2, 5)
out8 := Subset(in, 2, 3)
out9 := Subset(in, 2, 4)
out10 := Subset(in, -2, 4)
out11 := Subset(in, -4, 1)
out12 := Subset(in, -4, math.MaxUint)
is.Empty(out1)
is.Empty(out2)
is.Equal([]int{0, 1}, out3)
is.Equal([]int{0, 1, 2, 3, 4}, out4)
is.Equal([]int{0, 1}, out5)
is.Equal([]int{2, 3}, out6)
is.Equal([]int{2, 3, 4}, out7)
is.Equal([]int{2, 3, 4}, out8)
is.Equal([]int{2, 3, 4}, out9)
is.Equal([]int{3, 4}, out10)
is.Equal([]int{1}, out11)
is.Equal([]int{1, 2, 3, 4}, out12)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Subset(allStrings, 0, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestSlice(t *testing.T) {
t.Parallel()
is := assert.New(t)
in := []int{0, 1, 2, 3, 4}
out1 := Slice(in, 0, 0)
out2 := Slice(in, 0, 1)
out3 := Slice(in, 0, 5)
out4 := Slice(in, 0, 6)
out5 := Slice(in, 1, 1)
out6 := Slice(in, 1, 5)
out7 := Slice(in, 1, 6)
out8 := Slice(in, 4, 5)
out9 := Slice(in, 5, 5)
out10 := Slice(in, 6, 5)
out11 := Slice(in, 6, 6)
out12 := Slice(in, 1, 0)
out13 := Slice(in, 5, 0)
out14 := Slice(in, 6, 4)
out15 := Slice(in, 6, 7)
out16 := Slice(in, -10, 1)
out17 := Slice(in, -1, 3)
out18 := Slice(in, -10, 7)
is.Empty(out1)
is.Equal([]int{0}, out2)
is.Equal([]int{0, 1, 2, 3, 4}, out3)
is.Equal([]int{0, 1, 2, 3, 4}, out4)
is.Empty(out5)
is.Equal([]int{1, 2, 3, 4}, out6)
is.Equal([]int{1, 2, 3, 4}, out7)
is.Equal([]int{4}, out8)
is.Empty(out9)
is.Empty(out10)
is.Empty(out11)
is.Empty(out12)
is.Empty(out13)
is.Empty(out14)
is.Empty(out15)
is.Equal([]int{0}, out16)
is.Equal([]int{0, 1, 2}, out17)
is.Equal([]int{0, 1, 2, 3, 4}, out18)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Slice(allStrings, 0, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestReplace(t *testing.T) {
t.Parallel()
is := assert.New(t)
in := []int{0, 1, 0, 1, 2, 3, 0}
out1 := Replace(in, 0, 42, 2)
out2 := Replace(in, 0, 42, 1)
out3 := Replace(in, 0, 42, 0)
out4 := Replace(in, 0, 42, -1)
out5 := Replace(in, 0, 42, -1)
out6 := Replace(in, -1, 42, 2)
out7 := Replace(in, -1, 42, 1)
out8 := Replace(in, -1, 42, 0)
out9 := Replace(in, -1, 42, -1)
out10 := Replace(in, -1, 42, -1)
is.Equal([]int{42, 1, 42, 1, 2, 3, 0}, out1)
is.Equal([]int{42, 1, 0, 1, 2, 3, 0}, out2)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out3)
is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out4)
is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out5)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out6)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out7)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out8)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out9)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out10)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Replace(allStrings, "0", "2", 1)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestReplaceAll(t *testing.T) {
t.Parallel()
is := assert.New(t)
in := []int{0, 1, 0, 1, 2, 3, 0}
out1 := ReplaceAll(in, 0, 42)
out2 := ReplaceAll(in, -1, 42)
is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out1)
is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out2)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := ReplaceAll(allStrings, "0", "2")
is.IsType(nonempty, allStrings, "type preserved")
}
func TestCompact(t *testing.T) {
t.Parallel()
is := assert.New(t)
r1 := Compact([]int{2, 0, 4, 0})
is.Equal([]int{2, 4}, r1)
r2 := Compact([]string{"", "foo", "", "bar", ""})
is.Equal([]string{"foo", "bar"}, r2)
r3 := Compact([]bool{true, false, true, false})
is.Equal([]bool{true, true}, r3)
type foo struct {
bar int
baz string
}
// slice of structs
// If all fields of an element are zero values, Compact removes it.
r4 := Compact([]foo{
{bar: 1, baz: "a"}, // all fields are non-zero values
{bar: 0, baz: ""}, // all fields are zero values
{bar: 2, baz: ""}, // bar is non-zero
})
is.Equal([]foo{{bar: 1, baz: "a"}, {bar: 2, baz: ""}}, r4)
// slice of pointers to structs
// If an element is nil, Compact removes it.
e1, e2, e3 := foo{bar: 1, baz: "a"}, foo{bar: 0, baz: ""}, foo{bar: 2, baz: ""}
// NOTE: e2 is a zero value of foo, but its pointer &e2 is not a zero value of *foo.
r5 := Compact([]*foo{&e1, &e2, nil, &e3})
is.Equal([]*foo{&e1, &e2, &e3}, r5)
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Compact(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
func TestIsSorted(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.True(IsSorted([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}))
is.True(IsSorted([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}))
is.False(IsSorted([]int{0, 1, 4, 3, 2, 5, 6, 7, 8, 9, 10}))
is.False(IsSorted([]string{"a", "b", "d", "c", "e", "f", "g", "h", "i", "j"}))
}
func TestIsSortedByKey(t *testing.T) {
t.Parallel()
is := assert.New(t)
is.True(IsSortedByKey([]string{"a", "bb", "ccc"}, func(s string) int {
return len(s)
}))
is.False(IsSortedByKey([]string{"aa", "b", "ccc"}, func(s string) int {
return len(s)
}))
is.True(IsSortedByKey([]string{"1", "2", "3", "11"}, func(s string) int {
ret, _ := strconv.Atoi(s)
return ret
}))
}
func TestSplice(t *testing.T) {
t.Parallel()
is := assert.New(t)
sample := []string{"a", "b", "c", "d", "e", "f", "g"}
// normal case
results := Splice(sample, 1, "1", "2")
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
is.Equal([]string{"a", "1", "2", "b", "c", "d", "e", "f", "g"}, results)
// check there is no side effect
results = Splice(sample, 1)
results[0] = "b"
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
// positive overflow
results = Splice(sample, 42, "1", "2")
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g", "1", "2"}, results)
// negative overflow
results = Splice(sample, -42, "1", "2")
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
is.Equal([]string{"1", "2", "a", "b", "c", "d", "e", "f", "g"}, results)
// backward
results = Splice(sample, -2, "1", "2")
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
is.Equal([]string{"a", "b", "c", "d", "e", "1", "2", "f", "g"}, results)
results = Splice(sample, -7, "1", "2")
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, sample)
is.Equal([]string{"1", "2", "a", "b", "c", "d", "e", "f", "g"}, results)
// other
is.Equal([]string{"1", "2"}, Splice([]string{}, 0, "1", "2"))
is.Equal([]string{"1", "2"}, Splice([]string{}, 1, "1", "2"))
is.Equal([]string{"1", "2"}, Splice([]string{}, -1, "1", "2"))
is.Equal([]string{"1", "2", "0"}, Splice([]string{"0"}, 0, "1", "2"))
is.Equal([]string{"0", "1", "2"}, Splice([]string{"0"}, 1, "1", "2"))
is.Equal([]string{"1", "2", "0"}, Splice([]string{"0"}, -1, "1", "2"))
// type preserved
type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Splice(allStrings, 1, "1", "2")
is.IsType(nonempty, allStrings, "type preserved")
}
func TestCutSuccess(t *testing.T) {
t.Parallel()
is := assert.New(t)
// case 1
actualLeft, actualRight, result := Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.True(result)
is.Equal([]string{}, actualLeft)
is.Equal([]string{"c", "d", "e", "f", "g"}, actualRight)
// case 2
actualLeft, actualRight, result = Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"f", "g"})
is.True(result)
is.Equal([]string{"a", "b", "c", "d", "e"}, actualLeft)
is.Equal([]string{}, actualRight)
// case 3
actualLeft, actualRight, result = Cut([]string{"g"}, []string{"g"})
is.True(result)
is.Equal([]string{}, actualLeft)
is.Equal([]string{}, actualRight)
// case 4
actualLeft, actualRight, result = Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "c"})
is.True(result)
is.Equal([]string{"a"}, actualLeft)
is.Equal([]string{"d", "e", "f", "g"}, actualRight)
// case 5
actualLeft, actualRight, result = Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"e", "f"})
is.True(result)
is.Equal([]string{"a", "b", "c", "d"}, actualLeft)
is.Equal([]string{"g"}, actualRight)
// case 6
actualLeft, actualRight, result = Cut([]string{"a", "b"}, []string{"b"})
is.True(result)
is.Equal([]string{"a"}, actualLeft)
is.Equal([]string{}, actualRight)
// case 7
actualLeft, actualRight, result = Cut([]string{"a", "b"}, []string{"a"})
is.True(result)
is.Equal([]string{}, actualLeft)
is.Equal([]string{"b"}, actualRight)
}
func TestCutFail(t *testing.T) {
t.Parallel()
is := assert.New(t)
// case 1
actualLeft, actualRight, result := Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"z"})
is.False(result)
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actualLeft)
is.Equal([]string{}, actualRight)
// case 2
actualLeft, actualRight, result = Cut([]string{}, []string{"z"})
is.False(result)
is.Equal([]string{}, actualLeft)
is.Equal([]string{}, actualRight)
// case 3
actualLeft, actualRight, result = Cut([]string{"a"}, []string{"z"})
is.False(result)
is.Equal([]string{"a"}, actualLeft)
is.Equal([]string{}, actualRight)
}
type TestCutStruct struct {
id int
data string
}
func TestCutPrefix(t *testing.T) {
t.Parallel()
is := assert.New(t)
// case 1
actualAfter, result := CutPrefix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{{id: 1, data: "a"}},
)
is.True(result)
is.Equal([]TestCutStruct{{id: 2, data: "a"}, {id: 2, data: "b"}}, actualAfter)
// case 2
actualAfter, result = CutPrefix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{},
)
is.True(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}}, actualAfter)
// case 3
actualAfter, result = CutPrefix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{{id: 2, data: "b"}},
)
is.False(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}}, actualAfter)
// case 4
actualAfter, result = CutPrefix(
[]TestCutStruct{},
[]TestCutStruct{{id: 2, data: "b"}},
)
is.False(result)
is.Equal([]TestCutStruct{}, actualAfter)
// case 5
actualAfterS, result := CutPrefix([]string{"a", "a", "b"}, []string{})
is.True(result)
is.Equal([]string{"a", "a", "b"}, actualAfterS)
}
func TestCutSuffix(t *testing.T) {
t.Parallel()
is := assert.New(t)
// case 1
actualBefore, result := CutSuffix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{{id: 3, data: "b"}},
)
is.False(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}}, actualBefore)
// case 2
actualBefore, result = CutSuffix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{{id: 2, data: "b"}},
)
is.True(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}}, actualBefore)
// case 3
actualBefore, result = CutSuffix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{},
)
is.True(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}}, actualBefore)
// case 4
actualBefore, result = CutSuffix(
[]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}},
[]TestCutStruct{{id: 2, data: "a"}},
)
is.False(result)
is.Equal([]TestCutStruct{{id: 1, data: "a"}, {id: 2, data: "a"}, {id: 2, data: "b"}}, actualBefore)
// case 5
actualAfterS, result := CutSuffix([]string{"a", "a", "b"}, []string{})
is.True(result)
is.Equal([]string{"a", "a", "b"}, actualAfterS)
}
func TestTrim(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = Trim([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimLeft(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimLeft([]string{"a", "a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "a"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = TrimLeft([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimPrefix(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimPrefix([]string{"a", "b", "a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "a"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimRight(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{}, actual)
actual = TrimRight([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}
func TestTrimSuffix(t *testing.T) {
t.Parallel()
is := assert.New(t)
actual := TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, []string{"f", "g"})
is.Equal([]string{"a", "b", "c", "d", "e"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, []string{"g", "f"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g"})
is.Equal([]string{}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c", "d", "e", "f", "g", "h"})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
actual = TrimSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
is.Equal([]string{"a", "b", "c", "d", "e", "f", "g"}, actual)
}