Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[test] Optimizations, improvements, and unit tests for common/common.go (part 2 of 3) #182

Merged
merged 12 commits into from
May 10, 2023
5 changes: 5 additions & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func NamespacedNameToObjectKey(namespacedName, defaultNamespace string) client.O
return client.ObjectKey{Namespace: defaultNamespace, Name: split[0]}
}

// Contains checks if the given target string is present in the slice of strings 'slice'.
// It returns true if the target string is found in the slice, false otherwise.
func Contains(slice []string, target string) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can Generics be used here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@didierofrivia We need your expert opinion! 🤓

for idx := range slice {
if slice[idx] == target {
Expand All @@ -98,6 +100,7 @@ func Find[T any](slice []T, match func(T) bool) (*T, bool) {
return nil, false
}

// Map applies the given mapper function to each element in the input slice and returns a new slice with the results.
func Map[T, U any](slice []T, f func(T) U) []U {
arr := make([]U, len(slice))
for i, e := range slice {
Expand All @@ -106,12 +109,14 @@ func Map[T, U any](slice []T, f func(T) U) []U {
return arr
}

// SliceCopy copies the elements from the input slice into the output slice, and returns the output slice.
func SliceCopy[T any](s1 []T) []T {
s2 := make([]T, len(s1))
copy(s2, s1)
return s2
}

// ReverseSlice creates a reversed copy of the input slice.
func ReverseSlice[T any](input []T) []T {
inputLen := len(input)
output := make([]T, inputLen)
Expand Down
207 changes: 207 additions & 0 deletions pkg/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package common

import (
"reflect"
"testing"
)

Expand Down Expand Up @@ -67,3 +68,209 @@ func TestFind(t *testing.T) {
t.Error("should not have found anything in the slice")
}
}

func TestContains(t *testing.T) {
testCases := []struct {
name string
slice []string
target string
expected bool
}{
{
name: "when slice has one target item then return true",
slice: []string{"test-gw"},
target: "test-gw",
expected: true,
},
{
name: "when slice is empty then return false",
slice: []string{},
target: "test-gw",
expected: false,
},
{
name: "when target is in a slice then return true",
slice: []string{"test-gw1", "test-gw2", "test-gw3"},
target: "test-gw2",
expected: true,
},
{
name: "when no target in a slice then return false",
slice: []string{"test-gw1", "test-gw2", "test-gw3"},
target: "test-gw4",
expected: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if Contains(tc.slice, tc.target) != tc.expected {
t.Errorf("when slice=%v and target=%s, expected=%v, but got=%v", tc.slice, tc.target, tc.expected, !tc.expected)
}
})
}
}

func TestMap(t *testing.T) {
slice1 := []int{1, 2, 3, 4}
f1 := func(x int) int { return x + 1 }
expected1 := []int{2, 3, 4, 5}
result1 := Map(slice1, f1)
t.Run("when mapping an int slice with an increment function then return new slice with the incremented values", func(t *testing.T) {
if !reflect.DeepEqual(result1, expected1) {
t.Errorf("result1 = %v; expected %v", result1, expected1)
}
})

slice2 := []string{"hello", "world", "buz", "a"}
f2 := func(s string) int { return len(s) }
expected2 := []int{5, 5, 3, 1}
result2 := Map(slice2, f2)
t.Run("when mapping a string slice with string->int mapping then return new slice with the mapped values", func(t *testing.T) {
if !reflect.DeepEqual(result2, expected2) {
t.Errorf("result2 = %v; expected %v", result2, expected2)
}
})

slice3 := []int{}
f3 := func(x int) float32 { return float32(x) / 2 }
expected3 := []float32{}
result3 := Map(slice3, f3)
t.Run("when mapping an empty int slice then return an empty slice", func(t *testing.T) {
if !reflect.DeepEqual(result3, expected3) {
t.Errorf("result3 = %v; expected %v", result3, expected3)
}
})
}

func TestSliceCopy(t *testing.T) {
input1 := []int{1, 2, 3}
expected1 := []int{1, 2, 3}
output1 := SliceCopy(input1)
t.Run("when given slice of integers then return a copy of the input slice", func(t *testing.T) {
if !reflect.DeepEqual(output1, expected1) {
t.Errorf("SliceCopy(%v) = %v; expected %v", input1, output1, expected1)
}
})

input2 := []string{"foo", "bar", "baz"}
expected2 := []string{"foo", "bar", "baz"}
output2 := SliceCopy(input2)
t.Run("when given slice of strings then return a copy of the input slice", func(t *testing.T) {
if !reflect.DeepEqual(output2, expected2) {
t.Errorf("SliceCopy(%v) = %v; expected %v", input2, output2, expected2)
}
})

type person struct {
name string
age int
}
input3 := []person{{"Artem", 65}, {"DD", 18}, {"Charlie", 23}}
expected3 := []person{{"Artem", 65}, {"DD", 18}, {"Charlie", 23}}
output3 := SliceCopy(input3)
t.Run("when given slice of structs then return a copy of the input slice", func(t *testing.T) {
if !reflect.DeepEqual(output3, expected3) {
t.Errorf("SliceCopy(%v) = %v; expected %v", input3, output3, expected3)
}
})

input4 := []int{1, 2, 3}
expected4 := []int{1, 2, 3}
output4 := SliceCopy(input4)
t.Run("when modifying the original input slice then does not affect the returned copy", func(t *testing.T) {
if !reflect.DeepEqual(output4, expected4) {
t.Errorf("SliceCopy(%v) = %v; expected %v", input4, output4, expected4)
}
input4[0] = 4
if reflect.DeepEqual(output4, input4) {
t.Errorf("modifying the original input slice should not change the output slice")
}
})
}

func TestReverseSlice(t *testing.T) {
input1 := []int{1, 2, 3}
expected1 := []int{3, 2, 1}
output1 := ReverseSlice(input1)
t.Run("when given slice of integers then return reversed copy of the input slice", func(t *testing.T) {
if !reflect.DeepEqual(output1, expected1) {
t.Errorf("ReverseSlice(%v) = %v; expected %v", input1, output1, expected1)
}
})

input2 := []string{"foo", "bar", "baz"}
expected2 := []string{"baz", "bar", "foo"}
output2 := ReverseSlice(input2)
t.Run("when given slice of strings then return reversed copy of the input slice", func(t *testing.T) {
if !reflect.DeepEqual(output2, expected2) {
t.Errorf("ReverseSlice(%v) = %v; expected %v", input2, output2, expected2)
}
})

input3 := []int{}
expected3 := []int{}
output3 := ReverseSlice(input3)
t.Run("when given an empty slice then return empty slice", func(t *testing.T) {
if !reflect.DeepEqual(output3, expected3) {
t.Errorf("ReverseSlice(%v) = %v; expected %v", input3, output3, expected3)
}
})
}

func TestMergeMapStringString(t *testing.T) {
testCases := []struct {
name string
existing map[string]string
desired map[string]string
expected bool
expectedState map[string]string
}{
{
name: "when existing and desired are empty then return false and not modify the existing map",
existing: map[string]string{},
desired: map[string]string{},
expected: false,
expectedState: map[string]string{},
},
{
name: "when existing is empty and desired has values then return true and set the values in the existing map",
existing: map[string]string{},
desired: map[string]string{"a": "1", "b": "2"},
expected: true,
expectedState: map[string]string{"a": "1", "b": "2"},
},
{
name: "when existing has some values and desired has different/new values then return true and modify the existing map",
existing: map[string]string{"a": "1", "b": "2"},
desired: map[string]string{"a": "3", "c": "4"},
expected: true,
expectedState: map[string]string{"a": "3", "b": "2", "c": "4"},
},
{
name: "when existing has all the values from desired then return false and not modify the existing map",
existing: map[string]string{"a": "1", "b": "2"},
desired: map[string]string{"a": "1", "b": "2"},
expected: false,
expectedState: map[string]string{"a": "1", "b": "2"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
existingCopy := make(map[string]string, len(tc.existing))
for k, v := range tc.existing {
existingCopy[k] = v
}
modified := MergeMapStringString(&existingCopy, tc.desired)

if modified != tc.expected {
t.Errorf("MergeMapStringString(%v, %v) returned %v; expected %v", tc.existing, tc.desired, modified, tc.expected)
}

if !reflect.DeepEqual(existingCopy, tc.expectedState) {
t.Errorf("MergeMapStringString(%v, %v) modified the existing map to %v; expected %v", tc.existing, tc.desired, existingCopy, tc.expectedState)
}
})
}
}