Skip to content

Commit

Permalink
format for recursive object (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
seiyab authored May 5, 2024
1 parent 6f4df62 commit 9a86a68
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 54 deletions.
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

0 comments on commit 9a86a68

Please sign in to comment.