From fe1ef96bb9e70038bdf357d9c9c63baed279f749 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Fri, 21 Apr 2023 23:13:08 +0200 Subject: [PATCH 1/3] ObjectsExportedFieldsAreEqual: Handle nested pointer fields --- assert/assertions.go | 17 +++++++++++++---- assert/assertions_test.go | 9 +++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index b362b4a29..b589dc7d3 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -102,11 +102,20 @@ func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { field := expectedType.Field(i) isExported := field.PkgPath == "" // should use field.IsExported() but it's not available in Go 1.16.5 if isExported { + expectedField := expectedValue.Field(i) + actualField := actualValue.Field(i) + var equal bool - if field.Type.Kind() == reflect.Struct { - equal = ObjectsExportedFieldsAreEqual(expectedValue.Field(i).Interface(), actualValue.Field(i).Interface()) - } else { - equal = ObjectsAreEqualValues(expectedValue.Field(i).Interface(), actualValue.Field(i).Interface()) + switch field.Type.Kind() { + case reflect.Struct: + equal = ObjectsExportedFieldsAreEqual(expectedField.Interface(), actualField.Interface()) + case reflect.Ptr: + if expectedField.IsNil() || actualField.IsNil() { + return expectedField == actualField + } + equal = ObjectsExportedFieldsAreEqual(expectedField.Elem().Interface(), actualField.Elem().Interface()) + default: + equal = ObjectsAreEqualValues(expectedField.Interface(), actualField.Interface()) } if !equal { diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 5eaf2af8d..3dd4ed0d2 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -166,6 +166,10 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { foo interface{} } + type S3 struct { + ExportedPointer *Nested + } + cases := []struct { expected interface{} actual interface{} @@ -180,6 +184,11 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{"a", 3}, 4, Nested{5, 6}}, false}, {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S2{1}, false}, {1, S{1, Nested{2, 3}, 4, Nested{5, 6}}, false}, + {S3{&Nested{2, 3}}, S3{&Nested{2, 3}}, true}, + {S3{&Nested{2, 3}}, S3{&Nested{2, 4}}, true}, + {S3{&Nested{2, 3}}, S3{&Nested{"a", 3}}, false}, + {S3{&Nested{2, 3}}, S3{}, false}, + {S3{}, S3{}, true}, } for _, c := range cases { From 6ed14c65871aad7e190043de4897c696e439adbf Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Sat, 22 Apr 2023 09:30:02 +0200 Subject: [PATCH 2/3] Refactor to handle more pointer cases --- assert/assertions.go | 47 ++++++++++++++++++++------------------- assert/assertions_test.go | 21 ++++++++++++----- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index b589dc7d3..a4410d0f4 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -86,44 +86,45 @@ func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { expectedType := reflect.TypeOf(expected) actualType := reflect.TypeOf(actual) - if expectedType != actualType { return false } - if expectedType.Kind() != reflect.Struct || actualType.Kind() != reflect.Struct { + expectedKind := expectedType.Kind() + actualKind := actualType.Kind() + if expectedKind != actualKind { return false } expectedValue := reflect.ValueOf(expected) actualValue := reflect.ValueOf(actual) - for i := 0; i < expectedType.NumField(); i++ { - field := expectedType.Field(i) - isExported := field.PkgPath == "" // should use field.IsExported() but it's not available in Go 1.16.5 - if isExported { - expectedField := expectedValue.Field(i) - actualField := actualValue.Field(i) - - var equal bool - switch field.Type.Kind() { - case reflect.Struct: - equal = ObjectsExportedFieldsAreEqual(expectedField.Interface(), actualField.Interface()) - case reflect.Ptr: - if expectedField.IsNil() || actualField.IsNil() { - return expectedField == actualField + switch expectedKind { + case reflect.Struct: + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.PkgPath == "" // should use field.IsExported() but it's not available in Go 1.16.5 + if isExported { + expectedField := expectedValue.Field(i) + actualField := actualValue.Field(i) + equal := ObjectsExportedFieldsAreEqual(expectedField.Interface(), actualField.Interface()) + if !equal { + return false } - equal = ObjectsExportedFieldsAreEqual(expectedField.Elem().Interface(), actualField.Elem().Interface()) - default: - equal = ObjectsAreEqualValues(expectedField.Interface(), actualField.Interface()) } + } + return true - if !equal { - return false - } + case reflect.Interface, reflect.Ptr: + if expectedValue.IsNil() || actualValue.IsNil() { + return ObjectsAreEqualValues(expectedValue.Interface(), actualValue.Interface()) + } else { + return ObjectsExportedFieldsAreEqual(expectedValue.Elem().Interface(), actualValue.Elem().Interface()) } + + default: + return ObjectsAreEqualValues(expectedValue.Interface(), actualValue.Interface()) } - return true } // ObjectsAreEqualValues gets whether two objects are equal, or if their diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 3dd4ed0d2..d2619769c 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -167,9 +167,12 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { } type S3 struct { - ExportedPointer *Nested + Exported1 *Nested + Exported2 *Nested } + intValue := 1 + cases := []struct { expected interface{} actual interface{} @@ -182,13 +185,21 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, "a"}, 4, Nested{5, 6}}, true}, {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{"a", Nested{2, 3}, 4, Nested{5, 6}}, false}, {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{"a", 3}, 4, Nested{5, 6}}, false}, + {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S2{1}, false}, {1, S{1, Nested{2, 3}, 4, Nested{5, 6}}, false}, - {S3{&Nested{2, 3}}, S3{&Nested{2, 3}}, true}, - {S3{&Nested{2, 3}}, S3{&Nested{2, 4}}, true}, - {S3{&Nested{2, 3}}, S3{&Nested{"a", 3}}, false}, - {S3{&Nested{2, 3}}, S3{}, false}, + + {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, 2}, &Nested{3, 4}}, true}, + {S3{nil, &Nested{3, 4}}, S3{nil, &Nested{3, 4}}, true}, + {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, 2}, &Nested{3, "b"}}, true}, + {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{1, "a"}, &Nested{3, "b"}}, true}, + {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{"a", 2}, &Nested{3, 4}}, false}, + {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{}, false}, {S3{}, S3{}, true}, + + {Nested{&intValue, 2}, Nested{&intValue, 2}, true}, + {Nested{&Nested{1, 2}, 3}, Nested{&Nested{1, "b"}, 3}, true}, + {Nested{&Nested{1, 2}, 3}, Nested{nil, 3}, false}, } for _, c := range cases { From 1f000b26edd9da5e66d33c6e70e9f17c863bce14 Mon Sep 17 00:00:00 2001 From: Harald Nordgren Date: Sat, 22 Apr 2023 15:21:04 +0200 Subject: [PATCH 3/3] Handle slices as well --- assert/assertions.go | 29 ++++++++++++++++++++--------- assert/assertions_test.go | 9 ++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index a4410d0f4..aa9478c11 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -80,7 +80,7 @@ func ObjectsAreEqual(expected, actual interface{}) bool { // // This function does no assertion of any kind. func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { - if expected == nil || actual == nil { + if isNil(expected) || isNil(actual) { return expected == actual } @@ -105,10 +105,9 @@ func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { field := expectedType.Field(i) isExported := field.PkgPath == "" // should use field.IsExported() but it's not available in Go 1.16.5 if isExported { - expectedField := expectedValue.Field(i) - actualField := actualValue.Field(i) - equal := ObjectsExportedFieldsAreEqual(expectedField.Interface(), actualField.Interface()) - if !equal { + expectedElem := expectedValue.Field(i).Interface() + actualElem := actualValue.Field(i).Interface() + if !ObjectsExportedFieldsAreEqual(expectedElem, actualElem) { return false } } @@ -116,11 +115,23 @@ func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { return true case reflect.Interface, reflect.Ptr: - if expectedValue.IsNil() || actualValue.IsNil() { - return ObjectsAreEqualValues(expectedValue.Interface(), actualValue.Interface()) - } else { - return ObjectsExportedFieldsAreEqual(expectedValue.Elem().Interface(), actualValue.Elem().Interface()) + expectedElem := expectedValue.Elem().Interface() + actualElem := actualValue.Elem().Interface() + return ObjectsExportedFieldsAreEqual(expectedElem, actualElem) + + case reflect.Array, reflect.Slice: + expectedLen := expectedValue.Len() + if expectedLen != actualValue.Len() { + return false } + for i := 0; i < expectedLen; i++ { + expectedElem := expectedValue.Index(i).Elem().Interface() + actualElem := actualValue.Index(i).Elem().Interface() + if !ObjectsExportedFieldsAreEqual(expectedElem, actualElem) { + return false + } + } + return true default: return ObjectsAreEqualValues(expectedValue.Interface(), actualValue.Interface()) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index d2619769c..22d7fae7a 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -171,6 +171,10 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { Exported2 *Nested } + type S4 struct { + Exported1 []*Nested + } + intValue := 1 cases := []struct { @@ -185,7 +189,6 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, "a"}, 4, Nested{5, 6}}, true}, {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{"a", Nested{2, 3}, 4, Nested{5, 6}}, false}, {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{"a", 3}, 4, Nested{5, 6}}, false}, - {S{1, Nested{2, 3}, 4, Nested{5, 6}}, S2{1}, false}, {1, S{1, Nested{2, 3}, 4, Nested{5, 6}}, false}, @@ -197,6 +200,10 @@ func TestObjectsExportedFieldsAreEqual(t *testing.T) { {S3{&Nested{1, 2}, &Nested{3, 4}}, S3{}, false}, {S3{}, S3{}, true}, + {S4{[]*Nested{{1, 2}}}, S4{[]*Nested{{1, 2}}}, true}, + {S4{[]*Nested{{1, 2}}}, S4{[]*Nested{{1, 3}}}, true}, + {S4{[]*Nested{{1, 2}, {3, 4}}}, S4{[]*Nested{{1, "a"}, {3, "b"}}}, true}, + {Nested{&intValue, 2}, Nested{&intValue, 2}, true}, {Nested{&Nested{1, 2}, 3}, Nested{&Nested{1, "b"}, 3}, true}, {Nested{&Nested{1, 2}, 3}, Nested{nil, 3}, false},