Repository: MarvinJWendt/testza
Pull request #71 (2022-01-16 21:52:38)

Added `AssertSameElements`
Cleaned code


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd87c8f..72eb6fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,16 @@
 <a name="unreleased"></a>
 ## [Unreleased]
 
+### Features
+- proposal AssertSameElements
+
 ### Bug Fixes
 - **assert:** fixed missing custom messages in some functions
 
+### Code Refactoring
+- **assert:** clean code
+- **assert:** made output message for `AssertSameElements` more fluent
+
 
 <a name="v0.2.12"></a>
 ## [v0.2.12] - 2021-11-09
diff --git a/README.md b/README.md
index 71aebe3..56bd5c6 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
 </a>
 
 <a href="https://codecov.io/gh/MarvinJWendt/testza">
-<!-- unittestcount:start --><img src="https://img.shields.io/badge/Unit_Tests-2757-magenta?style=flat-square" alt="Unit test count"><!-- unittestcount:end -->
+<!-- unittestcount:start --><img src="https://img.shields.io/badge/Unit_Tests-3120-magenta?style=flat-square" alt="Unit test count"><!-- unittestcount:end -->
 </a>
   
 <a href="https://pkg.go.dev/github.com/MarvinJWendt/testza" target="_blank">
@@ -158,10 +158,12 @@ testza.MockStringRunTests(t, emailAddresses, func(t *testing.T, index int, str s
   - [AssertNotNumeric](https://github.com/MarvinJWendt/testza#AssertNotNumeric)
   - [AssertNotPanics](https://github.com/MarvinJWendt/testza#AssertNotPanics)
   - [AssertNotRegexp](https://github.com/MarvinJWendt/testza#AssertNotRegexp)
+  - [AssertNotSameElements](https://github.com/MarvinJWendt/testza#AssertNotSameElements)
   - [AssertNotZero](https://github.com/MarvinJWendt/testza#AssertNotZero)
   - [AssertNumeric](https://github.com/MarvinJWendt/testza#AssertNumeric)
   - [AssertPanics](https://github.com/MarvinJWendt/testza#AssertPanics)
   - [AssertRegexp](https://github.com/MarvinJWendt/testza#AssertRegexp)
+  - [AssertSameElements](https://github.com/MarvinJWendt/testza#AssertSameElements)
   - [AssertTestFails](https://github.com/MarvinJWendt/testza#AssertTestFails)
   - [AssertTrue](https://github.com/MarvinJWendt/testza#AssertTrue)
   - [AssertZero](https://github.com/MarvinJWendt/testza#AssertZero)
@@ -762,6 +764,25 @@ Example:
 
     testza.AssertNotRegexp(t, "ab.*", "Hello, World!")
 
+#### AssertNotSameElements
+
+```go
+func AssertNotSameElements(t testRunner, expected interface{}, actual interface{}, msg ...interface{})
+```
+
+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"}})
+
 #### AssertNotZero
 
 ```go
@@ -819,6 +840,26 @@ Example:
 
     testza.AssertRegexp(t, "^a.*c$", "abc")
 
+#### AssertSameElements
+
+```go
+func AssertSameElements(t testRunner, expected interface{}, actual interface{}, msg ...interface{})
+```
+
+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"}})
+
 #### AssertTestFails
 
 ```go
diff --git a/assert.go b/assert.go
index 83c8087..398007b 100644
--- a/assert.go
+++ b/assert.go
@@ -52,8 +52,6 @@ func (m *testMock) Fatalf(format string, args ...interface{}) {
 	m.fail(fmt.Sprintf(format, args...))
 }
 
-// ** Helper Methods **
-
 // AssertKindOf asserts that the object is a type of kind exptectedKind.
 //
 // Example:
@@ -775,3 +773,46 @@ func AssertDirNotEmpty(t testRunner, dir string, msg ...interface{}) {
 		internal.Fail(t, "The directory !!is!! empty.", internal.NewObjectsSingleNamed("Directory", dir), 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 !internal.HasSameElements(expected, actual) {
+		internal.Fail(t, "Two objects that !!should have the same elements!!, do not have the 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 internal.HasSameElements(expected, actual) {
+		internal.Fail(t, "Two objects that !!should have the same elements!!, do not have the same elements.", internal.NewObjectsExpectedActual(expected, actual), msg...)
+	}
+}
diff --git a/internal/assertion_helper.go b/internal/assertion_helper.go
index 0cf5858..c5fb4bb 100644
--- a/internal/assertion_helper.go
+++ b/internal/assertion_helper.go
@@ -277,3 +277,62 @@ func AssertDirEmptyHelper(t testRunner, dir string) bool {
 	_, err = f.Readdirnames(1)
 	return errors.Is(err, io.EOF)
 }
+
+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
+}


Test patch:
diff --git a/assert_test.go b/assert_test.go
index a14694d..3742d36 100644
--- a/assert_test.go
+++ b/assert_test.go
@@ -927,6 +927,156 @@ 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)
+		})
+	})
+}
+
 func TestAssertRegexp(t *testing.T) {
 	AssertRegexp(t, "p([a-z]+)ch", "peache")
 	rxp, err := regexp.Compile("Hello, .*")