Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

format for recursive object #8

Merged
merged 1 commit into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions format.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"
"sort"
"strings"
"unsafe"

"github.com/pmezard/go-difflib/difflib"
)
Expand Down Expand Up @@ -45,8 +46,8 @@ func (teq Teq) report(expected, actual any) string {
}

r, ok := richReport(
teq.format(ve, 0).diffSequence(),
teq.format(va, 0).diffSequence(),
teq.format(ve, make(map[fmtVisit]bool), 0).diffSequence(),
teq.format(va, make(map[fmtVisit]bool), 0).diffSequence(),
)
if !ok {
return simple
Expand Down Expand Up @@ -76,14 +77,35 @@ func richReport(a []string, b []string) (string, bool) {
}, "\n"), true
}

func (teq Teq) format(v reflect.Value, depth int) lines {
type fmtVisit struct {
a unsafe.Pointer
typ reflect.Type
}

func (teq Teq) format(v reflect.Value, visited map[fmtVisit]bool, depth int) lines {
if depth > teq.MaxDepth {
return linesOf("<max depth exceeded>")
}
if !v.IsValid() {
return linesOf("<invalid>")
}

if hard(v.Kind()) {
if v.CanAddr() {
addr := v.Addr().UnsafePointer()

// If references are already seen.
typ := v.Type()
v := fmtVisit{addr, typ}
if visited[v] {
return linesOf("<cyclic>")
}

// Remember for later.
visited[v] = true
}
}

ty := v.Type()
if fm, ok := teq.formats[ty]; ok {
return linesOf(fm(v))
Expand All @@ -94,7 +116,7 @@ func (teq Teq) format(v reflect.Value, depth int) lines {
fmtFn = todoFmt
}
next := func(v reflect.Value) lines {
return teq.format(v, depth+1)
return teq.format(v, visited, depth+1)
}
return fmtFn(v, next)
}
Expand Down
114 changes: 64 additions & 50 deletions teq_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import (
)

type test struct {
a any
b any
expected []string
pendingFormat bool // for development. we don't have stable format yet.
a any
b any
expected []string
}

type group struct {
Expand Down Expand Up @@ -50,13 +49,11 @@ func TestEqual(t *testing.T) {
t.Fatalf("expected %d errors, got %d", len(test.expected), len(mt.errors))
}

if !test.pendingFormat {
for i, e := range test.expected {
if mt.errors[i] != e {
t.Errorf("expected %q, got %q at i = %d", e, mt.errors[i], i)
}
assert.Equal(t, e, mt.errors[i])
for i, e := range test.expected {
if mt.errors[i] != e {
t.Errorf("expected %q, got %q at i = %d", e, mt.errors[i], i)
}
assert.Equal(t, e, mt.errors[i])
}

{
Expand All @@ -75,16 +72,16 @@ func TestEqual(t *testing.T) {

func primitives() []test {
return []test{
{1, 1, nil, false},
{1, 2, []string{"expected 1, got 2"}, false},
{uint8(1), uint8(1), nil, false},
{uint8(1), uint8(2), []string{"expected 1, got 2"}, false},
{1.5, 1.5, nil, false},
{1.5, 2.5, []string{"expected 1.5, got 2.5"}, false},
{"a", "a", nil, false},
{"a", "b", []string{"expected a, got b"}, false},
{1, 1, nil},
{1, 2, []string{"expected 1, got 2"}},
{uint8(1), uint8(1), nil},
{uint8(1), uint8(2), []string{"expected 1, got 2"}},
{1.5, 1.5, nil},
{1.5, 2.5, []string{"expected 1.5, got 2.5"}},
{"a", "a", nil},
{"a", "b", []string{"expected a, got b"}},

{"a", 1, []string{"expected a, got 1"}, false},
{"a", 1, []string{"expected a, got 1"}},
}
}

Expand All @@ -101,7 +98,7 @@ func structs() []test {
}

return []test{
{s{1}, s{1}, nil, false},
{s{1}, s{1}, nil},
{s{1}, s{2}, []string{`not equal
differences:
--- expected
Expand All @@ -111,17 +108,26 @@ differences:
- i: int(1),
+ i: int(2),
}
`}, false},
{s{1}, anotherS{1}, []string{"expected {1}, got {1}"}, false},
`}},
{s{1}, anotherS{1}, []string{"expected {1}, got {1}"}},

{withPointer{ref(1)}, withPointer{ref(1)}, nil, false},
{withPointer{ref(1)}, withPointer{ref(2)}, []string{"expected {1}, got {2}"}, true},
{withPointer{ref(1)}, withPointer{ref(1)}, nil},
{withPointer{ref(1)}, withPointer{ref(2)}, []string{`not equal
differences:
--- expected
+++ actual
@@ -1,3 +1,3 @@
teq_test.withPointer{
- i: *int(1),
+ i: *int(2),
}
`}},
}
}

func slices() []test {
return []test{
{[]int{1, 2}, []int{1, 2}, nil, false},
{[]int{1, 2}, []int{1, 2}, nil},
{[]int{1, 2}, []int{2, 1}, []string{`not equal
differences:
--- expected
Expand All @@ -132,7 +138,7 @@ differences:
int(1),
- int(2),
}
`}, false},
`}},
{io.Reader(bytes.NewBuffer([]byte("a"))), io.Reader(bytes.NewBuffer(nil)), []string{
`not equal
differences:
Expand All @@ -146,13 +152,13 @@ differences:
+ buf: []uint8{},
off: int(0),
`,
}, false},
}},
}
}

func maps() []test {
return []test{
{map[string]int{"a": 1}, map[string]int{"a": 1}, nil, false},
{map[string]int{"a": 1}, map[string]int{"a": 1}, nil},
{map[string]int{"a": 1}, map[string]int{"a": 2}, []string{`not equal
differences:
--- expected
Expand All @@ -162,7 +168,7 @@ differences:
- "a": int(1),
+ "a": int(2),
}
`}, false},
`}},
{map[string]int{"a": 1}, map[string]int{"b": 1}, []string{`not equal
differences:
--- expected
Expand All @@ -172,7 +178,7 @@ differences:
- "a": int(1),
+ "b": int(1),
}
`}, false},
`}},
{map[string]int{"a": 0}, map[string]int{}, []string{`not equal
differences:
--- expected
Expand All @@ -182,7 +188,7 @@ differences:
- "a": int(0),
-}
+map[string]int{}
`}, false},
`}},

{
map[int]map[string]int{
Expand All @@ -192,7 +198,6 @@ differences:
1: {"abc": 1},
},
nil,
false,
},
{
map[int]map[string]int{
Expand All @@ -211,7 +216,6 @@ differences:
+ "abc": int(2),
},
`},
false,
},
{
map[string]string{
Expand All @@ -238,7 +242,6 @@ differences:
+ "c": "10000",
"d": "4",
`},
false,
},
}
}
Expand All @@ -249,7 +252,6 @@ func interfaces() []test {
[]io.Reader{io.Reader(bytes.NewBuffer([]byte("a")))},
[]io.Reader{io.Reader(bytes.NewBuffer([]byte("a")))},
nil,
false,
},
{
[]io.Reader{
Expand Down Expand Up @@ -279,17 +281,17 @@ differences:
- }),
+ io.Reader(<nil>),
}
`}, false},
`}},
}
}

func channels() []test {
c1 := make(chan int)
c2 := make(chan int)
return []test{
{c1, c1, nil, false},
{c1, c2, []string{fmt.Sprintf("expected %p, got %p", c1, c2)}, false},
{[]chan int{c1}, []chan int{c1}, nil, false},
{c1, c1, nil},
{c1, c2, []string{fmt.Sprintf("expected %p, got %p", c1, c2)}},
{[]chan int{c1}, []chan int{c1}, nil},
{[]chan int{c1}, []chan int{c2}, []string{fmt.Sprintf(`not equal
differences:
--- expected
Expand All @@ -299,7 +301,7 @@ differences:
- chan int(%p),
+ chan int(%p),
}
`, c1, c2)}, false},
`, c1, c2)}},
{[]chan int{c1}, []chan int{nil}, []string{fmt.Sprintf(`not equal
differences:
--- expected
Expand All @@ -309,7 +311,7 @@ differences:
- chan int(%p),
+ chan int(<nil>),
}
`, c1)}, false},
`, c1)}},
}
}

Expand Down Expand Up @@ -346,17 +348,29 @@ func recursions() []test {
r3_2 = append(r3_2, 1, 2, r3_2)

return []test{
{r1_1, r1_1, nil, false},
{r1_1, r1_2, nil, false},
{r1_1, r1_3, []string{"expected {1, <cyclic>}, got {2, <cyclic>}"}, true},
{r1_4, r1_5, nil, false},
{r1_4, r1_6, nil, false},
{r1_1, r1_1, nil},
{r1_1, r1_2, nil},
{r1_1, r1_3, []string{`not equal
differences:
--- expected
+++ actual
@@ -1,5 +1,5 @@
teq_test.privateRecursiveStruct{
- i: int(1),
+ i: int(2),
r: *teq_test.privateRecursiveStruct{
- i: int(1),
+ i: int(2),
r: *<cyclic>,
`}},
{r1_4, r1_5, nil},
{r1_4, r1_6, nil},

{r2_1, r2_1, nil, false},
{r2_1, r2_2, nil, false},
{r2_1, r2_1, nil},
{r2_1, r2_2, nil},

{r3_1, r3_1, nil, false},
{r3_1, r3_2, nil, false},
{r3_1, r3_1, nil},
{r3_1, r3_2, nil},
}
}

Expand Down
Loading