github.com/psiphon-labs/goarista@v0.0.0-20160825065156-d002785f4c67/test/diff.go (about)

     1  // Copyright (C) 2015  Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/aristanetworks/goarista/areflect"
    15  	"github.com/aristanetworks/goarista/key"
    16  )
    17  
    18  // diffable types have a method that returns the diff
    19  // of two objects
    20  type diffable interface {
    21  	// Diff returns a human readable string of the diff of the two objects
    22  	// an empty string means that the two objects are equal
    23  	Diff(other interface{}) string
    24  }
    25  
    26  // Diff returns the difference of two objects in a human readable format.
    27  // An empty string is returned when there is no difference.
    28  // To avoid confusing diffs, make sure you pass the expected value first.
    29  func Diff(expected, actual interface{}) string {
    30  	if DeepEqual(expected, actual) {
    31  		return ""
    32  	}
    33  
    34  	return diffImpl(expected, actual, nil)
    35  }
    36  
    37  func diffImpl(a, b interface{}, seen map[edge]struct{}) string {
    38  	av := reflect.ValueOf(a)
    39  	bv := reflect.ValueOf(b)
    40  	// Check if nil
    41  	if !av.IsValid() {
    42  		if !bv.IsValid() {
    43  			return "" // Both are "nil" with no type
    44  		}
    45  		return fmt.Sprintf("expected nil but got a %T: %#v", b, b)
    46  	} else if !bv.IsValid() {
    47  		return fmt.Sprintf("expected a %T (%#v) but got nil", a, a)
    48  	}
    49  	if av.Type() != bv.Type() {
    50  		return fmt.Sprintf("expected a %T but got a %T", a, b)
    51  	}
    52  
    53  	switch a := a.(type) {
    54  	case string, bool,
    55  		int8, int16, int32, int64,
    56  		uint8, uint16, uint32, uint64,
    57  		float32, float64,
    58  		complex64, complex128,
    59  		int, uint, uintptr:
    60  		if a != b {
    61  			typ := reflect.TypeOf(a).Name()
    62  			return fmt.Sprintf("%s(%v) != %s(%v)", typ, a, typ, b)
    63  		}
    64  		return ""
    65  	case []byte:
    66  		if !bytes.Equal(a, b.([]byte)) {
    67  			return fmt.Sprintf("[]byte(%q) != []byte(%q)", a, b)
    68  		}
    69  	}
    70  
    71  	if ac, ok := a.(diffable); ok {
    72  		return ac.Diff(b.(diffable))
    73  	}
    74  
    75  	if ac, ok := a.(key.Comparable); ok {
    76  		if ac.Equal(b.(key.Comparable)) {
    77  			return ""
    78  		}
    79  		return fmt.Sprintf("Comparable types are different: %s vs %s",
    80  			PrettyPrint(a), PrettyPrint(b))
    81  	}
    82  
    83  	switch av.Kind() {
    84  	case reflect.Array, reflect.Slice:
    85  		l := av.Len()
    86  		if l != bv.Len() {
    87  			return fmt.Sprintf("Expected an array of size %d but got %d",
    88  				l, bv.Len())
    89  		}
    90  		for i := 0; i < l; i++ {
    91  			diff := diffImpl(av.Index(i).Interface(), bv.Index(i).Interface(),
    92  				seen)
    93  			if len(diff) > 0 {
    94  				return fmt.Sprintf("In arrays, values are different at index %d: %s", i, diff)
    95  			}
    96  		}
    97  
    98  	case reflect.Map:
    99  		if c, d := isNilCheck(av, bv); c {
   100  			return d
   101  		}
   102  		if av.Len() != bv.Len() {
   103  			return fmt.Sprintf("Maps have different size: %d != %d (%s)",
   104  				av.Len(), bv.Len(), diffMapKeys(av, bv))
   105  		}
   106  		for _, ka := range av.MapKeys() {
   107  			ae := av.MapIndex(ka)
   108  			if k := ka.Kind(); k == reflect.Ptr || k == reflect.Interface {
   109  				return diffComplexKeyMap(av, bv, seen)
   110  			}
   111  			be := bv.MapIndex(ka)
   112  			if !be.IsValid() {
   113  				return fmt.Sprintf(
   114  					"key %s in map is missing in the actual map",
   115  					prettyPrint(ka, ptrSet{}, prettyPrintDepth))
   116  			}
   117  			if !ae.CanInterface() {
   118  				return fmt.Sprintf(
   119  					"for key %s in map, value can't become an interface: %s",
   120  					prettyPrint(ka, ptrSet{}, prettyPrintDepth),
   121  					prettyPrint(ae, ptrSet{}, prettyPrintDepth))
   122  			}
   123  			if !be.CanInterface() {
   124  				return fmt.Sprintf(
   125  					"for key %s in map, value can't become an interface: %s",
   126  					prettyPrint(ka, ptrSet{}, prettyPrintDepth),
   127  					prettyPrint(be, ptrSet{}, prettyPrintDepth))
   128  			}
   129  			if diff := diffImpl(ae.Interface(), be.Interface(), seen); len(diff) > 0 {
   130  				return fmt.Sprintf(
   131  					"for key %s in map, values are different: %s",
   132  					prettyPrint(ka, ptrSet{}, prettyPrintDepth), diff)
   133  			}
   134  		}
   135  
   136  	case reflect.Ptr, reflect.Interface:
   137  		if c, d := isNilCheck(av, bv); c {
   138  			return d
   139  		}
   140  		av = av.Elem()
   141  		bv = bv.Elem()
   142  
   143  		if av.CanAddr() && bv.CanAddr() {
   144  			e := edge{from: av.UnsafeAddr(), to: bv.UnsafeAddr()}
   145  			// Detect and prevent cycles.
   146  			if seen == nil {
   147  				seen = make(map[edge]struct{})
   148  			} else if _, ok := seen[e]; ok {
   149  				return ""
   150  			}
   151  			seen[e] = struct{}{}
   152  		}
   153  		return diffImpl(av.Interface(), bv.Interface(), seen)
   154  
   155  	case reflect.Struct:
   156  		typ := av.Type()
   157  		for i, n := 0, av.NumField(); i < n; i++ {
   158  			if typ.Field(i).Tag.Get("deepequal") == "ignore" {
   159  				continue
   160  			}
   161  			af := areflect.ForceExport(av.Field(i))
   162  			bf := areflect.ForceExport(bv.Field(i))
   163  			if diff := diffImpl(af.Interface(), bf.Interface(), seen); len(diff) > 0 {
   164  				return fmt.Sprintf("attributes %q are different: %s",
   165  					av.Type().Field(i).Name, diff)
   166  			}
   167  		}
   168  
   169  		// The following cases are here to handle named types (aka type aliases).
   170  	case reflect.String:
   171  		if as, bs := av.String(), bv.String(); as != bs {
   172  			return fmt.Sprintf("%s(%q) != %s(%q)", av.Type().Name(), as, bv.Type().Name(), bs)
   173  		}
   174  	case reflect.Bool:
   175  		if ab, bb := av.Bool(), bv.Bool(); ab != bb {
   176  			return fmt.Sprintf("%s(%t) != %s(%t)", av.Type().Name(), ab, bv.Type().Name(), bb)
   177  		}
   178  	case reflect.Uint, reflect.Uintptr,
   179  		reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   180  		if ai, bi := av.Uint(), bv.Uint(); ai != bi {
   181  			return fmt.Sprintf("%s(%d) != %s(%d)", av.Type().Name(), ai, bv.Type().Name(), bi)
   182  		}
   183  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   184  		if ai, bi := av.Int(), bv.Int(); ai != bi {
   185  			return fmt.Sprintf("%s(%d) != %s(%d)", av.Type().Name(), ai, bv.Type().Name(), bi)
   186  		}
   187  	case reflect.Float32, reflect.Float64:
   188  		if af, bf := av.Float(), bv.Float(); af != bf {
   189  			return fmt.Sprintf("%s(%f) != %s(%f)", av.Type().Name(), af, bv.Type().Name(), bf)
   190  		}
   191  	case reflect.Complex64, reflect.Complex128:
   192  		if ac, bc := av.Complex(), bv.Complex(); ac != bc {
   193  			return fmt.Sprintf("%s(%f) != %s(%f)", av.Type().Name(), ac, bv.Type().Name(), bc)
   194  		}
   195  
   196  	default:
   197  		return fmt.Sprintf("Unknown or unsupported type: %T: %#v", a, a)
   198  	}
   199  
   200  	return ""
   201  }
   202  
   203  func diffComplexKeyMap(av, bv reflect.Value, seen map[edge]struct{}) string {
   204  	ok, ka, be := complexKeyMapEqual(av, bv, seen)
   205  	if ok {
   206  		return ""
   207  	} else if be.IsValid() {
   208  		return fmt.Sprintf("for complex key %s in map, values are different: %s",
   209  			prettyPrint(ka, ptrSet{}, prettyPrintDepth),
   210  			diffImpl(av.MapIndex(ka).Interface(), be.Interface(), seen))
   211  	}
   212  	return fmt.Sprintf("complex key %s in map is missing in the actual map",
   213  		prettyPrint(ka, ptrSet{}, prettyPrintDepth))
   214  }
   215  
   216  func diffMapKeys(av, bv reflect.Value) string {
   217  	var diffs []string
   218  	// TODO: We produce extraneous diffs for composite keys.
   219  	for _, ka := range av.MapKeys() {
   220  		be := bv.MapIndex(ka)
   221  		if !be.IsValid() {
   222  			diffs = append(diffs, fmt.Sprintf("missing key: %s",
   223  				PrettyPrint(ka.Interface())))
   224  		}
   225  	}
   226  	for _, kb := range bv.MapKeys() {
   227  		ae := av.MapIndex(kb)
   228  		if !ae.IsValid() {
   229  			diffs = append(diffs, fmt.Sprintf("extra key: %s",
   230  				PrettyPrint(kb.Interface())))
   231  		}
   232  	}
   233  	sort.Strings(diffs)
   234  	return strings.Join(diffs, ", ")
   235  }
   236  
   237  func isNilCheck(a, b reflect.Value) (bool /*checked*/, string) {
   238  	if a.IsNil() {
   239  		if b.IsNil() {
   240  			return true, ""
   241  		}
   242  		return true, fmt.Sprintf("expected nil but got %s",
   243  			prettyPrint(b, ptrSet{}, prettyPrintDepth))
   244  	} else if b.IsNil() {
   245  		return true, fmt.Sprintf("got nil instead of %s",
   246  			prettyPrint(a, ptrSet{}, prettyPrintDepth))
   247  	}
   248  	return false, ""
   249  }
   250  
   251  type mapEntry struct {
   252  	k, v string
   253  }
   254  
   255  type mapEntries struct {
   256  	entries []*mapEntry
   257  }
   258  
   259  func (t *mapEntries) Len() int {
   260  	return len(t.entries)
   261  }
   262  func (t *mapEntries) Less(i, j int) bool {
   263  	if t.entries[i].k > t.entries[j].k {
   264  		return false
   265  	} else if t.entries[i].k < t.entries[j].k {
   266  		return true
   267  	}
   268  	return t.entries[i].v <= t.entries[j].v
   269  }
   270  func (t *mapEntries) Swap(i, j int) {
   271  	t.entries[i], t.entries[j] = t.entries[j], t.entries[i]
   272  }