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

migrate everything to unify diff.Diff method using cmp #70929

Merged
merged 2 commits into from
May 15, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions Godeps/LICENSES

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ replace (
github.com/google/btree => github.com/google/btree v0.0.0-20160524151835-7d79101e329e
github.com/google/cadvisor => github.com/google/cadvisor v0.33.2-0.20190411163913-9db8c7dee20a
github.com/google/certificate-transparency-go => github.com/google/certificate-transparency-go v1.0.21
github.com/google/go-cmp => github.com/google/go-cmp v0.2.0
github.com/google/go-cmp => github.com/google/go-cmp v0.3.0
github.com/google/gofuzz => github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
github.com/google/uuid => github.com/google/uuid v1.0.0
github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ github.com/google/cadvisor v0.33.2-0.20190411163913-9db8c7dee20a h1:4N3IykedbdXG
github.com/google/cadvisor v0.33.2-0.20190411163913-9db8c7dee20a/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
Expand Down
2 changes: 2 additions & 0 deletions staging/src/k8s.io/api/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion staging/src/k8s.io/apiextensions-apiserver/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions staging/src/k8s.io/apimachinery/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903
github.com/golang/protobuf v1.2.0
github.com/google/go-cmp v0.3.0
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
github.com/google/uuid v1.0.0
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d
Expand Down
2 changes: 2 additions & 0 deletions staging/src/k8s.io/apimachinery/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,12 @@ func (q *Quantity) Neg() {
q.d.Dec.Neg(q.d.Dec)
}

// Equal checks equality of two Quantities. This is useful for testing with
// cmp.Equal.
func (q Quantity) Equal(v Quantity) bool {
mikedanese marked this conversation as resolved.
Show resolved Hide resolved
return q.Cmp(v) == 0
}

// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
// of most Quantity values.
const int64QuantityExpectedBytes = 18
Expand Down
2 changes: 1 addition & 1 deletion staging/src/k8s.io/apimachinery/pkg/util/diff/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ go_library(
importmap = "k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/diff",
importpath = "k8s.io/apimachinery/pkg/util/diff",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
],
)

Expand Down
242 changes: 17 additions & 225 deletions staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@ package diff

import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"
"text/tabwriter"

"github.com/davecgh/go-spew/spew"

"k8s.io/apimachinery/pkg/util/validation/field"
"github.com/google/go-cmp/cmp"
)

// StringDiff diffs a and b and returns a human readable diff.
Expand All @@ -50,233 +46,29 @@ func StringDiff(a, b string) string {
return string(out)
}

// ObjectDiff writes the two objects out as JSON and prints out the identical part of
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
// For debugging tests.
func legacyDiff(a, b interface{}) string {
return cmp.Diff(a, b)
}

// ObjectDiff prints the diff of two go objects and fails if the objects
// contain unhandled unexported fields.
// DEPRECATED: use github.com/google/go-cmp/cmp.Diff
func ObjectDiff(a, b interface{}) string {
ab, err := json.Marshal(a)
if err != nil {
panic(fmt.Sprintf("a: %v", err))
}
bb, err := json.Marshal(b)
if err != nil {
panic(fmt.Sprintf("b: %v", err))
}
return StringDiff(string(ab), string(bb))
return legacyDiff(a, b)
}

// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
// which shows absolutely everything by recursing into every single pointer
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
// can't figure out why reflect.DeepEqual is returning false and nothing is
// showing you differences. This will.
// ObjectGoPrintDiff prints the diff of two go objects and fails if the objects
// contain unhandled unexported fields.
// DEPRECATED: use github.com/google/go-cmp/cmp.Diff
func ObjectGoPrintDiff(a, b interface{}) string {
s := spew.ConfigState{DisableMethods: true}
return StringDiff(
s.Sprintf("%#v", a),
s.Sprintf("%#v", b),
)
return legacyDiff(a, b)
}

// ObjectReflectDiff returns a multi-line formatted diff between two objects
// of equal type. If an object with private fields is passed you will
// only see string comparison for those fields. Otherwise this presents the
// most human friendly diff of two structs of equal type in this package.
// ObjectReflectDiff prints the diff of two go objects and fails if the objects
// contain unhandled unexported fields.
// DEPRECATED: use github.com/google/go-cmp/cmp.Diff
func ObjectReflectDiff(a, b interface{}) string {
if a == nil && b == nil {
return "<no diffs>"
}
if a == nil {
return fmt.Sprintf("a is nil and b is not-nil")
}
if b == nil {
return fmt.Sprintf("a is not-nil and b is nil")
}
vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
if vA.Type() != vB.Type() {
return fmt.Sprintf("type A %T and type B %T do not match", a, b)
}
diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
if len(diffs) == 0 {
return "<no diffs>"
}
out := []string{""}
for _, d := range diffs {
elidedA, elidedB := limit(d.a, d.b, 80)
out = append(out,
fmt.Sprintf("%s:", d.path),
fmt.Sprintf(" a: %s", elidedA),
fmt.Sprintf(" b: %s", elidedB),
)
}
return strings.Join(out, "\n")
}

