Repository: MarvinJWendt/testza
Pull request #70 (2022-01-10 22:43:53)

Added `AssertSameElements`
#33 


Patch:
diff --git a/assert.go b/assert.go
index d66fa49..517957f 100644
--- a/assert.go
+++ b/assert.go
@@ -9,8 +9,9 @@ import (
 	"strings"
 	"time"
 
-	"github.com/MarvinJWendt/testza/internal"
 	"github.com/pterm/pterm"
+
+	"github.com/MarvinJWendt/testza/internal"
 )
 
 type testMock struct {
@@ -189,6 +190,65 @@ func doesContain(object, element interface{}) bool {
 	return false
 }
 
+func isList(list interface{}) bool {
+	kind := reflect.TypeOf(list).Kind()
+	if kind != reflect.Array && kind != reflect.Slice {
+		return false
+	}
+
+	return true
+}
+
+func hasSameElements(expected interface{}, actual interface{}) bool {
+	if isNil(expected) || isNil(actual) {
+		return expected == actual
+	}
+
+	if !isList(expected) || !isList(actual) {
+		return false
+	}
+
+	expectedValue := reflect.ValueOf(expected)
+	actualValue := reflect.ValueOf(actual)
+
+	expectedLen := expectedValue.Len()
+	actualLen := actualValue.Len()
+
+	visited := make([]bool, actualLen)
+
+	var extraA, extraB []interface{}
+	for i := 0; i < expectedLen; i++ {
+		element := expectedValue.Index(i).Interface()
+		found := false
+		for j := 0; j < actualLen; j++ {
+			if visited[j] {
+				continue
+			}
+			if isEqual(actualValue.Index(j).Interface(), element) {
+				visited[j] = true
+				found = true
+				break
+			}
+		}
+		if !found {
+			extraA = append(extraA, element)
+		}
+	}
+
+	for j := 0; j < actualLen; j++ {
+		if visited[j] {
+			continue
+		}
+		extraB = append(extraB, actualValue.Index(j).Interface())
+	}
+
+	if len(extraA) == 0 && len(extraB) == 0 {
+		return true
+	}
+
+	return false
+}
+
 // ** Helper Methods **
 
 // AssertKindOf asserts that the object is a type of kind exptectedKind.
@@ -794,3 +854,46 @@ func AssertDecreasing(t testRunner, object interface{}, msg ...interface{}) {
 
 	internal.AssertCompareHelper(t, object, -1, msg...)
 }
+
+// AssertSameElements asserts that two slices contains same elements (including pointers).
+// The order is irrelevant.
+//
+// Example:
+//  testza.AssertSameElements(t, []string{"Hello", "World"}, []string{"Hello", "World"})
+//  testza.AssertSameElements(t, []int{1,2,3}, []int{1,2,3})
+//  testza.AssertSameElements(t, []int{1,2}, []int{2,1})
+//
+//  type A struct {
+//	  a string
+//  }
+//  testza.AssertSameElements(t, []*A{{a: "A"}, {a: "B"}, {a: "C"}}, []*A{{a: "A"}, {a: "B"}, {a: "C"}})
+func AssertSameElements(t testRunner, expected interface{}, actual interface{}, msg ...interface{}) {
+	if test, ok := t.(helper); ok {
+		test.Helper()
+	}
+
+	if !hasSameElements(expected, actual) {
+		internal.Fail(t, "Two objects that !!should have same elements!!, do not have same elements.", internal.NewObjectsExpectedActual(expected, actual), msg...)
+	}
+}
+
+// AssertNotSameElements asserts that two slices contains same elements (including pointers).
+// The order is irrelevant.
+//
+// Example:
+//  testza.AssertNotSameElements(t, []string{"Hello", "World"}, []string{"Hello", "World", "World"})
+//  testza.AssertNotSameElements(t, []int{1,2}, []int{1,2,3})
+//
+//  type A struct {
+//	  a string
+//  }
+//  testza.AssertNotSameElements(t, []*A{{a: "A"}, {a: "B"}, {a: "C"}}, []*A{{a: "A"}, {a: "B"}, {a: "C"}, {a: "D"}})
+func AssertNotSameElements(t testRunner, expected interface{}, actual interface{}, msg ...interface{}) {
+	if test, ok := t.(helper); ok {
+		test.Helper()
+	}
+
+	if hasSameElements(expected, actual) {
+		internal.Fail(t, "Two objects that !!should have same elements!!, do not have same elements.", internal.NewObjectsExpectedActual(expected, actual), msg...)
+	}
+}


Test patch:
diff --git a/assert_test.go b/assert_test.go
index 23c6e0c..99b3e1c 100644
--- a/assert_test.go
+++ b/assert_test.go
@@ -9,8 +9,9 @@ import (
 	"testing"
 	"time"
 
-	. "github.com/MarvinJWendt/testza"
 	"github.com/pterm/pterm"
+
+	. "github.com/MarvinJWendt/testza"
 )
 
 type assertionTestStruct struct {
@@ -922,3 +923,153 @@ func TestAssertIsDecreasing_fail(t *testing.T) {
 		})
 	}
 }
