2 Copyright 2014 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
28 "github.com/davecgh/go-spew/spew"
30 "k8s.io/apimachinery/pkg/util/validation/field"
33 // StringDiff diffs a and b and returns a human readable diff.
34 func StringDiff(a, b string) string {
39 for ; i < len(ba) && i < len(bb); i++ {
43 out = append(out, ba[i])
45 out = append(out, []byte("\n\nA: ")...)
46 out = append(out, ba[i:]...)
47 out = append(out, []byte("\n\nB: ")...)
48 out = append(out, bb[i:]...)
49 out = append(out, []byte("\n\n")...)
53 // ObjectDiff writes the two objects out as JSON and prints out the identical part of
54 // the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
55 // For debugging tests.
56 func ObjectDiff(a, b interface{}) string {
57 ab, err := json.Marshal(a)
59 panic(fmt.Sprintf("a: %v", err))
61 bb, err := json.Marshal(b)
63 panic(fmt.Sprintf("b: %v", err))
65 return StringDiff(string(ab), string(bb))
68 // ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
69 // which shows absolutely everything by recursing into every single pointer
70 // (go's %#v formatters OTOH stop at a certain point). This is needed when you
71 // can't figure out why reflect.DeepEqual is returning false and nothing is
72 // showing you differences. This will.
73 func ObjectGoPrintDiff(a, b interface{}) string {
74 s := spew.ConfigState{DisableMethods: true}
81 func ObjectReflectDiff(a, b interface{}) string {
82 vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
83 if vA.Type() != vB.Type() {
84 return fmt.Sprintf("type A %T and type B %T do not match", a, b)
86 diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
91 for _, d := range diffs {
92 elidedA, elidedB := limit(d.a, d.b, 80)
94 fmt.Sprintf("%s:", d.path),
95 fmt.Sprintf(" a: %s", elidedA),
96 fmt.Sprintf(" b: %s", elidedB),
99 return strings.Join(out, "\n")
103 // 1. stringifies aObj and bObj
104 // 2. elides identical prefixes if either is too long
105 // 3. elides remaining content from the end if either is too long
106 func limit(aObj, bObj interface{}, max int) (string, string) {
110 a, b := fmt.Sprintf("%#v", aObj), fmt.Sprintf("%#v", bObj)
112 if aObj != nil && bObj != nil {
113 if aType, bType := fmt.Sprintf("%T", aObj), fmt.Sprintf("%T", bObj); aType != bType {
114 a = fmt.Sprintf("%s (%s)", a, aType)
115 b = fmt.Sprintf("%s (%s)", b, bType)
121 case len(a) > max && len(a) > 4 && len(b) > 4 && a[:4] == b[:4]:
122 // a is too long, b has data, and the first several characters are the same
127 case len(b) > max && len(b) > 4 && len(a) > 4 && a[:4] == b[:4]:
128 // b is too long, a has data, and the first several characters are the same
135 elidedASuffix = "..."
139 elidedBSuffix = "..."
142 // both are short enough
143 return elidedPrefix + a + elidedASuffix, elidedPrefix + b + elidedBSuffix
148 func public(s string) bool {
152 return s[:1] == strings.ToUpper(s[:1])
160 type orderedDiffs []diff
162 func (d orderedDiffs) Len() int { return len(d) }
163 func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
164 func (d orderedDiffs) Less(i, j int) bool {
165 a, b := d[i].path.String(), d[j].path.String()
172 func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
173 switch a.Type().Kind() {
176 for i := 0; i < a.Type().NumField(); i++ {
177 if !public(a.Type().Field(i).Name) {
178 if reflect.DeepEqual(a.Interface(), b.Interface()) {
181 return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
183 if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
184 changes = append(changes, sub...)
188 case reflect.Ptr, reflect.Interface:
189 if a.IsNil() || b.IsNil() {
191 case a.IsNil() && b.IsNil():
194 return []diff{{path: path, a: nil, b: b.Interface()}}
196 return []diff{{path: path, a: a.Interface(), b: nil}}
199 return objectReflectDiff(path, a.Elem(), b.Elem())
201 if !reflect.DeepEqual(a.Interface(), b.Interface()) {
202 return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
206 lA, lB := a.Len(), b.Len()
211 if lA == lB && lA == 0 {
212 if a.IsNil() != b.IsNil() {
213 return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
218 for i := 0; i < l; i++ {
219 if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
220 diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
223 for i := l; i < lA; i++ {
224 diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
226 for i := l; i < lB; i++ {
227 diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
231 if reflect.DeepEqual(a.Interface(), b.Interface()) {
234 aKeys := make(map[interface{}]interface{})
235 for _, key := range a.MapKeys() {
236 aKeys[key.Interface()] = a.MapIndex(key).Interface()
239 for _, key := range b.MapKeys() {
240 if _, ok := aKeys[key.Interface()]; ok {
241 delete(aKeys, key.Interface())
242 if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
245 missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
248 missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
250 for key, value := range aKeys {
251 missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
253 if len(missing) == 0 {
254 missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
256 sort.Sort(orderedDiffs(missing))
259 if reflect.DeepEqual(a.Interface(), b.Interface()) {
262 if !a.CanInterface() {
263 return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
265 return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
269 // ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
270 // enabling easy visual scanning for mismatches.
271 func ObjectGoPrintSideBySide(a, b interface{}) string {
272 s := spew.ConfigState{
275 DisableMethods: true,
280 linesA := strings.Split(sA, "\n")
281 linesB := strings.Split(sB, "\n")
283 for _, s := range linesA {
289 for _, s := range linesB {
295 buf := &bytes.Buffer{}
296 w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
298 if len(linesB) > max {
301 for i := 0; i < max; i++ {
309 fmt.Fprintf(w, "%s\t%s\n", a, b)