// limit:
// 1. stringifies aObj and bObj
// 2. elides identical prefixes if either is too long
// 3. elides remaining content from the end if either is too long
func limit(aObj, bObj interface{}, max int) (string, string) {
elidedPrefix := ""
elidedASuffix := ""
elidedBSuffix := ""
a, b := fmt.Sprintf("%#v", aObj), fmt.Sprintf("%#v", bObj)

if aObj != nil && bObj != nil {
if aType, bType := fmt.Sprintf("%T", aObj), fmt.Sprintf("%T", bObj); aType != bType {
a = fmt.Sprintf("%s (%s)", a, aType)
b = fmt.Sprintf("%s (%s)", b, bType)
}
}

for {
switch {
case len(a) > max && len(a) > 4 && len(b) > 4 && a[:4] == b[:4]:
// a is too long, b has data, and the first several characters are the same
elidedPrefix = "..."
a = a[2:]
b = b[2:]

case len(b) > max && len(b) > 4 && len(a) > 4 && a[:4] == b[:4]:
// b is too long, a has data, and the first several characters are the same
elidedPrefix = "..."
a = a[2:]
b = b[2:]

case len(a) > max:
a = a[:max]
elidedASuffix = "..."

case len(b) > max:
b = b[:max]
elidedBSuffix = "..."

default:
// both are short enough
return elidedPrefix + a + elidedASuffix, elidedPrefix + b + elidedBSuffix
}
}
}

func public(s string) bool {
if len(s) == 0 {
return false
}
return s[:1] == strings.ToUpper(s[:1])
}

type diff struct {
path *field.Path
a, b interface{}
}

type orderedDiffs []diff

func (d orderedDiffs) Len() int { return len(d) }
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d orderedDiffs) Less(i, j int) bool {
a, b := d[i].path.String(), d[j].path.String()
if a < b {
return true
}
return false
}

func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
switch a.Type().Kind() {
case reflect.Struct:
var changes []diff
for i := 0; i < a.Type().NumField(); i++ {
if !public(a.Type().Field(i).Name) {
if reflect.DeepEqual(a.Interface(), b.Interface()) {
continue
}
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
}
if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
changes = append(changes, sub...)
}
}
return changes
case reflect.Ptr, reflect.Interface:
if a.IsNil() || b.IsNil() {
switch {
case a.IsNil() && b.IsNil():
return nil
case a.IsNil():
return []diff{{path: path, a: nil, b: b.Interface()}}
default:
return []diff{{path: path, a: a.Interface(), b: nil}}
}
}
return objectReflectDiff(path, a.Elem(), b.Elem())
case reflect.Chan:
if !reflect.DeepEqual(a.Interface(), b.Interface()) {
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
return nil
case reflect.Slice:
lA, lB := a.Len(), b.Len()
l := lA
if lB < lA {
l = lB
}
if lA == lB && lA == 0 {
if a.IsNil() != b.IsNil() {
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
return nil
}
var diffs []diff
for i := 0; i < l; i++ {
if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
}
}
for i := l; i < lA; i++ {
diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
}
for i := l; i < lB; i++ {
diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
}
return diffs
case reflect.Map:
if reflect.DeepEqual(a.Interface(), b.Interface()) {
return nil
}
aKeys := make(map[interface{}]interface{})
for _, key := range a.MapKeys() {
aKeys[key.Interface()] = a.MapIndex(key).Interface()
}
var missing []diff
for _, key := range b.MapKeys() {
if _, ok := aKeys[key.Interface()]; ok {
delete(aKeys, key.Interface())
if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
continue
}
missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
continue
}
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
}
for key, value := range aKeys {
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
}
if len(missing) == 0 {
missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
}
sort.Sort(orderedDiffs(missing))
return missing
default:
if reflect.DeepEqual(a.Interface(), b.Interface()) {
return nil
}
if !a.CanInterface() {
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
}
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
return legacyDiff(a, b)
}

// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
Expand Down