123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- package testutil
- import (
- "reflect"
- "testing"
- "github.com/stretchr/testify/require"
- )
- // EqualButNotSame asserts that expected and actual objects are not the same.
- // It recursively checks all fields to ensure that no pointers are shared.
- // If a pointer, slice or map are nil in either object, the test fails.
- func EqualButNotSame(t *testing.T, expected, actual any) {
- t.Helper()
- expectedVal := reflect.ValueOf(expected)
- actualVal := reflect.ValueOf(actual)
- deepEqual(t, expectedVal, actualVal, "")
- }
- // deepEqual recursively verifies that all values are equal but pointers are different
- // except for the Expires field which is explicitly allowed to be shared
- func deepEqual(t *testing.T, left, right reflect.Value, fieldPath string) {
- require.True(t, left.IsValid() && right.IsValid(), "invalid value at %s", fieldPath)
- require.Equal(t, left.Type(), right.Type(), "types are not equal at %s", fieldPath)
- switch left.Kind() {
- case reflect.Ptr:
- // Pointers should not be nil and must point to different objects
- require.False(t, left.IsNil(), "nil pointer at %s (left)", fieldPath)
- require.False(t, right.IsNil(), "nil pointer at %s (right)", fieldPath)
- require.NotSame(t, left.Interface(), right.Interface(), "shared pointer at %s", fieldPath)
- deepEqual(t, left.Elem(), right.Elem(), fieldPath)
- case reflect.Slice:
- // Slices should contain some elements and must not share the same underlying array
- require.Equal(t, left.Len(), right.Len(), "slice length mismatch at %s", fieldPath)
- require.NotEmpty(t, left.Len(), "slice must not be empty %s (left)", fieldPath)
- require.NotEmpty(t, right.Len(), "slice must not be empty %s (right)", fieldPath)
- require.NotEqual(t, left.Pointer(), right.Pointer(), "shared slices at %s", fieldPath)
- // Recursively verify slice elements
- for i := 0; i < left.Len(); i++ {
- elemPath := buildPath(fieldPath, "[", anyToString(i), "]")
- deepEqual(t, left.Index(i), right.Index(i), elemPath)
- }
- case reflect.Map:
- // Maps should contain some elements and must not share the same underlying map
- require.Equal(t, left.Len(), right.Len(), "map length mismatch at %s", fieldPath)
- require.NotEmpty(t, left.Len(), "map must not be empty %s (left)", fieldPath)
- require.NotEmpty(t, right.Len(), "map must not be empty %s (right)", fieldPath)
- require.NotEqual(t, left.Pointer(), right.Pointer(), "shared maps at %s", fieldPath)
- // Recursively verify map values
- for _, key := range left.MapKeys() {
- keyStr := anyToString(key.Interface())
- keyPath := buildPath(fieldPath, "[", keyStr, "]")
- originalMapVal := left.MapIndex(key)
- clonedMapVal := right.MapIndex(key)
- deepEqual(t, originalMapVal, clonedMapVal, keyPath)
- }
- case reflect.Struct:
- require.Equal(t, left.Interface(), right.Interface(), "structs are not equal at %s", fieldPath)
- // Fallback to recursive field-by-field comparison
- for i := 0; i < left.NumField(); i++ {
- field := left.Type().Field(i)
- if !field.IsExported() {
- continue // Skip unexported fields
- }
- nestedPath := buildPath(fieldPath, ".", field.Name, "")
- originalFieldVal := left.Field(i)
- clonedFieldVal := right.Field(i)
- deepEqual(t, originalFieldVal, clonedFieldVal, nestedPath)
- }
- default:
- // For primitive types, just verify equality
- require.Equal(t, left.Interface(), right.Interface(), "values not equal at %s", fieldPath)
- }
- }
- // buildPath builds a field path for error messages
- func buildPath(basePath, separator, element, suffix string) string {
- if basePath == "" {
- return element + suffix
- }
- return basePath + separator + element + suffix
- }
- // anyToString converts an any to a string for path building
- func anyToString(v any) string {
- switch val := v.(type) {
- case string:
- return val
- default:
- return reflect.ValueOf(val).String()
- }
- }
- // ........-------------------============++===============+=+==+++++++++++++++++++
- // .........------------------==-=====#%%%%%%%%%#+============++==+==++++++++++++++
- // .........--------------------===++#%##%%%##%#####===========+==+=++=++=+++++=+++
- // ..........-----------------=-=+#%#####+++=-=-++#%#===========+==+==++=+==++++=+
- // ..........-----------------==###+#####++=---.. .=++==============+===+=+=+=++=+
- // .........-.---------------=--+=-=####++==---.. -=================++=+==++=++
- // .. ........-.--------------==+. -#####+==---. ====================++==+++=
- // .. ......---.-----------==-==- .+#++++=----. =====================++==++=
- // ........--.-------------===-. -#%%%#+-=-=---. -=====================++==++
- // ...........------------==++%= .+#+=+%#+#.=-.-- =====================+==++=+
- // .........--------------===### ++=#%=%+#+ -+#+=. .--====================+==+==
- // ........----------------===%# +.+###%+#- =+##.=. -===================++=++++
- // ........---------------====## ++#%%##+#= -=- .==================++++++=++
- // .......----------------===--+ .+#%#++++#= .. =========+=====+++=++=++++++
- // .....----------------=-====-- -+==+%%#%#+- = ============+==++++=++==++=+
- // ..----------------==========- -- #%%#%#+=. =- ========+===+=++=++++++++++++
- // .----------------==-=========. ==-%#++#=-. =. ====+=====+==+==+++==++=++++++
- // .--------------=--============.=-#%@%%%#+++--= ===+==++==++=+=+==+++++++++++++
- // -------------=-================-=#####%#+- .- ==++++++=++=+++++++++++++++++++
- // -------------=================- -.+%##+-.+-. . .+=++=++++++=+++++++++++++++++
- // ---------==-================= = --.+%%#=. =+++++++++++++++++++++++++++
- // -------=---===============. #= =+-==#++++. . -++++++++++++++++++++++++
- // -----=-==============- .. +#= =%###++ .- .=+++++++++++++++++++
- // --==-===========- .-==.. -### --++. +. . .=+++++++++++++++
- // ============- .-=+##=++-. =#%# =+%##+ #=. =++++++++++
- // ========== = =##+###=+===- +%%#..%+%+-+# #+- -++++++++
- // ========= +==##%%##++-#++- ##=++=.+###%%% +++. .. . +++++++
- // ========-.%++%%%%#+=+-## ++==++++=-..# -= =+#- -... . ++##+##
- // ======++ -%+#%%%%#+=#=# =+++===+++. . +# -+%#- .---.......... ++##+#+
- // ======+. -%=#%%%%#=+#+. =%%#+++##++=. .=#+=#%+. ..--..-........ - -++++##+
- // +++++++-..%=#%%%%#=+#..###%%#+=+++=---+.=###%=. ..--.--...--... -. ++++#++
- // ++=++++.%#+=#%%%#+++ =++%%@%%###%##++=.-###%+-..-------.------... .=. ++##+#
- // +++++++.#%%=#%%%#+ .##%%%%%%%###%@@%##%=#%%#=-.--=-=---------... +=. =#####
- // ++++++ .%%%#+#### ==##%%%@%%#=%@@@%%###+#%%+=--==-===--------....-#=. +####
- // +++++= +%%%%-#- +#+#%%%@%%%#+%@@@@%%%%%%%%+==========--------..--#+--..... .####
- // ++++= -.#%%%- #++##%%%%%%%#=%%@@@%#%%%%%%#+==+============------=#=--.. .... ###
- // +++++ .#+%% .=%### +%%#%%@%=#%@@@%%%@@%%%+++++++================%+==--.. . . ##
- // ++++ .==. =+##%%#%%%%%%%@%=#%@@@@%%%%@@%++++++++=++===========#%++=----. +#
- // +++ .+#..=###%##%#@%#%#%%%%=##@@@%#=%%@%#######++++++++++=+++++%%#++=---..- =#
- // +==--=.+##%%%%+=++++++#@%%%+#%@@@@%+=+#%###%#####++++++++=++++#%%%#++=---..--.-#
- // +%%%-+=%#@@%%%+=#%+++#%@%%%##%%@@@@%%#=++#%%%%######+++++=++++%%%%##++===-. . +
- // .- --%%@#@@@%%+++#%##@@@@%%%%%%%######%%##+#%######++#+++++++#%%%%##++++###+= -
- // ..=++%@@%@@@%%%##%#%@@@@@%%%@@@@@%%%#++++++++%###+++###++++++@@%%%%##+==--==++#=
- // -+#%%%@@%@@@@%%%%%@@@@@@@@%@@%%%%%%%###++++++++#####+++++++++%@%%%%######+=--..-
- // +##%%%%@@@@@@@%%@@@@@@@@@@@@@@@@%%%##++#%#+++++++++++#####+++%@@@%%%#++=====++=-
- // #%#@%@%@@@@@%%@@@@@@@@@@@@@%%%%%%%@%%#%%#####++++#+====++++++%@@@@@@@@@%#+==----
- // ##%%@%%@@@@%@@@@@@@@@@@@@@@@@%%%%%%%%%%%#%%%%%###%+++======+++##+=+#+++##%%#%%%+
- // +#%%@@@%@@@%@@%+#@@@@@@@@@@@@%%%%@@%%%%%@%%%%%%%%@#+++++===-===#++==+++=+++#%#%+
- // #%%@@@%%@%%%%@#+#%@@@@%%%@@@@@@@%%%%@@%%%%@%%%%%%@#######+#+==+++=++=======+=++%
- // ##@@@@@%%%%@#++++#%%%%%%%%@@@@@@@@@@@@@@@@@@@%%@@@##########+=++++++#++++++###+#
- // ++#%@%%%%%#++++#+-###%%%%%%%@@%@@@@@@@@@@@@@@@@@@@##+##+####%#########+#########
- // +++++++#++++##+#+ ++####%%%%%%@%@@@@@@@@@@@@@@@%%%####%%####%%#####%#%%#####%%##
- // +#++#++++++++#+++ -+#+##%%%%@@%%@@@@@@@@@@@@@@@@@%####%%%%%%%%%%%%##%@@%%%%#%%%%
- // +#+++#+#####++++= .=++##%#%%%%%%%@@@@@@@@@@@@@@@%####%%%%@%%@%%%%%%%%%@@%@%%%%%#
- // ####+#+++++###+#-..=++##%%%%%%%#%@@@@@@@@@@@@@@@%####%%%%%%%%@%#################
- // #++#++##+#++#++#..-=+###%%%%%%#+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%#################
- // ++#+#++#+##++##=---=++####%%%###@@@%@@@@@@%%@@@@@@@@@@@@@@@@@%%+################
- // +##+##++#++#++#--=+=++###%#%####@@@@@@@@@@@@@@@%@@@%%@%%%%%%###=+###############
- // +++++##+#++#+++.-===+#####%%####@@@@@@@@%%@@%%@@%%%%%%########+==###############
- // +++#++++++#+++..--==+##########%%%@@@@@%%%%%@@%%%%########%##+#=-###############
- // +#++#++#+#++++..--===%#########%%%%%%%@%%%%%###%##############+==+##############
- // ++++##+++++#++.==--=+@%##+###+#%%%%%%%%%%%####%##%%#%%%#######++==##############
|