+
+func TestAssertSameElements(t *testing.T) {
+	t.Run("String", func(t *testing.T) {
+		MockInputStringRunTests(t, MockInputStringFull(), func(t *testing.T, index int, str string) {
+			t.Helper()
+
+			testEqual(t, str, str)
+		})
+	})
+
+	t.Run("Nil and Nil are equal", func(t *testing.T) {
+		AssertSameElements(t, nil, nil)
+	})
+
+	t.Run("Same arrays to same struct are equal", func(t *testing.T) {
+		s := generateStruct()
+		ss := []assertionTestStruct{s}
+		AssertSameElements(t, ss, ss)
+	})
+
+	t.Run("Same arrays to same pointer struct are equal", func(t *testing.T) {
+		s := generateStruct()
+		ss := []*assertionTestStruct{&s}
+		AssertSameElements(t, ss, ss)
+	})
+
+	t.Run("Structs", func(t *testing.T) {
+		// Test ten random structs for equality.
+		ss := make([]assertionTestStruct, 0)
+		for i := 0; i < 10; i++ {
+			s := generateStruct()
+			ss = append(ss, s)
+		}
+
+		ss2 := ss
+		AssertSameElements(t, ss, ss2)
+	})
+
+	t.Run("Strings", func(t *testing.T) {
+		s := []string{"Hello", "World"}
+		ss := []string{"World", "Hello"}
+		AssertSameElements(t, s, ss)
+	})
+
+	t.Run("Integers", func(t *testing.T) {
+		s := []int{1, 2, 3, 4, 5, 6, 7, 8}
+		ss := []int{8, 7, 6, 5, 4, 3, 2, 1}
+		AssertSameElements(t, s, ss)
+	})
+}
+
+func TestAssertSameElementsFails(t *testing.T) {
+	t.Run("Not an array", func(t *testing.T) {
+		MockInputStringRunTests(t, MockInputStringFull(), func(t *testing.T, index int, str string) {
+			AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+				AssertSameElements(t, str, str)
+			})
+		})
+	})
+
+	t.Run("Structs", func(t *testing.T) {
+		// Test ten random structs for equality.
+		ss := make([]assertionTestStruct, 0)
+		for i := 0; i < 10; i++ {
+			s := generateStruct()
+			ss = append(ss, s)
+		}
+
+		ss2 := ss
+		ss2 = append(ss2, generateStruct())
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertSameElements(t, ss, ss2)
+		})
+	})
+
+	t.Run("Strings", func(t *testing.T) {
+		s := []string{"Hello", "World"}
+		ss := []string{"World", "Hello", "Again"}
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertSameElements(t, s, ss)
+		})
+	})
+
+	t.Run("Integers", func(t *testing.T) {
+		s := []int{1, 2, 3, 4, 5}
+		ss := []int{9, 8, 7, 6}
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertSameElements(t, s, ss)
+		})
+	})
+}
+
+func TestAssertNotSameElements(t *testing.T) {
+	t.Run("Structs", func(t *testing.T) {
+		// Test ten random structs for equality.
+		ss := make([]assertionTestStruct, 0)
+		for i := 0; i < 10; i++ {
+			s := generateStruct()
+			ss = append(ss, s)
+		}
+
+		ss2 := ss
+		ss2 = append(ss2, generateStruct())
+		AssertNotSameElements(t, ss, ss2)
+	})
+
+	t.Run("Strings", func(t *testing.T) {
+		s := []string{"Hello", "World"}
+		ss := []string{"World", "Hello", "Again"}
+		AssertNotSameElements(t, s, ss)
+	})
+
+	t.Run("Integers", func(t *testing.T) {
+		s := []int{1, 2, 3, 4, 5, 6, 7, 8}
+		ss := []int{9, 8, 7, 6, 5, 4, 3, 2, 1}
+		AssertNotSameElements(t, s, ss)
+	})
+}
+
+func TestAssertNotSameElementsFails(t *testing.T) {
+	t.Run("Structs", func(t *testing.T) {
+		// Test ten random structs for equality.
+		ss := make([]assertionTestStruct, 0)
+		for i := 0; i < 10; i++ {
+			s := generateStruct()
+			ss = append(ss, s)
+		}
+
+		ss2 := ss
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertNotSameElements(t, ss, ss2)
+		})
+	})
+
+	t.Run("Strings", func(t *testing.T) {
+		s := []string{"Hello", "World"}
+		ss := []string{"World", "Hello"}
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertNotSameElements(t, s, ss)
+		})
+	})
+
+	t.Run("Integers", func(t *testing.T) {
+		s := []int{1, 2, 3, 4, 5, 6, 7, 8}
+		ss := []int{8, 7, 6, 5, 4, 3, 2, 1}
+		AssertTestFails(t, func(t TestingPackageWithFailFunctions) {
+			AssertNotSameElements(t, s, ss)
+		})
+	})
+}