Description
assert.EqualExportedValues recurses through a value's exported fields via copyExportedFields. When the value contains a pointer cycle (a struct that, directly or indirectly, points back to itself), copyExportedFields follows the cycle forever and never terminates. The result is a fatal error: stack overflow that aborts the whole test binary. Tested with testify v1.11.1 and Go 1.26.4.
Step To Reproduce
Run this test file:
package repro
import (
"testing"
"github.com/stretchr/testify/assert"
)
type Node struct {
Self *Node
}
func TestRecursive(t *testing.T) {
a := &Node{}
a.Self = a
b := &Node{}
b.Self = b
assert.EqualExportedValues(t, a, b)
}
Expected behavior
The assertion should detect the cycle and report the two values as equal.
assert.EqualExportedValues already compares by structural equality, recursive structures are no different:
Details
func TestChain(t *testing.T) {
a := &Node{Self: &Node{Self: &Node{}}}
b := &Node{Self: &Node{Self: &Node{}}}
assert.EqualExportedValues(t, a, b)
}
Actual behavior
copyExportedFields recurses on the self-referential pointer until the goroutine stack is exhausted, killing the entire test run:
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0x... stack=[...]
fatal error: stack overflow
goroutine 5 [running]:
internal/abi.Name.Name(...)
.../internal/abi/type.go:659 +0xc0 ...
reflect.(*structType).Field(...)
.../reflect/type.go:1189 +0x90 ...
reflect.(*rtype).Field(...)
.../reflect/type.go:777 +0x50 ...
github.com/stretchr/testify/assert.copyExportedFields(...)
.../testify@v1.11.1/assert/assertions.go:99 +0x42c ...
github.com/stretchr/testify/assert.copyExportedFields(...)
.../testify@v1.11.1/assert/assertions.go:114 +0x310 ...
github.com/stretchr/testify/assert.copyExportedFields(...)
.../testify@v1.11.1/assert/assertions.go:106 +0x538 ...
[copyExportedFields repeats until the stack is exhausted]
I'd be happy to take a stab at addressing this if your willing to review.
Description
assert.EqualExportedValuesrecurses through a value's exported fields viacopyExportedFields. When the value contains a pointer cycle (a struct that, directly or indirectly, points back to itself),copyExportedFieldsfollows the cycle forever and never terminates. The result is afatal error: stack overflowthat aborts the whole test binary. Tested with testify v1.11.1 and Go 1.26.4.Step To Reproduce
Run this test file:
Expected behavior
The assertion should detect the cycle and report the two values as equal.
assert.EqualExportedValuesalready compares by structural equality, recursive structures are no different:Details
Actual behavior
copyExportedFieldsrecurses on the self-referential pointer until the goroutine stack is exhausted, killing the entire test run:I'd be happy to take a stab at addressing this if your willing to review.