vendor: Update github.com/d4l3k/messagediff

This commit is contained in:
Jakob Borg 2016-09-13 21:50:14 +02:00
parent 58cbd19742
commit 77c0a19451
6 changed files with 202 additions and 113 deletions

View File

@ -1,65 +0,0 @@
# messagediff [![Build Status](https://travis-ci.org/d4l3k/messagediff.svg?branch=master)](https://travis-ci.org/d4l3k/messagediff) [![Coverage Status](https://coveralls.io/repos/github/d4l3k/messagediff/badge.svg?branch=master)](https://coveralls.io/github/d4l3k/messagediff?branch=master) [![GoDoc](https://godoc.org/github.com/d4l3k/messagediff?status.svg)](https://godoc.org/github.com/d4l3k/messagediff)
A library for doing diffs of arbitrary Golang structs.
If the unsafe package is available messagediff will diff unexported fields in
addition to exported fields. This is primarily used for testing purposes as it
allows for providing informative error messages.
## Example Usage
In a normal file:
```go
package main
import "github.com/d4l3k/messagediff"
type someStruct struct {
A, b int
C []int
}
func main() {
a := someStruct{1, 2, []int{1}}
b := someStruct{1, 3, []int{1, 2}}
diff, equal := messagediff.PrettyDiff(a, b)
/*
diff =
`added: .C[1] = 2
modified: .b = 3`
equal = false
*/
}
```
In a test:
```go
import "github.com/d4l3k/messagediff"
...
type someStruct struct {
A, b int
C []int
}
func TestSomething(t *testing.T) {
want := someStruct{1, 2, []int{1}}
got := someStruct{1, 3, []int{1, 2}}
if diff, equal := messagediff.PrettyDiff(want, got); !equal {
t.Errorf("Something() = %#v\n%s", got, diff)
}
}
```
See the `DeepDiff` function for using the diff results programmatically.
## License
Copyright (c) 2015 [Tristan Rice](https://fn.lc) <rice@fn.lc>
messagediff is licensed under the MIT license. See the LICENSE file for more information.
bypass.go and bypasssafe.go are borrowed from
[go-spew](https://github.com/davecgh/go-spew) and have a seperate copyright
notice.

View File

@ -0,0 +1,59 @@
package examples
import (
"fmt"
"github.com/d4l3k/messagediff"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
func ExampleAtom() {
got := data2()
want := data1()
diff, equal := messagediff.PrettyDiff(want, got)
fmt.Printf("%v %s", equal, diff)
// Output: false modified: [0].FirstChild.NextSibling.Attr = " baz"
}
func data1() []*html.Node {
n := &html.Node{
Type: html.ElementNode, Data: atom.Span.String(),
Attr: []html.Attribute{
{Key: atom.Class.String(), Val: "foo"},
},
}
n.AppendChild(
&html.Node{
Type: html.ElementNode, Data: atom.Span.String(),
Attr: []html.Attribute{
{Key: atom.Class.String(), Val: "bar"},
},
},
)
n.AppendChild(&html.Node{
Type: html.TextNode, Data: "baz",
})
return []*html.Node{n}
}
func data2() []*html.Node {
n := &html.Node{
Type: html.ElementNode, Data: atom.Span.String(),
Attr: []html.Attribute{
{Key: atom.Class.String(), Val: "foo"},
},
}
n.AppendChild(
&html.Node{
Type: html.ElementNode, Data: atom.Span.String(),
Attr: []html.Attribute{
{Key: atom.Class.String(), Val: "bar"},
},
},
)
n.AppendChild(&html.Node{
Type: html.TextNode, Data: " baz",
})
return []*html.Node{n}
}

View File

@ -0,0 +1 @@
package examples

View File

@ -5,6 +5,7 @@ import (
"reflect"
"sort"
"strings"
"unsafe"
)
// PrettyDiff does a deep comparison and returns the nicely formated results.
@ -26,43 +27,87 @@ func PrettyDiff(a, b interface{}) (string, bool) {
// DeepDiff does a deep comparison and returns the results.
func DeepDiff(a, b interface{}) (*Diff, bool) {
d := newdiff()
return d, diff(a, b, nil, d)
d := newDiff()
return d, d.diff(reflect.ValueOf(a), reflect.ValueOf(b), nil)
}
func newdiff() *Diff {
func newDiff() *Diff {
return &Diff{
Added: make(map[*Path]interface{}),
Removed: make(map[*Path]interface{}),
Modified: make(map[*Path]interface{}),
visited: make(map[visit]bool),
}
}
func diff(a, b interface{}, path Path, d *Diff) bool {
aVal := reflect.ValueOf(a)
bVal := reflect.ValueOf(b)
func (d *Diff) diff(aVal, bVal reflect.Value, path Path) bool {
// Validity checks. Should only trigger if nil is one of the original arguments.
if !aVal.IsValid() && !bVal.IsValid() {
// Both are nil.
return true
}
if !aVal.IsValid() || !bVal.IsValid() {
// One is nil and the other isn't.
d.Modified[&path] = b
if !bVal.IsValid() {
d.Modified[&path] = nil
return false
} else if !aVal.IsValid() {
d.Modified[&path] = bVal.Interface()
return false
}
if aVal.Type() != bVal.Type() {
d.Modified[&path] = b
d.Modified[&path] = bVal.Interface()
return false
}
kind := aVal.Type().Kind()
kind := aVal.Kind()
// Borrowed from the reflect package to handle recursive data structures.
hard := func(k reflect.Kind) bool {
switch k {
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
return true
}
return false
}
if aVal.CanAddr() && bVal.CanAddr() && hard(kind) {
addr1 := unsafe.Pointer(aVal.UnsafeAddr())
addr2 := unsafe.Pointer(bVal.UnsafeAddr())
if uintptr(addr1) > uintptr(addr2) {
// Canonicalize order to reduce number of entries in visited.
// Assumes non-moving garbage collector.
addr1, addr2 = addr2, addr1
}
// Short circuit if references are already seen.
typ := aVal.Type()
v := visit{addr1, addr2, typ}
if d.visited[v] {
return true
}
// Remember for later.
d.visited[v] = true
}
// End of borrowed code.
equal := true
switch kind {
case reflect.Map, reflect.Ptr, reflect.Func, reflect.Chan, reflect.Slice:
if aVal.IsNil() && bVal.IsNil() {
return true
}
if aVal.IsNil() || bVal.IsNil() {
d.Modified[&path] = bVal.Interface()
return false
}
}
switch kind {
case reflect.Array, reflect.Slice:
aLen := aVal.Len()
bLen := bVal.Len()
for i := 0; i < min(aLen, bLen); i++ {
localPath := append(path, SliceIndex(i))
if eq := diff(aVal.Index(i).Interface(), bVal.Index(i).Interface(), localPath, d); !eq {
if eq := d.diff(aVal.Index(i), bVal.Index(i), localPath); !eq {
equal = false
}
}
@ -87,7 +132,7 @@ func diff(a, b interface{}, path Path, d *Diff) bool {
if !bI.IsValid() {
d.Removed[&localPath] = aI.Interface()
equal = false
} else if eq := diff(aI.Interface(), bI.Interface(), localPath, d); !eq {
} else if eq := d.diff(aI, bI, localPath); !eq {
equal = false
}
}
@ -106,30 +151,19 @@ func diff(a, b interface{}, path Path, d *Diff) bool {
index := []int{i}
field := typ.FieldByIndex(index)
localPath := append(path, StructField(field.Name))
aI := unsafeReflectValue(aVal.FieldByIndex(index)).Interface()
bI := unsafeReflectValue(bVal.FieldByIndex(index)).Interface()
if eq := diff(aI, bI, localPath, d); !eq {
aI := unsafeReflectValue(aVal.FieldByIndex(index))
bI := unsafeReflectValue(bVal.FieldByIndex(index))
if eq := d.diff(aI, bI, localPath); !eq {
equal = false
}
}
case reflect.Ptr:
aVal = aVal.Elem()
bVal = bVal.Elem()
if !aVal.IsValid() && !bVal.IsValid() {
// Both are nil.
equal = true
} else if !aVal.IsValid() || !bVal.IsValid() {
// One is nil and the other isn't.
d.Modified[&path] = b
equal = false
} else {
equal = diff(aVal.Interface(), bVal.Interface(), path, d)
}
equal = d.diff(aVal.Elem(), bVal.Elem(), path)
default:
if reflect.DeepEqual(a, b) {
if reflect.DeepEqual(aVal.Interface(), bVal.Interface()) {
equal = true
} else {
d.Modified[&path] = b
d.Modified[&path] = bVal.Interface()
equal = false
}
}
@ -143,9 +177,21 @@ func min(a, b int) int {
return b
}
// During deepValueEqual, must keep track of checks that are
// in progress. The comparison algorithm assumes that all
// checks in progress are true when it reencounters them.
// Visited comparisons are stored in a map indexed by visit.
// This is borrowed from the reflect package.
type visit struct {
a1 unsafe.Pointer
a2 unsafe.Pointer
typ reflect.Type
}
// Diff represents a change in a struct.
type Diff struct {
Added, Removed, Modified map[*Path]interface{}
visited map[visit]bool
}
// Path represents a path to a changed datum.

View File

@ -8,14 +8,46 @@ import (
type testStruct struct {
A, b int
C []int
D [3]int
}
func TestPrettyDiff(t *testing.T) {
testData := []struct {
type RecursiveStruct struct {
Key int
Child *RecursiveStruct
}
func newRecursiveStruct(key int) *RecursiveStruct {
a := &RecursiveStruct{
Key: key,
}
b := &RecursiveStruct{
Key: key,
Child: a,
}
a.Child = b
return a
}
type testCase struct {
a, b interface{}
diff string
equal bool
}{
}
func checkTestCases(t *testing.T, testData []testCase) {
for i, td := range testData {
diff, equal := PrettyDiff(td.a, td.b)
if diff != td.diff {
t.Errorf("%d. PrettyDiff(%#v, %#v) diff = %#v; not %#v", i, td.a, td.b, diff, td.diff)
}
if equal != td.equal {
t.Errorf("%d. PrettyDiff(%#v, %#v) equal = %#v; not %#v", i, td.a, td.b, equal, td.equal)
}
}
}
func TestPrettyDiff(t *testing.T) {
testData := []testCase{
{
true,
false,
@ -59,8 +91,8 @@ func TestPrettyDiff(t *testing.T) {
false,
},
{
testStruct{1, 2, []int{1}},
testStruct{1, 3, []int{1, 2}},
testStruct{1, 2, []int{1}, [3]int{4, 5, 6}},
testStruct{1, 3, []int{1, 2}, [3]int{4, 5, 6}},
"added: .C[1] = 2\nmodified: .b = 3\n",
false,
},
@ -71,11 +103,17 @@ func TestPrettyDiff(t *testing.T) {
true,
},
{
&time.Time{},
&struct{}{},
nil,
"modified: = <nil>\n",
false,
},
{
nil,
&struct{}{},
"modified: = &struct {}{}\n",
false,
},
{
time.Time{},
time.Time{},
@ -89,15 +127,25 @@ func TestPrettyDiff(t *testing.T) {
false,
},
}
for i, td := range testData {
diff, equal := PrettyDiff(td.a, td.b)
if diff != td.diff {
t.Errorf("%d. PrettyDiff(%#v, %#v) diff = %#v; not %#v", i, td.a, td.b, diff, td.diff)
}
if equal != td.equal {
t.Errorf("%d. PrettyDiff(%#v, %#v) equal = %#v; not %#v", i, td.a, td.b, equal, td.equal)
checkTestCases(t, testData)
}
func TestPrettyDiffRecursive(t *testing.T) {
testData := []testCase{
{
newRecursiveStruct(1),
newRecursiveStruct(1),
"",
true,
},
{
newRecursiveStruct(1),
newRecursiveStruct(2),
"modified: .Child.Key = 2\nmodified: .Key = 2\n",
false,
},
}
checkTestCases(t, testData)
}
func TestPathString(t *testing.T) {

2
vendor/manifest vendored
View File

@ -121,7 +121,7 @@
{
"importpath": "github.com/d4l3k/messagediff",
"repository": "https://github.com/d4l3k/messagediff",
"revision": "bf29d7cd9038386a5b4a22e2d73c8fb20ae14602",
"revision": "7b706999d935b04cf2dbc71a5a5afcbd288aeb48",
"branch": "master"
},
{