SEBA-949 support for publishing bbsim events on kafka
Change-Id: I4354cd026bbadc801e4d6d08b2f9cd3462917b4c
diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go
index 7e215f2..c9a63ce 100644
--- a/vendor/github.com/google/go-cmp/cmp/compare.go
+++ b/vendor/github.com/google/go-cmp/cmp/compare.go
@@ -22,33 +22,24 @@
// equality is determined by recursively comparing the primitive kinds on both
// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
// fields are not compared by default; they result in panics unless suppressed
-// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
-// using the AllowUnexported option.
+// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly
+// compared using the Exporter option.
package cmp
import (
"fmt"
"reflect"
+ "strings"
"github.com/google/go-cmp/cmp/internal/diff"
+ "github.com/google/go-cmp/cmp/internal/flags"
"github.com/google/go-cmp/cmp/internal/function"
"github.com/google/go-cmp/cmp/internal/value"
)
-// BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to
-// the reflection package's inability to retrieve such entries. Equal will panic
-// anytime it comes across a NaN key, but this behavior may change.
-//
-// See https://golang.org/issue/11104 for more details.
-
-var nothing = reflect.Value{}
-
// Equal reports whether x and y are equal by recursively applying the
// following rules in the given order to x and y and all of their sub-values:
//
-// • If two values are not of the same type, then they are never equal
-// and the overall result is false.
-//
// • Let S be the set of all Ignore, Transformer, and Comparer options that
// remain after applying all path filters, value filters, and type filters.
// If at least one Ignore exists in S, then the comparison is ignored.
@@ -61,43 +52,84 @@
//
// • If the values have an Equal method of the form "(T) Equal(T) bool" or
// "(T) Equal(I) bool" where T is assignable to I, then use the result of
-// x.Equal(y) even if x or y is nil.
-// Otherwise, no such method exists and evaluation proceeds to the next rule.
+// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and
+// evaluation proceeds to the next rule.
//
// • Lastly, try to compare x and y based on their basic kinds.
// Simple kinds like booleans, integers, floats, complex numbers, strings, and
// channels are compared using the equivalent of the == operator in Go.
// Functions are only equal if they are both nil, otherwise they are unequal.
-// Pointers are equal if the underlying values they point to are also equal.
-// Interfaces are equal if their underlying concrete values are also equal.
//
-// Structs are equal if all of their fields are equal. If a struct contains
-// unexported fields, Equal panics unless the AllowUnexported option is used or
-// an Ignore option (e.g., cmpopts.IgnoreUnexported) ignores that field.
+// Structs are equal if recursively calling Equal on all fields report equal.
+// If a struct contains unexported fields, Equal panics unless an Ignore option
+// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option
+// explicitly permits comparing the unexported field.
//
-// Arrays, slices, and maps are equal if they are both nil or both non-nil
-// with the same length and the elements at each index or key are equal.
-// Note that a non-nil empty slice and a nil slice are not equal.
-// To equate empty slices and maps, consider using cmpopts.EquateEmpty.
+// Slices are equal if they are both nil or both non-nil, where recursively
+// calling Equal on all non-ignored slice or array elements report equal.
+// Empty non-nil slices and nil slices are not equal; to equate empty slices,
+// consider using cmpopts.EquateEmpty.
+//
+// Maps are equal if they are both nil or both non-nil, where recursively
+// calling Equal on all non-ignored map entries report equal.
// Map keys are equal according to the == operator.
// To use custom comparisons for map keys, consider using cmpopts.SortMaps.
+// Empty non-nil maps and nil maps are not equal; to equate empty maps,
+// consider using cmpopts.EquateEmpty.
+//
+// Pointers and interfaces are equal if they are both nil or both non-nil,
+// where they have the same underlying concrete type and recursively
+// calling Equal on the underlying values reports equal.
+//
+// Before recursing into a pointer, slice element, or map, the current path
+// is checked to detect whether the address has already been visited.
+// If there is a cycle, then the pointed at values are considered equal
+// only if both addresses were previously visited in the same path step.
func Equal(x, y interface{}, opts ...Option) bool {
+ vx := reflect.ValueOf(x)
+ vy := reflect.ValueOf(y)
+
+ // If the inputs are different types, auto-wrap them in an empty interface
+ // so that they have the same parent type.
+ var t reflect.Type
+ if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() {
+ t = reflect.TypeOf((*interface{})(nil)).Elem()
+ if vx.IsValid() {
+ vvx := reflect.New(t).Elem()
+ vvx.Set(vx)
+ vx = vvx
+ }
+ if vy.IsValid() {
+ vvy := reflect.New(t).Elem()
+ vvy.Set(vy)
+ vy = vvy
+ }
+ } else {
+ t = vx.Type()
+ }
+
s := newState(opts)
- s.compareAny(reflect.ValueOf(x), reflect.ValueOf(y))
+ s.compareAny(&pathStep{t, vx, vy})
return s.result.Equal()
}
// Diff returns a human-readable report of the differences between two values.
// It returns an empty string if and only if Equal returns true for the same
-// input values and options. The output string will use the "-" symbol to
-// indicate elements removed from x, and the "+" symbol to indicate elements
-// added to y.
+// input values and options.
//
-// Do not depend on this output being stable.
+// The output is displayed as a literal in pseudo-Go syntax.
+// At the start of each line, a "-" prefix indicates an element removed from x,
+// a "+" prefix to indicates an element added to y, and the lack of a prefix
+// indicates an element common to both x and y. If possible, the output
+// uses fmt.Stringer.String or error.Error methods to produce more humanly
+// readable outputs. In such cases, the string is prefixed with either an
+// 's' or 'e' character, respectively, to indicate that the method was called.
+//
+// Do not depend on this output being stable. If you need the ability to
+// programmatically interpret the difference, consider using a custom Reporter.
func Diff(x, y interface{}, opts ...Option) string {
r := new(defaultReporter)
- opts = Options{Options(opts), r}
- eq := Equal(x, y, opts...)
+ eq := Equal(x, y, Options(opts), Reporter(r))
d := r.String()
if (d == "") != eq {
panic("inconsistent difference and equality results")
@@ -108,24 +140,29 @@
type state struct {
// These fields represent the "comparison state".
// Calling statelessCompare must not result in observable changes to these.
- result diff.Result // The current result of comparison
- curPath Path // The current path in the value tree
- reporter reporter // Optional reporter used for difference formatting
+ result diff.Result // The current result of comparison
+ curPath Path // The current path in the value tree
+ curPtrs pointerPath // The current set of visited pointers
+ reporters []reporter // Optional reporters
+
+ // recChecker checks for infinite cycles applying the same set of
+ // transformers upon the output of itself.
+ recChecker recChecker
// dynChecker triggers pseudo-random checks for option correctness.
// It is safe for statelessCompare to mutate this value.
dynChecker dynChecker
// These fields, once set by processOption, will not change.
- exporters map[reflect.Type]bool // Set of structs with unexported field visibility
- opts Options // List of all fundamental and filter options
+ exporters []exporter // List of exporters for structs with unexported fields
+ opts Options // List of all fundamental and filter options
}
func newState(opts []Option) *state {
- s := new(state)
- for _, opt := range opts {
- s.processOption(opt)
- }
+ // Always ensure a validator option exists to validate the inputs.
+ s := &state{opts: Options{validator{}}}
+ s.curPtrs.Init()
+ s.processOption(Options(opts))
return s
}
@@ -144,18 +181,10 @@
panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt))
}
s.opts = append(s.opts, opt)
- case visibleStructs:
- if s.exporters == nil {
- s.exporters = make(map[reflect.Type]bool)
- }
- for t := range opt {
- s.exporters[t] = true
- }
+ case exporter:
+ s.exporters = append(s.exporters, opt)
case reporter:
- if s.reporter != nil {
- panic("difference reporter already registered")
- }
- s.reporter = opt
+ s.reporters = append(s.reporters, opt)
default:
panic(fmt.Sprintf("unknown option %T", opt))
}
@@ -164,153 +193,96 @@
// statelessCompare compares two values and returns the result.
// This function is stateless in that it does not alter the current result,
// or output to any registered reporters.
-func (s *state) statelessCompare(vx, vy reflect.Value) diff.Result {
- // We do not save and restore the curPath because all of the compareX
- // methods should properly push and pop from the path.
- // It is an implementation bug if the contents of curPath differs from
+func (s *state) statelessCompare(step PathStep) diff.Result {
+ // We do not save and restore curPath and curPtrs because all of the
+ // compareX methods should properly push and pop from them.
+ // It is an implementation bug if the contents of the paths differ from
// when calling this function to when returning from it.
- oldResult, oldReporter := s.result, s.reporter
+ oldResult, oldReporters := s.result, s.reporters
s.result = diff.Result{} // Reset result
- s.reporter = nil // Remove reporter to avoid spurious printouts
- s.compareAny(vx, vy)
+ s.reporters = nil // Remove reporters to avoid spurious printouts
+ s.compareAny(step)
res := s.result
- s.result, s.reporter = oldResult, oldReporter
+ s.result, s.reporters = oldResult, oldReporters
return res
}
-func (s *state) compareAny(vx, vy reflect.Value) {
- // TODO: Support cyclic data structures.
+func (s *state) compareAny(step PathStep) {
+ // Update the path stack.
+ s.curPath.push(step)
+ defer s.curPath.pop()
+ for _, r := range s.reporters {
+ r.PushStep(step)
+ defer r.PopStep()
+ }
+ s.recChecker.Check(s.curPath)
- // Rule 0: Differing types are never equal.
- if !vx.IsValid() || !vy.IsValid() {
- s.report(vx.IsValid() == vy.IsValid(), vx, vy)
- return
+ // Cycle-detection for slice elements (see NOTE in compareSlice).
+ t := step.Type()
+ vx, vy := step.Values()
+ if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() {
+ px, py := vx.Addr(), vy.Addr()
+ if eq, visited := s.curPtrs.Push(px, py); visited {
+ s.report(eq, reportByCycle)
+ return
+ }
+ defer s.curPtrs.Pop(px, py)
}
- if vx.Type() != vy.Type() {
- s.report(false, vx, vy) // Possible for path to be empty
- return
- }
- t := vx.Type()
- if len(s.curPath) == 0 {
- s.curPath.push(&pathStep{typ: t})
- defer s.curPath.pop()
- }
- vx, vy = s.tryExporting(vx, vy)
// Rule 1: Check whether an option applies on this node in the value tree.
- if s.tryOptions(vx, vy, t) {
+ if s.tryOptions(t, vx, vy) {
return
}
// Rule 2: Check whether the type has a valid Equal method.
- if s.tryMethod(vx, vy, t) {
+ if s.tryMethod(t, vx, vy) {
return
}
- // Rule 3: Recursively descend into each value's underlying kind.
+ // Rule 3: Compare based on the underlying kind.
switch t.Kind() {
case reflect.Bool:
- s.report(vx.Bool() == vy.Bool(), vx, vy)
- return
+ s.report(vx.Bool() == vy.Bool(), 0)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- s.report(vx.Int() == vy.Int(), vx, vy)
- return
+ s.report(vx.Int() == vy.Int(), 0)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- s.report(vx.Uint() == vy.Uint(), vx, vy)
- return
+ s.report(vx.Uint() == vy.Uint(), 0)
case reflect.Float32, reflect.Float64:
- s.report(vx.Float() == vy.Float(), vx, vy)
- return
+ s.report(vx.Float() == vy.Float(), 0)
case reflect.Complex64, reflect.Complex128:
- s.report(vx.Complex() == vy.Complex(), vx, vy)
- return
+ s.report(vx.Complex() == vy.Complex(), 0)
case reflect.String:
- s.report(vx.String() == vy.String(), vx, vy)
- return
+ s.report(vx.String() == vy.String(), 0)
case reflect.Chan, reflect.UnsafePointer:
- s.report(vx.Pointer() == vy.Pointer(), vx, vy)
- return
+ s.report(vx.Pointer() == vy.Pointer(), 0)
case reflect.Func:
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- case reflect.Ptr:
- if vx.IsNil() || vy.IsNil() {
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- }
- s.curPath.push(&indirect{pathStep{t.Elem()}})
- defer s.curPath.pop()
- s.compareAny(vx.Elem(), vy.Elem())
- return
- case reflect.Interface:
- if vx.IsNil() || vy.IsNil() {
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- }
- if vx.Elem().Type() != vy.Elem().Type() {
- s.report(false, vx.Elem(), vy.Elem())
- return
- }
- s.curPath.push(&typeAssertion{pathStep{vx.Elem().Type()}})
- defer s.curPath.pop()
- s.compareAny(vx.Elem(), vy.Elem())
- return
- case reflect.Slice:
- if vx.IsNil() || vy.IsNil() {
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- }
- fallthrough
- case reflect.Array:
- s.compareArray(vx, vy, t)
- return
- case reflect.Map:
- s.compareMap(vx, vy, t)
- return
+ s.report(vx.IsNil() && vy.IsNil(), 0)
case reflect.Struct:
- s.compareStruct(vx, vy, t)
- return
+ s.compareStruct(t, vx, vy)
+ case reflect.Slice, reflect.Array:
+ s.compareSlice(t, vx, vy)
+ case reflect.Map:
+ s.compareMap(t, vx, vy)
+ case reflect.Ptr:
+ s.comparePtr(t, vx, vy)
+ case reflect.Interface:
+ s.compareInterface(t, vx, vy)
default:
panic(fmt.Sprintf("%v kind not handled", t.Kind()))
}
}
-func (s *state) tryExporting(vx, vy reflect.Value) (reflect.Value, reflect.Value) {
- if sf, ok := s.curPath[len(s.curPath)-1].(*structField); ok && sf.unexported {
- if sf.force {
- // Use unsafe pointer arithmetic to get read-write access to an
- // unexported field in the struct.
- vx = unsafeRetrieveField(sf.pvx, sf.field)
- vy = unsafeRetrieveField(sf.pvy, sf.field)
- } else {
- // We are not allowed to export the value, so invalidate them
- // so that tryOptions can panic later if not explicitly ignored.
- vx = nothing
- vy = nothing
- }
- }
- return vx, vy
-}
-
-func (s *state) tryOptions(vx, vy reflect.Value, t reflect.Type) bool {
- // If there were no FilterValues, we will not detect invalid inputs,
- // so manually check for them and append invalid if necessary.
- // We still evaluate the options since an ignore can override invalid.
- opts := s.opts
- if !vx.IsValid() || !vy.IsValid() {
- opts = Options{opts, invalid{}}
- }
-
+func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool {
// Evaluate all filters and apply the remaining options.
- if opt := opts.filter(s, vx, vy, t); opt != nil {
+ if opt := s.opts.filter(s, t, vx, vy); opt != nil {
opt.apply(s, vx, vy)
return true
}
return false
}
-func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool {
+func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool {
// Check if this type even has an Equal method.
m, ok := t.MethodByName("Equal")
if !ok || !function.IsType(m.Type, function.EqualAssignable) {
@@ -318,11 +290,11 @@
}
eq := s.callTTBFunc(m.Func, vx, vy)
- s.report(eq, vx, vy)
+ s.report(eq, reportByMethod)
return true
}
-func (s *state) callTRFunc(f, v reflect.Value) reflect.Value {
+func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value {
v = sanitizeValue(v, f.Type().In(0))
if !s.dynChecker.Next() {
return f.Call([]reflect.Value{v})[0]
@@ -333,15 +305,15 @@
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, v)
+ got := <-c
want := f.Call([]reflect.Value{v})[0]
- if got := <-c; !s.statelessCompare(got, want).Equal() {
+ if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() {
// To avoid false-positives with non-reflexive equality operations,
// we sanity check whether a value is equal to itself.
- if !s.statelessCompare(want, want).Equal() {
+ if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() {
return want
}
- fn := getFuncName(f.Pointer())
- panic(fmt.Sprintf("non-deterministic function detected: %s", fn))
+ panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f)))
}
return want
}
@@ -359,10 +331,10 @@
// unsafe mutations to the input.
c := make(chan reflect.Value)
go detectRaces(c, f, y, x)
+ got := <-c
want := f.Call([]reflect.Value{x, y})[0].Bool()
- if got := <-c; !got.IsValid() || got.Bool() != want {
- fn := getFuncName(f.Pointer())
- panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", fn))
+ if !got.IsValid() || got.Bool() != want {
+ panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f)))
}
return want
}
@@ -380,140 +352,270 @@
// assuming that T is assignable to R.
// Otherwise, it returns the input value as is.
func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value {
- // TODO(dsnet): Remove this hacky workaround.
- // See https://golang.org/issue/22143
- if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
- return reflect.New(t).Elem()
+ // TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/22143).
+ if !flags.AtLeastGo110 {
+ if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t {
+ return reflect.New(t).Elem()
+ }
}
return v
}
-func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) {
- step := &sliceIndex{pathStep{t.Elem()}, 0, 0}
- s.curPath.push(step)
-
- // Compute an edit-script for slices vx and vy.
- es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result {
- step.xkey, step.ykey = ix, iy
- return s.statelessCompare(vx.Index(ix), vy.Index(iy))
- })
-
- // Report the entire slice as is if the arrays are of primitive kind,
- // and the arrays are different enough.
- isPrimitive := false
- switch t.Elem().Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
- reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
- isPrimitive = true
- }
- if isPrimitive && es.Dist() > (vx.Len()+vy.Len())/4 {
- s.curPath.pop() // Pop first since we are reporting the whole slice
- s.report(false, vx, vy)
- return
- }
-
- // Replay the edit-script.
- var ix, iy int
- for _, e := range es {
- switch e {
- case diff.UniqueX:
- step.xkey, step.ykey = ix, -1
- s.report(false, vx.Index(ix), nothing)
- ix++
- case diff.UniqueY:
- step.xkey, step.ykey = -1, iy
- s.report(false, nothing, vy.Index(iy))
- iy++
- default:
- step.xkey, step.ykey = ix, iy
- if e == diff.Identity {
- s.report(true, vx.Index(ix), vy.Index(iy))
- } else {
- s.compareAny(vx.Index(ix), vy.Index(iy))
- }
- ix++
- iy++
- }
- }
- s.curPath.pop()
- return
-}
-
-func (s *state) compareMap(vx, vy reflect.Value, t reflect.Type) {
- if vx.IsNil() || vy.IsNil() {
- s.report(vx.IsNil() && vy.IsNil(), vx, vy)
- return
- }
-
- // We combine and sort the two map keys so that we can perform the
- // comparisons in a deterministic order.
- step := &mapIndex{pathStep: pathStep{t.Elem()}}
- s.curPath.push(step)
- defer s.curPath.pop()
- for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
- step.key = k
- vvx := vx.MapIndex(k)
- vvy := vy.MapIndex(k)
- switch {
- case vvx.IsValid() && vvy.IsValid():
- s.compareAny(vvx, vvy)
- case vvx.IsValid() && !vvy.IsValid():
- s.report(false, vvx, nothing)
- case !vvx.IsValid() && vvy.IsValid():
- s.report(false, nothing, vvy)
- default:
- // It is possible for both vvx and vvy to be invalid if the
- // key contained a NaN value in it. There is no way in
- // reflection to be able to retrieve these values.
- // See https://golang.org/issue/11104
- panic(fmt.Sprintf("%#v has map key with NaNs", s.curPath))
- }
- }
-}
-
-func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) {
+func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
var vax, vay reflect.Value // Addressable versions of vx and vy
- step := &structField{}
- s.curPath.push(step)
- defer s.curPath.pop()
+ var mayForce, mayForceInit bool
+ step := StructField{&structField{}}
for i := 0; i < t.NumField(); i++ {
- vvx := vx.Field(i)
- vvy := vy.Field(i)
step.typ = t.Field(i).Type
+ step.vx = vx.Field(i)
+ step.vy = vy.Field(i)
step.name = t.Field(i).Name
step.idx = i
step.unexported = !isExported(step.name)
if step.unexported {
+ if step.name == "_" {
+ continue
+ }
// Defer checking of unexported fields until later to give an
// Ignore a chance to ignore the field.
if !vax.IsValid() || !vay.IsValid() {
- // For unsafeRetrieveField to work, the parent struct must
+ // For retrieveUnexportedField to work, the parent struct must
// be addressable. Create a new copy of the values if
// necessary to make them addressable.
vax = makeAddressable(vx)
vay = makeAddressable(vy)
}
- step.force = s.exporters[t]
+ if !mayForceInit {
+ for _, xf := range s.exporters {
+ mayForce = mayForce || xf(t)
+ }
+ mayForceInit = true
+ }
+ step.mayForce = mayForce
step.pvx = vax
step.pvy = vay
step.field = t.Field(i)
}
- s.compareAny(vvx, vvy)
+ s.compareAny(step)
}
}
-// report records the result of a single comparison.
-// It also calls Report if any reporter is registered.
-func (s *state) report(eq bool, vx, vy reflect.Value) {
- if eq {
- s.result.NSame++
- } else {
- s.result.NDiff++
+func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) {
+ isSlice := t.Kind() == reflect.Slice
+ if isSlice && (vx.IsNil() || vy.IsNil()) {
+ s.report(vx.IsNil() && vy.IsNil(), 0)
+ return
}
- if s.reporter != nil {
- s.reporter.Report(vx, vy, eq, s.curPath)
+
+ // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer
+ // since slices represents a list of pointers, rather than a single pointer.
+ // The pointer checking logic must be handled on a per-element basis
+ // in compareAny.
+ //
+ // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting
+ // pointer P, a length N, and a capacity C. Supposing each slice element has
+ // a memory size of M, then the slice is equivalent to the list of pointers:
+ // [P+i*M for i in range(N)]
+ //
+ // For example, v[:0] and v[:1] are slices with the same starting pointer,
+ // but they are clearly different values. Using the slice pointer alone
+ // violates the assumption that equal pointers implies equal values.
+
+ step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}}
+ withIndexes := func(ix, iy int) SliceIndex {
+ if ix >= 0 {
+ step.vx, step.xkey = vx.Index(ix), ix
+ } else {
+ step.vx, step.xkey = reflect.Value{}, -1
+ }
+ if iy >= 0 {
+ step.vy, step.ykey = vy.Index(iy), iy
+ } else {
+ step.vy, step.ykey = reflect.Value{}, -1
+ }
+ return step
+ }
+
+ // Ignore options are able to ignore missing elements in a slice.
+ // However, detecting these reliably requires an optimal differencing
+ // algorithm, for which diff.Difference is not.
+ //
+ // Instead, we first iterate through both slices to detect which elements
+ // would be ignored if standing alone. The index of non-discarded elements
+ // are stored in a separate slice, which diffing is then performed on.
+ var indexesX, indexesY []int
+ var ignoredX, ignoredY []bool
+ for ix := 0; ix < vx.Len(); ix++ {
+ ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0
+ if !ignored {
+ indexesX = append(indexesX, ix)
+ }
+ ignoredX = append(ignoredX, ignored)
+ }
+ for iy := 0; iy < vy.Len(); iy++ {
+ ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0
+ if !ignored {
+ indexesY = append(indexesY, iy)
+ }
+ ignoredY = append(ignoredY, ignored)
+ }
+
+ // Compute an edit-script for slices vx and vy (excluding ignored elements).
+ edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result {
+ return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy]))
+ })
+
+ // Replay the ignore-scripts and the edit-script.
+ var ix, iy int
+ for ix < vx.Len() || iy < vy.Len() {
+ var e diff.EditType
+ switch {
+ case ix < len(ignoredX) && ignoredX[ix]:
+ e = diff.UniqueX
+ case iy < len(ignoredY) && ignoredY[iy]:
+ e = diff.UniqueY
+ default:
+ e, edits = edits[0], edits[1:]
+ }
+ switch e {
+ case diff.UniqueX:
+ s.compareAny(withIndexes(ix, -1))
+ ix++
+ case diff.UniqueY:
+ s.compareAny(withIndexes(-1, iy))
+ iy++
+ default:
+ s.compareAny(withIndexes(ix, iy))
+ ix++
+ iy++
+ }
+ }
+}
+
+func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
+ if vx.IsNil() || vy.IsNil() {
+ s.report(vx.IsNil() && vy.IsNil(), 0)
+ return
+ }
+
+ // Cycle-detection for maps.
+ if eq, visited := s.curPtrs.Push(vx, vy); visited {
+ s.report(eq, reportByCycle)
+ return
+ }
+ defer s.curPtrs.Pop(vx, vy)
+
+ // We combine and sort the two map keys so that we can perform the
+ // comparisons in a deterministic order.
+ step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}}
+ for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
+ step.vx = vx.MapIndex(k)
+ step.vy = vy.MapIndex(k)
+ step.key = k
+ if !step.vx.IsValid() && !step.vy.IsValid() {
+ // It is possible for both vx and vy to be invalid if the
+ // key contained a NaN value in it.
+ //
+ // Even with the ability to retrieve NaN keys in Go 1.12,
+ // there still isn't a sensible way to compare the values since
+ // a NaN key may map to multiple unordered values.
+ // The most reasonable way to compare NaNs would be to compare the
+ // set of values. However, this is impossible to do efficiently
+ // since set equality is provably an O(n^2) operation given only
+ // an Equal function. If we had a Less function or Hash function,
+ // this could be done in O(n*log(n)) or O(n), respectively.
+ //
+ // Rather than adding complex logic to deal with NaNs, make it
+ // the user's responsibility to compare such obscure maps.
+ const help = "consider providing a Comparer to compare the map"
+ panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help))
+ }
+ s.compareAny(step)
+ }
+}
+
+func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) {
+ if vx.IsNil() || vy.IsNil() {
+ s.report(vx.IsNil() && vy.IsNil(), 0)
+ return
+ }
+
+ // Cycle-detection for pointers.
+ if eq, visited := s.curPtrs.Push(vx, vy); visited {
+ s.report(eq, reportByCycle)
+ return
+ }
+ defer s.curPtrs.Pop(vx, vy)
+
+ vx, vy = vx.Elem(), vy.Elem()
+ s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}})
+}
+
+func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) {
+ if vx.IsNil() || vy.IsNil() {
+ s.report(vx.IsNil() && vy.IsNil(), 0)
+ return
+ }
+ vx, vy = vx.Elem(), vy.Elem()
+ if vx.Type() != vy.Type() {
+ s.report(false, 0)
+ return
+ }
+ s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}})
+}
+
+func (s *state) report(eq bool, rf resultFlags) {
+ if rf&reportByIgnore == 0 {
+ if eq {
+ s.result.NumSame++
+ rf |= reportEqual
+ } else {
+ s.result.NumDiff++
+ rf |= reportUnequal
+ }
+ }
+ for _, r := range s.reporters {
+ r.Report(Result{flags: rf})
+ }
+}
+
+// recChecker tracks the state needed to periodically perform checks that
+// user provided transformers are not stuck in an infinitely recursive cycle.
+type recChecker struct{ next int }
+
+// Check scans the Path for any recursive transformers and panics when any
+// recursive transformers are detected. Note that the presence of a
+// recursive Transformer does not necessarily imply an infinite cycle.
+// As such, this check only activates after some minimal number of path steps.
+func (rc *recChecker) Check(p Path) {
+ const minLen = 1 << 16
+ if rc.next == 0 {
+ rc.next = minLen
+ }
+ if len(p) < rc.next {
+ return
+ }
+ rc.next <<= 1
+
+ // Check whether the same transformer has appeared at least twice.
+ var ss []string
+ m := map[Option]int{}
+ for _, ps := range p {
+ if t, ok := ps.(Transform); ok {
+ t := t.Option()
+ if m[t] == 1 { // Transformer was used exactly once before
+ tf := t.(*transformer).fnc.Type()
+ ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0)))
+ }
+ m[t]++
+ }
+ }
+ if len(ss) > 0 {
+ const warning = "recursive set of Transformers detected"
+ const help = "consider using cmpopts.AcyclicTransformer"
+ set := strings.Join(ss, "\n\t")
+ panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help))
}
}
diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go
new file mode 100644
index 0000000..dd03235
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/export_panic.go
@@ -0,0 +1,15 @@
+// Copyright 2017, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build purego
+
+package cmp
+
+import "reflect"
+
+const supportExporters = false
+
+func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value {
+ panic("no support for forcibly accessing unexported fields")
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go
new file mode 100644
index 0000000..57020e2
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go
@@ -0,0 +1,25 @@
+// Copyright 2017, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build !purego
+
+package cmp
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const supportExporters = true
+
+// retrieveUnexportedField uses unsafe to forcibly retrieve any field from
+// a struct such that the value has read-write permissions.
+//
+// The parent struct, v, must be addressable, while f must be a StructField
+// describing the field to retrieve.
+func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value {
+ // See https://github.com/google/go-cmp/issues/167 for discussion of the
+ // following expression.
+ return reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem()
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go
index 42afa49..fe98dcc 100644
--- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go
+++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
-// +build !debug
+// +build !cmp_debug
package diff
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go
index fd9f7f1..597b6ae 100644
--- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go
+++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
-// +build debug
+// +build cmp_debug
package diff
@@ -14,7 +14,7 @@
)
// The algorithm can be seen running in real-time by enabling debugging:
-// go test -tags=debug -v
+// go test -tags=cmp_debug -v
//
// Example output:
// === RUN TestDifference/#34
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
index 260befe..3d2e426 100644
--- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
+++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go
@@ -85,22 +85,31 @@
type EqualFunc func(ix int, iy int) Result
// Result is the result of comparison.
-// NSame is the number of sub-elements that are equal.
-// NDiff is the number of sub-elements that are not equal.
-type Result struct{ NSame, NDiff int }
+// NumSame is the number of sub-elements that are equal.
+// NumDiff is the number of sub-elements that are not equal.
+type Result struct{ NumSame, NumDiff int }
+
+// BoolResult returns a Result that is either Equal or not Equal.
+func BoolResult(b bool) Result {
+ if b {
+ return Result{NumSame: 1} // Equal, Similar
+ } else {
+ return Result{NumDiff: 2} // Not Equal, not Similar
+ }
+}
// Equal indicates whether the symbols are equal. Two symbols are equal
-// if and only if NDiff == 0. If Equal, then they are also Similar.
-func (r Result) Equal() bool { return r.NDiff == 0 }
+// if and only if NumDiff == 0. If Equal, then they are also Similar.
+func (r Result) Equal() bool { return r.NumDiff == 0 }
// Similar indicates whether two symbols are similar and may be represented
// by using the Modified type. As a special case, we consider binary comparisons
// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar.
//
-// The exact ratio of NSame to NDiff to determine similarity may change.
+// The exact ratio of NumSame to NumDiff to determine similarity may change.
func (r Result) Similar() bool {
- // Use NSame+1 to offset NSame so that binary comparisons are similar.
- return r.NSame+1 >= r.NDiff
+ // Use NumSame+1 to offset NumSame so that binary comparisons are similar.
+ return r.NumSame+1 >= r.NumDiff
}
// Difference reports whether two lists of lengths nx and ny are equal
@@ -191,9 +200,9 @@
// that two lists commonly differ because elements were added to the front
// or end of the other list.
//
- // Running the tests with the "debug" build tag prints a visualization of
- // the algorithm running in real-time. This is educational for understanding
- // how the algorithm works. See debug_enable.go.
+ // Running the tests with the "cmp_debug" build tag prints a visualization
+ // of the algorithm running in real-time. This is educational for
+ // understanding how the algorithm works. See debug_enable.go.
f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es)
for {
// Forward search from the beginning.
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go
new file mode 100644
index 0000000..a9e7fc0
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go
@@ -0,0 +1,9 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package flags
+
+// Deterministic controls whether the output of Diff should be deterministic.
+// This is only used for testing.
+var Deterministic bool
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go
new file mode 100644
index 0000000..01aed0a
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go
@@ -0,0 +1,10 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build !go1.10
+
+package flags
+
+// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
+const AtLeastGo110 = false
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go
new file mode 100644
index 0000000..c0b667f
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go
@@ -0,0 +1,10 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build go1.10
+
+package flags
+
+// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10.
+const AtLeastGo110 = true
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go
index 4c35ff1..ace1dbe 100644
--- a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go
+++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go
@@ -2,25 +2,34 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.
-// Package function identifies function types.
+// Package function provides functionality for identifying function types.
package function
-import "reflect"
+import (
+ "reflect"
+ "regexp"
+ "runtime"
+ "strings"
+)
type funcType int
const (
_ funcType = iota
+ tbFunc // func(T) bool
ttbFunc // func(T, T) bool
+ trbFunc // func(T, R) bool
tibFunc // func(T, I) bool
trFunc // func(T) R
- Equal = ttbFunc // func(T, T) bool
- EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
- Transformer = trFunc // func(T) R
- ValueFilter = ttbFunc // func(T, T) bool
- Less = ttbFunc // func(T, T) bool
+ Equal = ttbFunc // func(T, T) bool
+ EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool
+ Transformer = trFunc // func(T) R
+ ValueFilter = ttbFunc // func(T, T) bool
+ Less = ttbFunc // func(T, T) bool
+ ValuePredicate = tbFunc // func(T) bool
+ KeyValuePredicate = trbFunc // func(T, R) bool
)
var boolType = reflect.TypeOf(true)
@@ -32,10 +41,18 @@
}
ni, no := t.NumIn(), t.NumOut()
switch ft {
+ case tbFunc: // func(T) bool
+ if ni == 1 && no == 1 && t.Out(0) == boolType {
+ return true
+ }
case ttbFunc: // func(T, T) bool
if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType {
return true
}
+ case trbFunc: // func(T, R) bool
+ if ni == 2 && no == 1 && t.Out(0) == boolType {
+ return true
+ }
case tibFunc: // func(T, I) bool
if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType {
return true
@@ -47,3 +64,36 @@
}
return false
}
+
+var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`)
+
+// NameOf returns the name of the function value.
+func NameOf(v reflect.Value) string {
+ fnc := runtime.FuncForPC(v.Pointer())
+ if fnc == nil {
+ return "<unknown>"
+ }
+ fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm"
+
+ // Method closures have a "-fm" suffix.
+ fullName = strings.TrimSuffix(fullName, "-fm")
+
+ var name string
+ for len(fullName) > 0 {
+ inParen := strings.HasSuffix(fullName, ")")
+ fullName = strings.TrimSuffix(fullName, ")")
+
+ s := lastIdentRx.FindString(fullName)
+ if s == "" {
+ break
+ }
+ name = s + "." + name
+ fullName = strings.TrimSuffix(fullName, s)
+
+ if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 {
+ fullName = fullName[:i]
+ }
+ fullName = strings.TrimSuffix(fullName, ".")
+ }
+ return strings.TrimSuffix(name, ".")
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go b/vendor/github.com/google/go-cmp/cmp/internal/value/format.go
deleted file mode 100644
index 657e508..0000000
--- a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2017, The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.md file.
-
-// Package value provides functionality for reflect.Value types.
-package value
-
-import (
- "fmt"
- "reflect"
- "strconv"
- "strings"
- "unicode"
-)
-
-var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
-
-// Format formats the value v as a string.
-//
-// This is similar to fmt.Sprintf("%+v", v) except this:
-// * Prints the type unless it can be elided
-// * Avoids printing struct fields that are zero
-// * Prints a nil-slice as being nil, not empty
-// * Prints map entries in deterministic order
-func Format(v reflect.Value, conf FormatConfig) string {
- conf.printType = true
- conf.followPointers = true
- conf.realPointers = true
- return formatAny(v, conf, nil)
-}
-
-type FormatConfig struct {
- UseStringer bool // Should the String method be used if available?
- printType bool // Should we print the type before the value?
- PrintPrimitiveType bool // Should we print the type of primitives?
- followPointers bool // Should we recursively follow pointers?
- realPointers bool // Should we print the real address of pointers?
-}
-
-func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string {
- // TODO: Should this be a multi-line printout in certain situations?
-
- if !v.IsValid() {
- return "<non-existent>"
- }
- if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() {
- if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
- return "<nil>"
- }
-
- const stringerPrefix = "s" // Indicates that the String method was used
- s := v.Interface().(fmt.Stringer).String()
- return stringerPrefix + formatString(s)
- }
-
- switch v.Kind() {
- case reflect.Bool:
- return formatPrimitive(v.Type(), v.Bool(), conf)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return formatPrimitive(v.Type(), v.Int(), conf)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr {
- // Unnamed uints are usually bytes or words, so use hexadecimal.
- return formatPrimitive(v.Type(), formatHex(v.Uint()), conf)
- }
- return formatPrimitive(v.Type(), v.Uint(), conf)
- case reflect.Float32, reflect.Float64:
- return formatPrimitive(v.Type(), v.Float(), conf)
- case reflect.Complex64, reflect.Complex128:
- return formatPrimitive(v.Type(), v.Complex(), conf)
- case reflect.String:
- return formatPrimitive(v.Type(), formatString(v.String()), conf)
- case reflect.UnsafePointer, reflect.Chan, reflect.Func:
- return formatPointer(v, conf)
- case reflect.Ptr:
- if v.IsNil() {
- if conf.printType {
- return fmt.Sprintf("(%v)(nil)", v.Type())
- }
- return "<nil>"
- }
- if visited[v.Pointer()] || !conf.followPointers {
- return formatPointer(v, conf)
- }
- visited = insertPointer(visited, v.Pointer())
- return "&" + formatAny(v.Elem(), conf, visited)
- case reflect.Interface:
- if v.IsNil() {
- if conf.printType {
- return fmt.Sprintf("%v(nil)", v.Type())
- }
- return "<nil>"
- }
- return formatAny(v.Elem(), conf, visited)
- case reflect.Slice:
- if v.IsNil() {
- if conf.printType {
- return fmt.Sprintf("%v(nil)", v.Type())
- }
- return "<nil>"
- }
- if visited[v.Pointer()] {
- return formatPointer(v, conf)
- }
- visited = insertPointer(visited, v.Pointer())
- fallthrough
- case reflect.Array:
- var ss []string
- subConf := conf
- subConf.printType = v.Type().Elem().Kind() == reflect.Interface
- for i := 0; i < v.Len(); i++ {
- s := formatAny(v.Index(i), subConf, visited)
- ss = append(ss, s)
- }
- s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
- if conf.printType {
- return v.Type().String() + s
- }
- return s
- case reflect.Map:
- if v.IsNil() {
- if conf.printType {
- return fmt.Sprintf("%v(nil)", v.Type())
- }
- return "<nil>"
- }
- if visited[v.Pointer()] {
- return formatPointer(v, conf)
- }
- visited = insertPointer(visited, v.Pointer())
-
- var ss []string
- keyConf, valConf := conf, conf
- keyConf.printType = v.Type().Key().Kind() == reflect.Interface
- keyConf.followPointers = false
- valConf.printType = v.Type().Elem().Kind() == reflect.Interface
- for _, k := range SortKeys(v.MapKeys()) {
- sk := formatAny(k, keyConf, visited)
- sv := formatAny(v.MapIndex(k), valConf, visited)
- ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
- }
- s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
- if conf.printType {
- return v.Type().String() + s
- }
- return s
- case reflect.Struct:
- var ss []string
- subConf := conf
- subConf.printType = true
- for i := 0; i < v.NumField(); i++ {
- vv := v.Field(i)
- if isZero(vv) {
- continue // Elide zero value fields
- }
- name := v.Type().Field(i).Name
- subConf.UseStringer = conf.UseStringer
- s := formatAny(vv, subConf, visited)
- ss = append(ss, fmt.Sprintf("%s: %s", name, s))
- }
- s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
- if conf.printType {
- return v.Type().String() + s
- }
- return s
- default:
- panic(fmt.Sprintf("%v kind not handled", v.Kind()))
- }
-}
-
-func formatString(s string) string {
- // Use quoted string if it the same length as a raw string literal.
- // Otherwise, attempt to use the raw string form.
- qs := strconv.Quote(s)
- if len(qs) == 1+len(s)+1 {
- return qs
- }
-
- // Disallow newlines to ensure output is a single line.
- // Only allow printable runes for readability purposes.
- rawInvalid := func(r rune) bool {
- return r == '`' || r == '\n' || !unicode.IsPrint(r)
- }
- if strings.IndexFunc(s, rawInvalid) < 0 {
- return "`" + s + "`"
- }
- return qs
-}
-
-func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string {
- if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") {
- return fmt.Sprintf("%v(%v)", t, v)
- }
- return fmt.Sprintf("%v", v)
-}
-
-func formatPointer(v reflect.Value, conf FormatConfig) string {
- p := v.Pointer()
- if !conf.realPointers {
- p = 0 // For deterministic printing purposes
- }
- s := formatHex(uint64(p))
- if conf.printType {
- return fmt.Sprintf("(%v)(%s)", v.Type(), s)
- }
- return s
-}
-
-func formatHex(u uint64) string {
- var f string
- switch {
- case u <= 0xff:
- f = "0x%02x"
- case u <= 0xffff:
- f = "0x%04x"
- case u <= 0xffffff:
- f = "0x%06x"
- case u <= 0xffffffff:
- f = "0x%08x"
- case u <= 0xffffffffff:
- f = "0x%010x"
- case u <= 0xffffffffffff:
- f = "0x%012x"
- case u <= 0xffffffffffffff:
- f = "0x%014x"
- case u <= 0xffffffffffffffff:
- f = "0x%016x"
- }
- return fmt.Sprintf(f, u)
-}
-
-// insertPointer insert p into m, allocating m if necessary.
-func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool {
- if m == nil {
- m = make(map[uintptr]bool)
- }
- m[p] = true
- return m
-}
-
-// isZero reports whether v is the zero value.
-// This does not rely on Interface and so can be used on unexported fields.
-func isZero(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Bool:
- return v.Bool() == false
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Complex64, reflect.Complex128:
- return v.Complex() == 0
- case reflect.String:
- return v.String() == ""
- case reflect.UnsafePointer:
- return v.Pointer() == 0
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
- return v.IsNil()
- case reflect.Array:
- for i := 0; i < v.Len(); i++ {
- if !isZero(v.Index(i)) {
- return false
- }
- }
- return true
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- if !isZero(v.Field(i)) {
- return false
- }
- }
- return true
- }
- return false
-}
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
new file mode 100644
index 0000000..0a01c47
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go
@@ -0,0 +1,23 @@
+// Copyright 2018, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build purego
+
+package value
+
+import "reflect"
+
+// Pointer is an opaque typed pointer and is guaranteed to be comparable.
+type Pointer struct {
+ p uintptr
+ t reflect.Type
+}
+
+// PointerOf returns a Pointer from v, which must be a
+// reflect.Ptr, reflect.Slice, or reflect.Map.
+func PointerOf(v reflect.Value) Pointer {
+ // NOTE: Storing a pointer as an uintptr is technically incorrect as it
+ // assumes that the GC implementation does not use a moving collector.
+ return Pointer{v.Pointer(), v.Type()}
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
new file mode 100644
index 0000000..da134ae
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go
@@ -0,0 +1,26 @@
+// Copyright 2018, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+// +build !purego
+
+package value
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// Pointer is an opaque typed pointer and is guaranteed to be comparable.
+type Pointer struct {
+ p unsafe.Pointer
+ t reflect.Type
+}
+
+// PointerOf returns a Pointer from v, which must be a
+// reflect.Ptr, reflect.Slice, or reflect.Map.
+func PointerOf(v reflect.Value) Pointer {
+ // The proper representation of a pointer is unsafe.Pointer,
+ // which is necessary if the GC ever uses a moving collector.
+ return Pointer{unsafe.Pointer(v.Pointer()), v.Type()}
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go
index fe8aa27..24fbae6 100644
--- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go
+++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go
@@ -19,7 +19,7 @@
}
// Sort the map keys.
- sort.Sort(valueSorter(vs))
+ sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) })
// Deduplicate keys (fails for NaNs).
vs2 := vs[:1]
@@ -31,13 +31,6 @@
return vs2
}
-// TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above.
-type valueSorter []reflect.Value
-
-func (vs valueSorter) Len() int { return len(vs) }
-func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) }
-func (vs valueSorter) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
-
// isLess is a generic function for sorting arbitrary map keys.
// The inputs must be of the same type and must be comparable.
func isLess(x, y reflect.Value) bool {
@@ -49,6 +42,8 @@
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return x.Uint() < y.Uint()
case reflect.Float32, reflect.Float64:
+ // NOTE: This does not sort -0 as less than +0
+ // since Go maps treat -0 and +0 as equal keys.
fx, fy := x.Float(), y.Float()
return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy)
case reflect.Complex64, reflect.Complex128:
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go
new file mode 100644
index 0000000..06a8ffd
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go
@@ -0,0 +1,48 @@
+// Copyright 2017, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package value
+
+import (
+ "math"
+ "reflect"
+)
+
+// IsZero reports whether v is the zero value.
+// This does not rely on Interface and so can be used on unexported fields.
+func IsZero(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Bool:
+ return v.Bool() == false
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return math.Float64bits(v.Float()) == 0
+ case reflect.Complex64, reflect.Complex128:
+ return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0
+ case reflect.String:
+ return v.String() == ""
+ case reflect.UnsafePointer:
+ return v.Pointer() == 0
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
+ return v.IsNil()
+ case reflect.Array:
+ for i := 0; i < v.Len(); i++ {
+ if !IsZero(v.Index(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ if !IsZero(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go
index 91d4b06..abbd2a6 100644
--- a/vendor/github.com/google/go-cmp/cmp/options.go
+++ b/vendor/github.com/google/go-cmp/cmp/options.go
@@ -7,7 +7,7 @@
import (
"fmt"
"reflect"
- "runtime"
+ "regexp"
"strings"
"github.com/google/go-cmp/cmp/internal/function"
@@ -29,11 +29,11 @@
// An Options is returned only if multiple comparers or transformers
// can apply simultaneously and will only contain values of those types
// or sub-Options containing values of those types.
- filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption
+ filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption
}
// applicableOption represents the following types:
-// Fundamental: ignore | invalid | *comparer | *transformer
+// Fundamental: ignore | validator | *comparer | *transformer
// Grouping: Options
type applicableOption interface {
Option
@@ -43,7 +43,7 @@
}
// coreOption represents the following types:
-// Fundamental: ignore | invalid | *comparer | *transformer
+// Fundamental: ignore | validator | *comparer | *transformer
// Filters: *pathFilter | *valuesFilter
type coreOption interface {
Option
@@ -63,19 +63,19 @@
// on all individual options held within.
type Options []Option
-func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) {
+func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) {
for _, opt := range opts {
- switch opt := opt.filter(s, vx, vy, t); opt.(type) {
+ switch opt := opt.filter(s, t, vx, vy); opt.(type) {
case ignore:
return ignore{} // Only ignore can short-circuit evaluation
- case invalid:
- out = invalid{} // Takes precedence over comparer or transformer
+ case validator:
+ out = validator{} // Takes precedence over comparer or transformer
case *comparer, *transformer, Options:
switch out.(type) {
case nil:
out = opt
- case invalid:
- // Keep invalid
+ case validator:
+ // Keep validator
case *comparer, *transformer, Options:
out = Options{out, opt} // Conflicting comparers or transformers
}
@@ -106,6 +106,11 @@
// FilterPath returns a new Option where opt is only evaluated if filter f
// returns true for the current Path in the value tree.
//
+// This filter is called even if a slice element or map entry is missing and
+// provides an opportunity to ignore such cases. The filter function must be
+// symmetric such that the filter result is identical regardless of whether the
+// missing value is from x or y.
+//
// The option passed in may be an Ignore, Transformer, Comparer, Options, or
// a previously filtered Option.
func FilterPath(f func(Path) bool, opt Option) Option {
@@ -124,22 +129,22 @@
opt Option
}
-func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
+func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
if f.fnc(s.curPath) {
- return f.opt.filter(s, vx, vy, t)
+ return f.opt.filter(s, t, vx, vy)
}
return nil
}
func (f pathFilter) String() string {
- fn := getFuncName(reflect.ValueOf(f.fnc).Pointer())
- return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt)
+ return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt)
}
// FilterValues returns a new Option where opt is only evaluated if filter f,
// which is a function of the form "func(T, T) bool", returns true for the
-// current pair of values being compared. If the type of the values is not
-// assignable to T, then this filter implicitly returns false.
+// current pair of values being compared. If either value is invalid or
+// the type of the values is not assignable to T, then this filter implicitly
+// returns false.
//
// The filter function must be
// symmetric (i.e., agnostic to the order of the inputs) and
@@ -171,19 +176,18 @@
opt Option
}
-func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
- if !vx.IsValid() || !vy.IsValid() {
- return invalid{}
+func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
+ if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() {
+ return nil
}
if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) {
- return f.opt.filter(s, vx, vy, t)
+ return f.opt.filter(s, t, vx, vy)
}
return nil
}
func (f valuesFilter) String() string {
- fn := getFuncName(f.fnc.Pointer())
- return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt)
+ return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt)
}
// Ignore is an Option that causes all comparisons to be ignored.
@@ -194,19 +198,56 @@
type ignore struct{ core }
func (ignore) isFiltered() bool { return false }
-func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} }
-func (ignore) apply(_ *state, _, _ reflect.Value) { return }
+func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} }
+func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) }
func (ignore) String() string { return "Ignore()" }
-// invalid is a sentinel Option type to indicate that some options could not
-// be evaluated due to unexported fields.
-type invalid struct{ core }
+// validator is a sentinel Option type to indicate that some options could not
+// be evaluated due to unexported fields, missing slice elements, or
+// missing map entries. Both values are validator only for unexported fields.
+type validator struct{ core }
-func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} }
-func (invalid) apply(s *state, _, _ reflect.Value) {
- const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported"
- panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help))
+func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption {
+ if !vx.IsValid() || !vy.IsValid() {
+ return validator{}
+ }
+ if !vx.CanInterface() || !vy.CanInterface() {
+ return validator{}
+ }
+ return nil
}
+func (validator) apply(s *state, vx, vy reflect.Value) {
+ // Implies missing slice element or map entry.
+ if !vx.IsValid() || !vy.IsValid() {
+ s.report(vx.IsValid() == vy.IsValid(), 0)
+ return
+ }
+
+ // Unable to Interface implies unexported field without visibility access.
+ if !vx.CanInterface() || !vy.CanInterface() {
+ const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported"
+ var name string
+ if t := s.curPath.Index(-2).Type(); t.Name() != "" {
+ // Named type with unexported fields.
+ name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType
+ } else {
+ // Unnamed type with unexported fields. Derive PkgPath from field.
+ var pkgPath string
+ for i := 0; i < t.NumField() && pkgPath == ""; i++ {
+ pkgPath = t.Field(i).PkgPath
+ }
+ name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int })
+ }
+ panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help))
+ }
+
+ panic("not reachable")
+}
+
+// identRx represents a valid identifier according to the Go specification.
+const identRx = `[_\p{L}][_\p{L}\p{N}]*`
+
+var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
// Transformer returns an Option that applies a transformation function that
// converts values of a certain type into that of another.
@@ -220,18 +261,25 @@
// input and output types are the same), an implicit filter is added such that
// a transformer is applicable only if that exact transformer is not already
// in the tail of the Path since the last non-Transform step.
+// For situations where the implicit filter is still insufficient,
+// consider using cmpopts.AcyclicTransformer, which adds a filter
+// to prevent the transformer from being recursively applied upon itself.
//
// The name is a user provided label that is used as the Transform.Name in the
-// transformation PathStep. If empty, an arbitrary name is used.
+// transformation PathStep (and eventually shown in the Diff output).
+// The name must be a valid identifier or qualified identifier in Go syntax.
+// If empty, an arbitrary name is used.
func Transformer(name string, f interface{}) Option {
v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
panic(fmt.Sprintf("invalid transformer function: %T", f))
}
if name == "" {
- name = "λ" // Lambda-symbol as place-holder for anonymous transformer
- }
- if !isValid(name) {
+ name = function.NameOf(v)
+ if !identsRx.MatchString(name) {
+ name = "λ" // Lambda-symbol as placeholder name
+ }
+ } else if !identsRx.MatchString(name) {
panic(fmt.Sprintf("invalid name: %q", name))
}
tr := &transformer{name: name, fnc: reflect.ValueOf(f)}
@@ -250,9 +298,9 @@
func (tr *transformer) isFiltered() bool { return tr.typ != nil }
-func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption {
+func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption {
for i := len(s.curPath) - 1; i >= 0; i-- {
- if t, ok := s.curPath[i].(*transform); !ok {
+ if t, ok := s.curPath[i].(Transform); !ok {
break // Hit most recent non-Transform step
} else if tr == t.trans {
return nil // Cannot directly use same Transform
@@ -265,18 +313,15 @@
}
func (tr *transformer) apply(s *state, vx, vy reflect.Value) {
- // Update path before calling the Transformer so that dynamic checks
- // will use the updated path.
- s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr})
- defer s.curPath.pop()
-
- vx = s.callTRFunc(tr.fnc, vx)
- vy = s.callTRFunc(tr.fnc, vy)
- s.compareAny(vx, vy)
+ step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}}
+ vvx := s.callTRFunc(tr.fnc, vx, step)
+ vvy := s.callTRFunc(tr.fnc, vy, step)
+ step.vx, step.vy = vvx, vvy
+ s.compareAny(step)
}
func (tr transformer) String() string {
- return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer()))
+ return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc))
}
// Comparer returns an Option that determines whether two values are equal
@@ -311,7 +356,7 @@
func (cm *comparer) isFiltered() bool { return cm.typ != nil }
-func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption {
+func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption {
if cm.typ == nil || t.AssignableTo(cm.typ) {
return cm
}
@@ -320,16 +365,15 @@
func (cm *comparer) apply(s *state, vx, vy reflect.Value) {
eq := s.callTTBFunc(cm.fnc, vx, vy)
- s.report(eq, vx, vy)
+ s.report(eq, reportByFunc)
}
func (cm comparer) String() string {
- return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer()))
+ return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc))
}
-// AllowUnexported returns an Option that forcibly allows operations on
-// unexported fields in certain structs, which are specified by passing in a
-// value of each struct type.
+// Exporter returns an Option that specifies whether Equal is allowed to
+// introspect into the unexported fields of certain struct types.
//
// Users of this option must understand that comparing on unexported fields
// from external packages is not safe since changes in the internal
@@ -338,7 +382,7 @@
// defined in an internal package where the semantic meaning of an unexported
// field is in the control of the user.
//
-// For some cases, a custom Comparer should be used instead that defines
+// In many cases, a custom Comparer should be used instead that defines
// equality as a function of the public API of a type rather than the underlying
// unexported implementation.
//
@@ -353,10 +397,24 @@
//
// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore
// all unexported fields on specified struct types.
-func AllowUnexported(types ...interface{}) Option {
- if !supportAllowUnexported {
- panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS")
+func Exporter(f func(reflect.Type) bool) Option {
+ if !supportExporters {
+ panic("Exporter is not supported on purego builds")
}
+ return exporter(f)
+}
+
+type exporter func(reflect.Type) bool
+
+func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
+ panic("not implemented")
+}
+
+// AllowUnexported returns an Options that allows Equal to forcibly introspect
+// unexported fields of the specified struct types.
+//
+// See Exporter for the proper use of this option.
+func AllowUnexported(types ...interface{}) Option {
m := make(map[reflect.Type]bool)
for _, typ := range types {
t := reflect.TypeOf(typ)
@@ -365,32 +423,97 @@
}
m[t] = true
}
- return visibleStructs(m)
+ return exporter(func(t reflect.Type) bool { return m[t] })
}
-type visibleStructs map[reflect.Type]bool
-
-func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption {
- panic("not implemented")
+// Result represents the comparison result for a single node and
+// is provided by cmp when calling Result (see Reporter).
+type Result struct {
+ _ [0]func() // Make Result incomparable
+ flags resultFlags
}
-// reporter is an Option that configures how differences are reported.
-type reporter interface {
- // TODO: Not exported yet.
+// Equal reports whether the node was determined to be equal or not.
+// As a special case, ignored nodes are considered equal.
+func (r Result) Equal() bool {
+ return r.flags&(reportEqual|reportByIgnore) != 0
+}
+
+// ByIgnore reports whether the node is equal because it was ignored.
+// This never reports true if Equal reports false.
+func (r Result) ByIgnore() bool {
+ return r.flags&reportByIgnore != 0
+}
+
+// ByMethod reports whether the Equal method determined equality.
+func (r Result) ByMethod() bool {
+ return r.flags&reportByMethod != 0
+}
+
+// ByFunc reports whether a Comparer function determined equality.
+func (r Result) ByFunc() bool {
+ return r.flags&reportByFunc != 0
+}
+
+// ByCycle reports whether a reference cycle was detected.
+func (r Result) ByCycle() bool {
+ return r.flags&reportByCycle != 0
+}
+
+type resultFlags uint
+
+const (
+ _ resultFlags = (1 << iota) / 2
+
+ reportEqual
+ reportUnequal
+ reportByIgnore
+ reportByMethod
+ reportByFunc
+ reportByCycle
+)
+
+// Reporter is an Option that can be passed to Equal. When Equal traverses
+// the value trees, it calls PushStep as it descends into each node in the
+// tree and PopStep as it ascend out of the node. The leaves of the tree are
+// either compared (determined to be equal or not equal) or ignored and reported
+// as such by calling the Report method.
+func Reporter(r interface {
+ // PushStep is called when a tree-traversal operation is performed.
+ // The PathStep itself is only valid until the step is popped.
+ // The PathStep.Values are valid for the duration of the entire traversal
+ // and must not be mutated.
//
- // Perhaps add PushStep and PopStep and change Report to only accept
- // a PathStep instead of the full-path? Adding a PushStep and PopStep makes
- // it clear that we are traversing the value tree in a depth-first-search
- // manner, which has an effect on how values are printed.
+ // Equal always calls PushStep at the start to provide an operation-less
+ // PathStep used to report the root values.
+ //
+ // Within a slice, the exact set of inserted, removed, or modified elements
+ // is unspecified and may change in future implementations.
+ // The entries of a map are iterated through in an unspecified order.
+ PushStep(PathStep)
- Option
+ // Report is called exactly once on leaf nodes to report whether the
+ // comparison identified the node as equal, unequal, or ignored.
+ // A leaf node is one that is immediately preceded by and followed by
+ // a pair of PushStep and PopStep calls.
+ Report(Result)
- // Report is called for every comparison made and will be provided with
- // the two values being compared, the equality result, and the
- // current path in the value tree. It is possible for x or y to be an
- // invalid reflect.Value if one of the values is non-existent;
- // which is possible with maps and slices.
- Report(x, y reflect.Value, eq bool, p Path)
+ // PopStep ascends back up the value tree.
+ // There is always a matching pop call for every push call.
+ PopStep()
+}) Option {
+ return reporter{r}
+}
+
+type reporter struct{ reporterIface }
+type reporterIface interface {
+ PushStep(PathStep)
+ Report(Result)
+ PopStep()
+}
+
+func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
+ panic("not implemented")
}
// normalizeOption normalizes the input options such that all Options groups
@@ -424,30 +547,3 @@
}
return dst
}
-
-// getFuncName returns a short function name from the pointer.
-// The string parsing logic works up until Go1.9.
-func getFuncName(p uintptr) string {
- fnc := runtime.FuncForPC(p)
- if fnc == nil {
- return "<unknown>"
- }
- name := fnc.Name() // E.g., "long/path/name/mypkg.(mytype).(long/path/name/mypkg.myfunc)-fm"
- if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") {
- // Strip the package name from method name.
- name = strings.TrimSuffix(name, ")-fm")
- name = strings.TrimSuffix(name, ")·fm")
- if i := strings.LastIndexByte(name, '('); i >= 0 {
- methodName := name[i+1:] // E.g., "long/path/name/mypkg.myfunc"
- if j := strings.LastIndexByte(methodName, '.'); j >= 0 {
- methodName = methodName[j+1:] // E.g., "myfunc"
- }
- name = name[:i] + methodName // E.g., "long/path/name/mypkg.(mytype)." + "myfunc"
- }
- }
- if i := strings.LastIndexByte(name, '/'); i >= 0 {
- // Strip the package name.
- name = name[i+1:] // E.g., "mypkg.(mytype).myfunc"
- }
- return name
-}
diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go
index c08a3cf..509d6b8 100644
--- a/vendor/github.com/google/go-cmp/cmp/path.go
+++ b/vendor/github.com/google/go-cmp/cmp/path.go
@@ -10,82 +10,56 @@
"strings"
"unicode"
"unicode/utf8"
+
+ "github.com/google/go-cmp/cmp/internal/value"
)
-type (
- // Path is a list of PathSteps describing the sequence of operations to get
- // from some root type to the current position in the value tree.
- // The first Path element is always an operation-less PathStep that exists
- // simply to identify the initial type.
+// Path is a list of PathSteps describing the sequence of operations to get
+// from some root type to the current position in the value tree.
+// The first Path element is always an operation-less PathStep that exists
+// simply to identify the initial type.
+//
+// When traversing structs with embedded structs, the embedded struct will
+// always be accessed as a field before traversing the fields of the
+// embedded struct themselves. That is, an exported field from the
+// embedded struct will never be accessed directly from the parent struct.
+type Path []PathStep
+
+// PathStep is a union-type for specific operations to traverse
+// a value's tree structure. Users of this package never need to implement
+// these types as values of this type will be returned by this package.
+//
+// Implementations of this interface are
+// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform.
+type PathStep interface {
+ String() string
+
+ // Type is the resulting type after performing the path step.
+ Type() reflect.Type
+
+ // Values is the resulting values after performing the path step.
+ // The type of each valid value is guaranteed to be identical to Type.
//
- // When traversing structs with embedded structs, the embedded struct will
- // always be accessed as a field before traversing the fields of the
- // embedded struct themselves. That is, an exported field from the
- // embedded struct will never be accessed directly from the parent struct.
- Path []PathStep
+ // In some cases, one or both may be invalid or have restrictions:
+ // • For StructField, both are not interface-able if the current field
+ // is unexported and the struct type is not explicitly permitted by
+ // an Exporter to traverse unexported fields.
+ // • For SliceIndex, one may be invalid if an element is missing from
+ // either the x or y slice.
+ // • For MapIndex, one may be invalid if an entry is missing from
+ // either the x or y map.
+ //
+ // The provided values must not be mutated.
+ Values() (vx, vy reflect.Value)
+}
- // PathStep is a union-type for specific operations to traverse
- // a value's tree structure. Users of this package never need to implement
- // these types as values of this type will be returned by this package.
- PathStep interface {
- String() string
- Type() reflect.Type // Resulting type after performing the path step
- isPathStep()
- }
-
- // SliceIndex is an index operation on a slice or array at some index Key.
- SliceIndex interface {
- PathStep
- Key() int // May return -1 if in a split state
-
- // SplitKeys returns the indexes for indexing into slices in the
- // x and y values, respectively. These indexes may differ due to the
- // insertion or removal of an element in one of the slices, causing
- // all of the indexes to be shifted. If an index is -1, then that
- // indicates that the element does not exist in the associated slice.
- //
- // Key is guaranteed to return -1 if and only if the indexes returned
- // by SplitKeys are not the same. SplitKeys will never return -1 for
- // both indexes.
- SplitKeys() (x int, y int)
-
- isSliceIndex()
- }
- // MapIndex is an index operation on a map at some index Key.
- MapIndex interface {
- PathStep
- Key() reflect.Value
- isMapIndex()
- }
- // TypeAssertion represents a type assertion on an interface.
- TypeAssertion interface {
- PathStep
- isTypeAssertion()
- }
- // StructField represents a struct field access on a field called Name.
- StructField interface {
- PathStep
- Name() string
- Index() int
- isStructField()
- }
- // Indirect represents pointer indirection on the parent type.
- Indirect interface {
- PathStep
- isIndirect()
- }
- // Transform is a transformation from the parent type to the current type.
- Transform interface {
- PathStep
- Name() string
- Func() reflect.Value
-
- // Option returns the originally constructed Transformer option.
- // The == operator can be used to detect the exact option used.
- Option() Option
-
- isTransform()
- }
+var (
+ _ PathStep = StructField{}
+ _ PathStep = SliceIndex{}
+ _ PathStep = MapIndex{}
+ _ PathStep = Indirect{}
+ _ PathStep = TypeAssertion{}
+ _ PathStep = Transform{}
)
func (pa *Path) push(s PathStep) {
@@ -124,7 +98,7 @@
func (pa Path) String() string {
var ss []string
for _, s := range pa {
- if _, ok := s.(*structField); ok {
+ if _, ok := s.(StructField); ok {
ss = append(ss, s.String())
}
}
@@ -144,13 +118,13 @@
nextStep = pa[i+1]
}
switch s := s.(type) {
- case *indirect:
+ case Indirect:
numIndirect++
pPre, pPost := "(", ")"
switch nextStep.(type) {
- case *indirect:
+ case Indirect:
continue // Next step is indirection, so let them batch up
- case *structField:
+ case StructField:
numIndirect-- // Automatic indirection on struct fields
case nil:
pPre, pPost = "", "" // Last step; no need for parenthesis
@@ -161,19 +135,10 @@
}
numIndirect = 0
continue
- case *transform:
+ case Transform:
ssPre = append(ssPre, s.trans.name+"(")
ssPost = append(ssPost, ")")
continue
- case *typeAssertion:
- // As a special-case, elide type assertions on anonymous types
- // since they are typically generated dynamically and can be very
- // verbose. For example, some transforms return interface{} because
- // of Go's lack of generics, but typically take in and return the
- // exact same concrete type.
- if s.Type().PkgPath() == "" {
- continue
- }
}
ssPost = append(ssPost, s.String())
}
@@ -183,44 +148,13 @@
return strings.Join(ssPre, "") + strings.Join(ssPost, "")
}
-type (
- pathStep struct {
- typ reflect.Type
- }
+type pathStep struct {
+ typ reflect.Type
+ vx, vy reflect.Value
+}
- sliceIndex struct {
- pathStep
- xkey, ykey int
- }
- mapIndex struct {
- pathStep
- key reflect.Value
- }
- typeAssertion struct {
- pathStep
- }
- structField struct {
- pathStep
- name string
- idx int
-
- // These fields are used for forcibly accessing an unexported field.
- // pvx, pvy, and field are only valid if unexported is true.
- unexported bool
- force bool // Forcibly allow visibility
- pvx, pvy reflect.Value // Parent values
- field reflect.StructField // Field information
- }
- indirect struct {
- pathStep
- }
- transform struct {
- pathStep
- trans *transformer
- }
-)
-
-func (ps pathStep) Type() reflect.Type { return ps.typ }
+func (ps pathStep) Type() reflect.Type { return ps.typ }
+func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy }
func (ps pathStep) String() string {
if ps.typ == nil {
return "<nil>"
@@ -232,7 +166,55 @@
return fmt.Sprintf("{%s}", s)
}
-func (si sliceIndex) String() string {
+// StructField represents a struct field access on a field called Name.
+type StructField struct{ *structField }
+type structField struct {
+ pathStep
+ name string
+ idx int
+
+ // These fields are used for forcibly accessing an unexported field.
+ // pvx, pvy, and field are only valid if unexported is true.
+ unexported bool
+ mayForce bool // Forcibly allow visibility
+ pvx, pvy reflect.Value // Parent values
+ field reflect.StructField // Field information
+}
+
+func (sf StructField) Type() reflect.Type { return sf.typ }
+func (sf StructField) Values() (vx, vy reflect.Value) {
+ if !sf.unexported {
+ return sf.vx, sf.vy // CanInterface reports true
+ }
+
+ // Forcibly obtain read-write access to an unexported struct field.
+ if sf.mayForce {
+ vx = retrieveUnexportedField(sf.pvx, sf.field)
+ vy = retrieveUnexportedField(sf.pvy, sf.field)
+ return vx, vy // CanInterface reports true
+ }
+ return sf.vx, sf.vy // CanInterface reports false
+}
+func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
+
+// Name is the field name.
+func (sf StructField) Name() string { return sf.name }
+
+// Index is the index of the field in the parent struct type.
+// See reflect.Type.Field.
+func (sf StructField) Index() int { return sf.idx }
+
+// SliceIndex is an index operation on a slice or array at some index Key.
+type SliceIndex struct{ *sliceIndex }
+type sliceIndex struct {
+ pathStep
+ xkey, ykey int
+ isSlice bool // False for reflect.Array
+}
+
+func (si SliceIndex) Type() reflect.Type { return si.typ }
+func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy }
+func (si SliceIndex) String() string {
switch {
case si.xkey == si.ykey:
return fmt.Sprintf("[%d]", si.xkey)
@@ -247,63 +229,149 @@
return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
}
}
-func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
-func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
-func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) }
-func (in indirect) String() string { return "*" }
-func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
-func (si sliceIndex) Key() int {
+// Key is the index key; it may return -1 if in a split state
+func (si SliceIndex) Key() int {
if si.xkey != si.ykey {
return -1
}
return si.xkey
}
-func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey }
-func (mi mapIndex) Key() reflect.Value { return mi.key }
-func (sf structField) Name() string { return sf.name }
-func (sf structField) Index() int { return sf.idx }
-func (tf transform) Name() string { return tf.trans.name }
-func (tf transform) Func() reflect.Value { return tf.trans.fnc }
-func (tf transform) Option() Option { return tf.trans }
-func (pathStep) isPathStep() {}
-func (sliceIndex) isSliceIndex() {}
-func (mapIndex) isMapIndex() {}
-func (typeAssertion) isTypeAssertion() {}
-func (structField) isStructField() {}
-func (indirect) isIndirect() {}
-func (transform) isTransform() {}
+// SplitKeys are the indexes for indexing into slices in the
+// x and y values, respectively. These indexes may differ due to the
+// insertion or removal of an element in one of the slices, causing
+// all of the indexes to be shifted. If an index is -1, then that
+// indicates that the element does not exist in the associated slice.
+//
+// Key is guaranteed to return -1 if and only if the indexes returned
+// by SplitKeys are not the same. SplitKeys will never return -1 for
+// both indexes.
+func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
-var (
- _ SliceIndex = sliceIndex{}
- _ MapIndex = mapIndex{}
- _ TypeAssertion = typeAssertion{}
- _ StructField = structField{}
- _ Indirect = indirect{}
- _ Transform = transform{}
+// MapIndex is an index operation on a map at some index Key.
+type MapIndex struct{ *mapIndex }
+type mapIndex struct {
+ pathStep
+ key reflect.Value
+}
- _ PathStep = sliceIndex{}
- _ PathStep = mapIndex{}
- _ PathStep = typeAssertion{}
- _ PathStep = structField{}
- _ PathStep = indirect{}
- _ PathStep = transform{}
-)
+func (mi MapIndex) Type() reflect.Type { return mi.typ }
+func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy }
+func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
+
+// Key is the value of the map key.
+func (mi MapIndex) Key() reflect.Value { return mi.key }
+
+// Indirect represents pointer indirection on the parent type.
+type Indirect struct{ *indirect }
+type indirect struct {
+ pathStep
+}
+
+func (in Indirect) Type() reflect.Type { return in.typ }
+func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
+func (in Indirect) String() string { return "*" }
+
+// TypeAssertion represents a type assertion on an interface.
+type TypeAssertion struct{ *typeAssertion }
+type typeAssertion struct {
+ pathStep
+}
+
+func (ta TypeAssertion) Type() reflect.Type { return ta.typ }
+func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
+func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) }
+
+// Transform is a transformation from the parent type to the current type.
+type Transform struct{ *transform }
+type transform struct {
+ pathStep
+ trans *transformer
+}
+
+func (tf Transform) Type() reflect.Type { return tf.typ }
+func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
+func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
+
+// Name is the name of the Transformer.
+func (tf Transform) Name() string { return tf.trans.name }
+
+// Func is the function pointer to the transformer function.
+func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
+
+// Option returns the originally constructed Transformer option.
+// The == operator can be used to detect the exact option used.
+func (tf Transform) Option() Option { return tf.trans }
+
+// pointerPath represents a dual-stack of pointers encountered when
+// recursively traversing the x and y values. This data structure supports
+// detection of cycles and determining whether the cycles are equal.
+// In Go, cycles can occur via pointers, slices, and maps.
+//
+// The pointerPath uses a map to represent a stack; where descension into a
+// pointer pushes the address onto the stack, and ascension from a pointer
+// pops the address from the stack. Thus, when traversing into a pointer from
+// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles
+// by checking whether the pointer has already been visited. The cycle detection
+// uses a seperate stack for the x and y values.
+//
+// If a cycle is detected we need to determine whether the two pointers
+// should be considered equal. The definition of equality chosen by Equal
+// requires two graphs to have the same structure. To determine this, both the
+// x and y values must have a cycle where the previous pointers were also
+// encountered together as a pair.
+//
+// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and
+// MapIndex with pointer information for the x and y values.
+// Suppose px and py are two pointers to compare, we then search the
+// Path for whether px was ever encountered in the Path history of x, and
+// similarly so with py. If either side has a cycle, the comparison is only
+// equal if both px and py have a cycle resulting from the same PathStep.
+//
+// Using a map as a stack is more performant as we can perform cycle detection
+// in O(1) instead of O(N) where N is len(Path).
+type pointerPath struct {
+ // mx is keyed by x pointers, where the value is the associated y pointer.
+ mx map[value.Pointer]value.Pointer
+ // my is keyed by y pointers, where the value is the associated x pointer.
+ my map[value.Pointer]value.Pointer
+}
+
+func (p *pointerPath) Init() {
+ p.mx = make(map[value.Pointer]value.Pointer)
+ p.my = make(map[value.Pointer]value.Pointer)
+}
+
+// Push indicates intent to descend into pointers vx and vy where
+// visited reports whether either has been seen before. If visited before,
+// equal reports whether both pointers were encountered together.
+// Pop must be called if and only if the pointers were never visited.
+//
+// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map
+// and be non-nil.
+func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) {
+ px := value.PointerOf(vx)
+ py := value.PointerOf(vy)
+ _, ok1 := p.mx[px]
+ _, ok2 := p.my[py]
+ if ok1 || ok2 {
+ equal = p.mx[px] == py && p.my[py] == px // Pointers paired together
+ return equal, true
+ }
+ p.mx[px] = py
+ p.my[py] = px
+ return false, false
+}
+
+// Pop ascends from pointers vx and vy.
+func (p pointerPath) Pop(vx, vy reflect.Value) {
+ delete(p.mx, value.PointerOf(vx))
+ delete(p.my, value.PointerOf(vy))
+}
// isExported reports whether the identifier is exported.
func isExported(id string) bool {
r, _ := utf8.DecodeRuneInString(id)
return unicode.IsUpper(r)
}
-
-// isValid reports whether the identifier is valid.
-// Empty and underscore-only strings are not valid.
-func isValid(id string) bool {
- ok := id != "" && id != "_"
- for j, c := range id {
- ok = ok && (j > 0 || !unicode.IsDigit(c))
- ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c))
- }
- return ok
-}
diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go
new file mode 100644
index 0000000..6ddf299
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report.go
@@ -0,0 +1,51 @@
+// Copyright 2017, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+// defaultReporter implements the reporter interface.
+//
+// As Equal serially calls the PushStep, Report, and PopStep methods, the
+// defaultReporter constructs a tree-based representation of the compared value
+// and the result of each comparison (see valueNode).
+//
+// When the String method is called, the FormatDiff method transforms the
+// valueNode tree into a textNode tree, which is a tree-based representation
+// of the textual output (see textNode).
+//
+// Lastly, the textNode.String method produces the final report as a string.
+type defaultReporter struct {
+ root *valueNode
+ curr *valueNode
+}
+
+func (r *defaultReporter) PushStep(ps PathStep) {
+ r.curr = r.curr.PushStep(ps)
+ if r.root == nil {
+ r.root = r.curr
+ }
+}
+func (r *defaultReporter) Report(rs Result) {
+ r.curr.Report(rs)
+}
+func (r *defaultReporter) PopStep() {
+ r.curr = r.curr.PopStep()
+}
+
+// String provides a full report of the differences detected as a structured
+// literal in pseudo-Go syntax. String may only be called after the entire tree
+// has been traversed.
+func (r *defaultReporter) String() string {
+ assert(r.root != nil && r.curr == nil)
+ if r.root.NumDiff == 0 {
+ return ""
+ }
+ return formatOptions{}.FormatDiff(r.root).String()
+}
+
+func assert(ok bool) {
+ if !ok {
+ panic("assertion failure")
+ }
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go
new file mode 100644
index 0000000..17a05ee
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go
@@ -0,0 +1,296 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/google/go-cmp/cmp/internal/value"
+)
+
+// TODO: Enforce limits?
+// * Enforce maximum number of records to print per node?
+// * Enforce maximum size in bytes allowed?
+// * As a heuristic, use less verbosity for equal nodes than unequal nodes.
+// TODO: Enforce unique outputs?
+// * Avoid Stringer methods if it results in same output?
+// * Print pointer address if outputs still equal?
+
+// numContextRecords is the number of surrounding equal records to print.
+const numContextRecords = 2
+
+type diffMode byte
+
+const (
+ diffUnknown diffMode = 0
+ diffIdentical diffMode = ' '
+ diffRemoved diffMode = '-'
+ diffInserted diffMode = '+'
+)
+
+type typeMode int
+
+const (
+ // emitType always prints the type.
+ emitType typeMode = iota
+ // elideType never prints the type.
+ elideType
+ // autoType prints the type only for composite kinds
+ // (i.e., structs, slices, arrays, and maps).
+ autoType
+)
+
+type formatOptions struct {
+ // DiffMode controls the output mode of FormatDiff.
+ //
+ // If diffUnknown, then produce a diff of the x and y values.
+ // If diffIdentical, then emit values as if they were equal.
+ // If diffRemoved, then only emit x values (ignoring y values).
+ // If diffInserted, then only emit y values (ignoring x values).
+ DiffMode diffMode
+
+ // TypeMode controls whether to print the type for the current node.
+ //
+ // As a general rule of thumb, we always print the type of the next node
+ // after an interface, and always elide the type of the next node after
+ // a slice or map node.
+ TypeMode typeMode
+
+ // formatValueOptions are options specific to printing reflect.Values.
+ formatValueOptions
+}
+
+func (opts formatOptions) WithDiffMode(d diffMode) formatOptions {
+ opts.DiffMode = d
+ return opts
+}
+func (opts formatOptions) WithTypeMode(t typeMode) formatOptions {
+ opts.TypeMode = t
+ return opts
+}
+
+// FormatDiff converts a valueNode tree into a textNode tree, where the later
+// is a textual representation of the differences detected in the former.
+func (opts formatOptions) FormatDiff(v *valueNode) textNode {
+ // Check whether we have specialized formatting for this node.
+ // This is not necessary, but helpful for producing more readable outputs.
+ if opts.CanFormatDiffSlice(v) {
+ return opts.FormatDiffSlice(v)
+ }
+
+ // For leaf nodes, format the value based on the reflect.Values alone.
+ if v.MaxDepth == 0 {
+ switch opts.DiffMode {
+ case diffUnknown, diffIdentical:
+ // Format Equal.
+ if v.NumDiff == 0 {
+ outx := opts.FormatValue(v.ValueX, visitedPointers{})
+ outy := opts.FormatValue(v.ValueY, visitedPointers{})
+ if v.NumIgnored > 0 && v.NumSame == 0 {
+ return textEllipsis
+ } else if outx.Len() < outy.Len() {
+ return outx
+ } else {
+ return outy
+ }
+ }
+
+ // Format unequal.
+ assert(opts.DiffMode == diffUnknown)
+ var list textList
+ outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{})
+ outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{})
+ if outx != nil {
+ list = append(list, textRecord{Diff: '-', Value: outx})
+ }
+ if outy != nil {
+ list = append(list, textRecord{Diff: '+', Value: outy})
+ }
+ return opts.WithTypeMode(emitType).FormatType(v.Type, list)
+ case diffRemoved:
+ return opts.FormatValue(v.ValueX, visitedPointers{})
+ case diffInserted:
+ return opts.FormatValue(v.ValueY, visitedPointers{})
+ default:
+ panic("invalid diff mode")
+ }
+ }
+
+ // Descend into the child value node.
+ if v.TransformerName != "" {
+ out := opts.WithTypeMode(emitType).FormatDiff(v.Value)
+ out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"}
+ return opts.FormatType(v.Type, out)
+ } else {
+ switch k := v.Type.Kind(); k {
+ case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map:
+ return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k))
+ case reflect.Ptr:
+ return textWrap{"&", opts.FormatDiff(v.Value), ""}
+ case reflect.Interface:
+ return opts.WithTypeMode(emitType).FormatDiff(v.Value)
+ default:
+ panic(fmt.Sprintf("%v cannot have children", k))
+ }
+ }
+}
+
+func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode {
+ // Derive record name based on the data structure kind.
+ var name string
+ var formatKey func(reflect.Value) string
+ switch k {
+ case reflect.Struct:
+ name = "field"
+ opts = opts.WithTypeMode(autoType)
+ formatKey = func(v reflect.Value) string { return v.String() }
+ case reflect.Slice, reflect.Array:
+ name = "element"
+ opts = opts.WithTypeMode(elideType)
+ formatKey = func(reflect.Value) string { return "" }
+ case reflect.Map:
+ name = "entry"
+ opts = opts.WithTypeMode(elideType)
+ formatKey = formatMapKey
+ }
+
+ // Handle unification.
+ switch opts.DiffMode {
+ case diffIdentical, diffRemoved, diffInserted:
+ var list textList
+ var deferredEllipsis bool // Add final "..." to indicate records were dropped
+ for _, r := range recs {
+ // Elide struct fields that are zero value.
+ if k == reflect.Struct {
+ var isZero bool
+ switch opts.DiffMode {
+ case diffIdentical:
+ isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY)
+ case diffRemoved:
+ isZero = value.IsZero(r.Value.ValueX)
+ case diffInserted:
+ isZero = value.IsZero(r.Value.ValueY)
+ }
+ if isZero {
+ continue
+ }
+ }
+ // Elide ignored nodes.
+ if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 {
+ deferredEllipsis = !(k == reflect.Slice || k == reflect.Array)
+ if !deferredEllipsis {
+ list.AppendEllipsis(diffStats{})
+ }
+ continue
+ }
+ if out := opts.FormatDiff(r.Value); out != nil {
+ list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
+ }
+ }
+ if deferredEllipsis {
+ list.AppendEllipsis(diffStats{})
+ }
+ return textWrap{"{", list, "}"}
+ case diffUnknown:
+ default:
+ panic("invalid diff mode")
+ }
+
+ // Handle differencing.
+ var list textList
+ groups := coalesceAdjacentRecords(name, recs)
+ for i, ds := range groups {
+ // Handle equal records.
+ if ds.NumDiff() == 0 {
+ // Compute the number of leading and trailing records to print.
+ var numLo, numHi int
+ numEqual := ds.NumIgnored + ds.NumIdentical
+ for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 {
+ if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
+ break
+ }
+ numLo++
+ }
+ for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
+ if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 {
+ break
+ }
+ numHi++
+ }
+ if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 {
+ numHi++ // Avoid pointless coalescing of a single equal record
+ }
+
+ // Format the equal values.
+ for _, r := range recs[:numLo] {
+ out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
+ list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
+ }
+ if numEqual > numLo+numHi {
+ ds.NumIdentical -= numLo + numHi
+ list.AppendEllipsis(ds)
+ }
+ for _, r := range recs[numEqual-numHi : numEqual] {
+ out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value)
+ list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
+ }
+ recs = recs[numEqual:]
+ continue
+ }
+
+ // Handle unequal records.
+ for _, r := range recs[:ds.NumDiff()] {
+ switch {
+ case opts.CanFormatDiffSlice(r.Value):
+ out := opts.FormatDiffSlice(r.Value)
+ list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
+ case r.Value.NumChildren == r.Value.MaxDepth:
+ outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value)
+ outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value)
+ if outx != nil {
+ list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx})
+ }
+ if outy != nil {
+ list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy})
+ }
+ default:
+ out := opts.FormatDiff(r.Value)
+ list = append(list, textRecord{Key: formatKey(r.Key), Value: out})
+ }
+ }
+ recs = recs[ds.NumDiff():]
+ }
+ assert(len(recs) == 0)
+ return textWrap{"{", list, "}"}
+}
+
+// coalesceAdjacentRecords coalesces the list of records into groups of
+// adjacent equal, or unequal counts.
+func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) {
+ var prevCase int // Arbitrary index into which case last occurred
+ lastStats := func(i int) *diffStats {
+ if prevCase != i {
+ groups = append(groups, diffStats{Name: name})
+ prevCase = i
+ }
+ return &groups[len(groups)-1]
+ }
+ for _, r := range recs {
+ switch rv := r.Value; {
+ case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0:
+ lastStats(1).NumIgnored++
+ case rv.NumDiff == 0:
+ lastStats(1).NumIdentical++
+ case rv.NumDiff > 0 && !rv.ValueY.IsValid():
+ lastStats(2).NumRemoved++
+ case rv.NumDiff > 0 && !rv.ValueX.IsValid():
+ lastStats(2).NumInserted++
+ default:
+ lastStats(2).NumModified++
+ }
+ }
+ return groups
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go
new file mode 100644
index 0000000..2761b62
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go
@@ -0,0 +1,278 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "unicode"
+
+ "github.com/google/go-cmp/cmp/internal/flags"
+ "github.com/google/go-cmp/cmp/internal/value"
+)
+
+type formatValueOptions struct {
+ // AvoidStringer controls whether to avoid calling custom stringer
+ // methods like error.Error or fmt.Stringer.String.
+ AvoidStringer bool
+
+ // ShallowPointers controls whether to avoid descending into pointers.
+ // Useful when printing map keys, where pointer comparison is performed
+ // on the pointer address rather than the pointed-at value.
+ ShallowPointers bool
+
+ // PrintAddresses controls whether to print the address of all pointers,
+ // slice elements, and maps.
+ PrintAddresses bool
+}
+
+// FormatType prints the type as if it were wrapping s.
+// This may return s as-is depending on the current type and TypeMode mode.
+func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
+ // Check whether to emit the type or not.
+ switch opts.TypeMode {
+ case autoType:
+ switch t.Kind() {
+ case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
+ if s.Equal(textNil) {
+ return s
+ }
+ default:
+ return s
+ }
+ case elideType:
+ return s
+ }
+
+ // Determine the type label, applying special handling for unnamed types.
+ typeName := t.String()
+ if t.Name() == "" {
+ // According to Go grammar, certain type literals contain symbols that
+ // do not strongly bind to the next lexicographical token (e.g., *T).
+ switch t.Kind() {
+ case reflect.Chan, reflect.Func, reflect.Ptr:
+ typeName = "(" + typeName + ")"
+ }
+ typeName = strings.Replace(typeName, "struct {", "struct{", -1)
+ typeName = strings.Replace(typeName, "interface {", "interface{", -1)
+ }
+
+ // Avoid wrap the value in parenthesis if unnecessary.
+ if s, ok := s.(textWrap); ok {
+ hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
+ hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
+ if hasParens || hasBraces {
+ return textWrap{typeName, s, ""}
+ }
+ }
+ return textWrap{typeName + "(", s, ")"}
+}
+
+// FormatValue prints the reflect.Value, taking extra care to avoid descending
+// into pointers already in m. As pointers are visited, m is also updated.
+func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
+ if !v.IsValid() {
+ return nil
+ }
+ t := v.Type()
+
+ // Check whether there is an Error or String method to call.
+ if !opts.AvoidStringer && v.CanInterface() {
+ // Avoid calling Error or String methods on nil receivers since many
+ // implementations crash when doing so.
+ if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
+ switch v := v.Interface().(type) {
+ case error:
+ return textLine("e" + formatString(v.Error()))
+ case fmt.Stringer:
+ return textLine("s" + formatString(v.String()))
+ }
+ }
+ }
+
+ // Check whether to explicitly wrap the result with the type.
+ var skipType bool
+ defer func() {
+ if !skipType {
+ out = opts.FormatType(t, out)
+ }
+ }()
+
+ var ptr string
+ switch t.Kind() {
+ case reflect.Bool:
+ return textLine(fmt.Sprint(v.Bool()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return textLine(fmt.Sprint(v.Int()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ // Unnamed uints are usually bytes or words, so use hexadecimal.
+ if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
+ return textLine(formatHex(v.Uint()))
+ }
+ return textLine(fmt.Sprint(v.Uint()))
+ case reflect.Float32, reflect.Float64:
+ return textLine(fmt.Sprint(v.Float()))
+ case reflect.Complex64, reflect.Complex128:
+ return textLine(fmt.Sprint(v.Complex()))
+ case reflect.String:
+ return textLine(formatString(v.String()))
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ return textLine(formatPointer(v))
+ case reflect.Struct:
+ var list textList
+ for i := 0; i < v.NumField(); i++ {
+ vv := v.Field(i)
+ if value.IsZero(vv) {
+ continue // Elide fields with zero values
+ }
+ s := opts.WithTypeMode(autoType).FormatValue(vv, m)
+ list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
+ }
+ return textWrap{"{", list, "}"}
+ case reflect.Slice:
+ if v.IsNil() {
+ return textNil
+ }
+ if opts.PrintAddresses {
+ ptr = formatPointer(v)
+ }
+ fallthrough
+ case reflect.Array:
+ var list textList
+ for i := 0; i < v.Len(); i++ {
+ vi := v.Index(i)
+ if vi.CanAddr() { // Check for cyclic elements
+ p := vi.Addr()
+ if m.Visit(p) {
+ var out textNode
+ out = textLine(formatPointer(p))
+ out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
+ out = textWrap{"*", out, ""}
+ list = append(list, textRecord{Value: out})
+ continue
+ }
+ }
+ s := opts.WithTypeMode(elideType).FormatValue(vi, m)
+ list = append(list, textRecord{Value: s})
+ }
+ return textWrap{ptr + "{", list, "}"}
+ case reflect.Map:
+ if v.IsNil() {
+ return textNil
+ }
+ if m.Visit(v) {
+ return textLine(formatPointer(v))
+ }
+
+ var list textList
+ for _, k := range value.SortKeys(v.MapKeys()) {
+ sk := formatMapKey(k)
+ sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
+ list = append(list, textRecord{Key: sk, Value: sv})
+ }
+ if opts.PrintAddresses {
+ ptr = formatPointer(v)
+ }
+ return textWrap{ptr + "{", list, "}"}
+ case reflect.Ptr:
+ if v.IsNil() {
+ return textNil
+ }
+ if m.Visit(v) || opts.ShallowPointers {
+ return textLine(formatPointer(v))
+ }
+ if opts.PrintAddresses {
+ ptr = formatPointer(v)
+ }
+ skipType = true // Let the underlying value print the type instead
+ return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
+ case reflect.Interface:
+ if v.IsNil() {
+ return textNil
+ }
+ // Interfaces accept different concrete types,
+ // so configure the underlying value to explicitly print the type.
+ skipType = true // Print the concrete type instead
+ return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
+ default:
+ panic(fmt.Sprintf("%v kind not handled", v.Kind()))
+ }
+}
+
+// formatMapKey formats v as if it were a map key.
+// The result is guaranteed to be a single line.
+func formatMapKey(v reflect.Value) string {
+ var opts formatOptions
+ opts.TypeMode = elideType
+ opts.ShallowPointers = true
+ s := opts.FormatValue(v, visitedPointers{}).String()
+ return strings.TrimSpace(s)
+}
+
+// formatString prints s as a double-quoted or backtick-quoted string.
+func formatString(s string) string {
+ // Use quoted string if it the same length as a raw string literal.
+ // Otherwise, attempt to use the raw string form.
+ qs := strconv.Quote(s)
+ if len(qs) == 1+len(s)+1 {
+ return qs
+ }
+
+ // Disallow newlines to ensure output is a single line.
+ // Only allow printable runes for readability purposes.
+ rawInvalid := func(r rune) bool {
+ return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
+ }
+ if strings.IndexFunc(s, rawInvalid) < 0 {
+ return "`" + s + "`"
+ }
+ return qs
+}
+
+// formatHex prints u as a hexadecimal integer in Go notation.
+func formatHex(u uint64) string {
+ var f string
+ switch {
+ case u <= 0xff:
+ f = "0x%02x"
+ case u <= 0xffff:
+ f = "0x%04x"
+ case u <= 0xffffff:
+ f = "0x%06x"
+ case u <= 0xffffffff:
+ f = "0x%08x"
+ case u <= 0xffffffffff:
+ f = "0x%010x"
+ case u <= 0xffffffffffff:
+ f = "0x%012x"
+ case u <= 0xffffffffffffff:
+ f = "0x%014x"
+ case u <= 0xffffffffffffffff:
+ f = "0x%016x"
+ }
+ return fmt.Sprintf(f, u)
+}
+
+// formatPointer prints the address of the pointer.
+func formatPointer(v reflect.Value) string {
+ p := v.Pointer()
+ if flags.Deterministic {
+ p = 0xdeadf00f // Only used for stable testing purposes
+ }
+ return fmt.Sprintf("⟪0x%x⟫", p)
+}
+
+type visitedPointers map[value.Pointer]struct{}
+
+// Visit inserts pointer v into the visited map and reports whether it had
+// already been visited before.
+func (m visitedPointers) Visit(v reflect.Value) bool {
+ p := value.PointerOf(v)
+ _, visited := m[p]
+ m[p] = struct{}{}
+ return visited
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go
new file mode 100644
index 0000000..eafcf2e
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go
@@ -0,0 +1,333 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/google/go-cmp/cmp/internal/diff"
+)
+
+// CanFormatDiffSlice reports whether we support custom formatting for nodes
+// that are slices of primitive kinds or strings.
+func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
+ switch {
+ case opts.DiffMode != diffUnknown:
+ return false // Must be formatting in diff mode
+ case v.NumDiff == 0:
+ return false // No differences detected
+ case v.NumIgnored+v.NumCompared+v.NumTransformed > 0:
+ // TODO: Handle the case where someone uses bytes.Equal on a large slice.
+ return false // Some custom option was used to determined equality
+ case !v.ValueX.IsValid() || !v.ValueY.IsValid():
+ return false // Both values must be valid
+ }
+
+ switch t := v.Type; t.Kind() {
+ case reflect.String:
+ case reflect.Array, reflect.Slice:
+ // Only slices of primitive types have specialized handling.
+ switch t.Elem().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
+ reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
+ default:
+ return false
+ }
+
+ // If a sufficient number of elements already differ,
+ // use specialized formatting even if length requirement is not met.
+ if v.NumDiff > v.NumSame {
+ return true
+ }
+ default:
+ return false
+ }
+
+ // Use specialized string diffing for longer slices or strings.
+ const minLength = 64
+ return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength
+}
+
+// FormatDiffSlice prints a diff for the slices (or strings) represented by v.
+// This provides custom-tailored logic to make printing of differences in
+// textual strings and slices of primitive kinds more readable.
+func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
+ assert(opts.DiffMode == diffUnknown)
+ t, vx, vy := v.Type, v.ValueX, v.ValueY
+
+ // Auto-detect the type of the data.
+ var isLinedText, isText, isBinary bool
+ var sx, sy string
+ switch {
+ case t.Kind() == reflect.String:
+ sx, sy = vx.String(), vy.String()
+ isText = true // Initial estimate, verify later
+ case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)):
+ sx, sy = string(vx.Bytes()), string(vy.Bytes())
+ isBinary = true // Initial estimate, verify later
+ case t.Kind() == reflect.Array:
+ // Arrays need to be addressable for slice operations to work.
+ vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem()
+ vx2.Set(vx)
+ vy2.Set(vy)
+ vx, vy = vx2, vy2
+ }
+ if isText || isBinary {
+ var numLines, lastLineIdx, maxLineLen int
+ isBinary = false
+ for i, r := range sx + sy {
+ if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError {
+ isBinary = true
+ break
+ }
+ if r == '\n' {
+ if maxLineLen < i-lastLineIdx {
+ maxLineLen = i - lastLineIdx
+ }
+ lastLineIdx = i + 1
+ numLines++
+ }
+ }
+ isText = !isBinary
+ isLinedText = isText && numLines >= 4 && maxLineLen <= 256
+ }
+
+ // Format the string into printable records.
+ var list textList
+ var delim string
+ switch {
+ // If the text appears to be multi-lined text,
+ // then perform differencing across individual lines.
+ case isLinedText:
+ ssx := strings.Split(sx, "\n")
+ ssy := strings.Split(sy, "\n")
+ list = opts.formatDiffSlice(
+ reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line",
+ func(v reflect.Value, d diffMode) textRecord {
+ s := formatString(v.Index(0).String())
+ return textRecord{Diff: d, Value: textLine(s)}
+ },
+ )
+ delim = "\n"
+ // If the text appears to be single-lined text,
+ // then perform differencing in approximately fixed-sized chunks.
+ // The output is printed as quoted strings.
+ case isText:
+ list = opts.formatDiffSlice(
+ reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte",
+ func(v reflect.Value, d diffMode) textRecord {
+ s := formatString(v.String())
+ return textRecord{Diff: d, Value: textLine(s)}
+ },
+ )
+ delim = ""
+ // If the text appears to be binary data,
+ // then perform differencing in approximately fixed-sized chunks.
+ // The output is inspired by hexdump.
+ case isBinary:
+ list = opts.formatDiffSlice(
+ reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte",
+ func(v reflect.Value, d diffMode) textRecord {
+ var ss []string
+ for i := 0; i < v.Len(); i++ {
+ ss = append(ss, formatHex(v.Index(i).Uint()))
+ }
+ s := strings.Join(ss, ", ")
+ comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String())))
+ return textRecord{Diff: d, Value: textLine(s), Comment: comment}
+ },
+ )
+ // For all other slices of primitive types,
+ // then perform differencing in approximately fixed-sized chunks.
+ // The size of each chunk depends on the width of the element kind.
+ default:
+ var chunkSize int
+ if t.Elem().Kind() == reflect.Bool {
+ chunkSize = 16
+ } else {
+ switch t.Elem().Bits() {
+ case 8:
+ chunkSize = 16
+ case 16:
+ chunkSize = 12
+ case 32:
+ chunkSize = 8
+ default:
+ chunkSize = 8
+ }
+ }
+ list = opts.formatDiffSlice(
+ vx, vy, chunkSize, t.Elem().Kind().String(),
+ func(v reflect.Value, d diffMode) textRecord {
+ var ss []string
+ for i := 0; i < v.Len(); i++ {
+ switch t.Elem().Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ ss = append(ss, fmt.Sprint(v.Index(i).Int()))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ ss = append(ss, formatHex(v.Index(i).Uint()))
+ case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
+ ss = append(ss, fmt.Sprint(v.Index(i).Interface()))
+ }
+ }
+ s := strings.Join(ss, ", ")
+ return textRecord{Diff: d, Value: textLine(s)}
+ },
+ )
+ }
+
+ // Wrap the output with appropriate type information.
+ var out textNode = textWrap{"{", list, "}"}
+ if !isText {
+ // The "{...}" byte-sequence literal is not valid Go syntax for strings.
+ // Emit the type for extra clarity (e.g. "string{...}").
+ if t.Kind() == reflect.String {
+ opts = opts.WithTypeMode(emitType)
+ }
+ return opts.FormatType(t, out)
+ }
+ switch t.Kind() {
+ case reflect.String:
+ out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)}
+ if t != reflect.TypeOf(string("")) {
+ out = opts.FormatType(t, out)
+ }
+ case reflect.Slice:
+ out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)}
+ if t != reflect.TypeOf([]byte(nil)) {
+ out = opts.FormatType(t, out)
+ }
+ }
+ return out
+}
+
+// formatASCII formats s as an ASCII string.
+// This is useful for printing binary strings in a semi-legible way.
+func formatASCII(s string) string {
+ b := bytes.Repeat([]byte{'.'}, len(s))
+ for i := 0; i < len(s); i++ {
+ if ' ' <= s[i] && s[i] <= '~' {
+ b[i] = s[i]
+ }
+ }
+ return string(b)
+}
+
+func (opts formatOptions) formatDiffSlice(
+ vx, vy reflect.Value, chunkSize int, name string,
+ makeRec func(reflect.Value, diffMode) textRecord,
+) (list textList) {
+ es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result {
+ return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface())
+ })
+
+ appendChunks := func(v reflect.Value, d diffMode) int {
+ n0 := v.Len()
+ for v.Len() > 0 {
+ n := chunkSize
+ if n > v.Len() {
+ n = v.Len()
+ }
+ list = append(list, makeRec(v.Slice(0, n), d))
+ v = v.Slice(n, v.Len())
+ }
+ return n0 - v.Len()
+ }
+
+ groups := coalesceAdjacentEdits(name, es)
+ groups = coalesceInterveningIdentical(groups, chunkSize/4)
+ for i, ds := range groups {
+ // Print equal.
+ if ds.NumDiff() == 0 {
+ // Compute the number of leading and trailing equal bytes to print.
+ var numLo, numHi int
+ numEqual := ds.NumIgnored + ds.NumIdentical
+ for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 {
+ numLo++
+ }
+ for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 {
+ numHi++
+ }
+ if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 {
+ numHi = numEqual - numLo // Avoid pointless coalescing of single equal row
+ }
+
+ // Print the equal bytes.
+ appendChunks(vx.Slice(0, numLo), diffIdentical)
+ if numEqual > numLo+numHi {
+ ds.NumIdentical -= numLo + numHi
+ list.AppendEllipsis(ds)
+ }
+ appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical)
+ vx = vx.Slice(numEqual, vx.Len())
+ vy = vy.Slice(numEqual, vy.Len())
+ continue
+ }
+
+ // Print unequal.
+ nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved)
+ vx = vx.Slice(nx, vx.Len())
+ ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted)
+ vy = vy.Slice(ny, vy.Len())
+ }
+ assert(vx.Len() == 0 && vy.Len() == 0)
+ return list
+}
+
+// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent
+// equal or unequal counts.
+func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) {
+ var prevCase int // Arbitrary index into which case last occurred
+ lastStats := func(i int) *diffStats {
+ if prevCase != i {
+ groups = append(groups, diffStats{Name: name})
+ prevCase = i
+ }
+ return &groups[len(groups)-1]
+ }
+ for _, e := range es {
+ switch e {
+ case diff.Identity:
+ lastStats(1).NumIdentical++
+ case diff.UniqueX:
+ lastStats(2).NumRemoved++
+ case diff.UniqueY:
+ lastStats(2).NumInserted++
+ case diff.Modified:
+ lastStats(2).NumModified++
+ }
+ }
+ return groups
+}
+
+// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize)
+// equal groups into adjacent unequal groups that currently result in a
+// dual inserted/removed printout. This acts as a high-pass filter to smooth
+// out high-frequency changes within the windowSize.
+func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats {
+ groups, groupsOrig := groups[:0], groups
+ for i, ds := range groupsOrig {
+ if len(groups) >= 2 && ds.NumDiff() > 0 {
+ prev := &groups[len(groups)-2] // Unequal group
+ curr := &groups[len(groups)-1] // Equal group
+ next := &groupsOrig[i] // Unequal group
+ hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0
+ hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0
+ if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize {
+ *prev = prev.Append(*curr).Append(*next)
+ groups = groups[:len(groups)-1] // Truncate off equal group
+ continue
+ }
+ }
+ groups = append(groups, ds)
+ }
+ return groups
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go
new file mode 100644
index 0000000..8b8fcab
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report_text.go
@@ -0,0 +1,387 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+import (
+ "bytes"
+ "fmt"
+ "math/rand"
+ "strings"
+ "time"
+
+ "github.com/google/go-cmp/cmp/internal/flags"
+)
+
+var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0
+
+type indentMode int
+
+func (n indentMode) appendIndent(b []byte, d diffMode) []byte {
+ // The output of Diff is documented as being unstable to provide future
+ // flexibility in changing the output for more humanly readable reports.
+ // This logic intentionally introduces instability to the exact output
+ // so that users can detect accidental reliance on stability early on,
+ // rather than much later when an actual change to the format occurs.
+ if flags.Deterministic || randBool {
+ // Use regular spaces (U+0020).
+ switch d {
+ case diffUnknown, diffIdentical:
+ b = append(b, " "...)
+ case diffRemoved:
+ b = append(b, "- "...)
+ case diffInserted:
+ b = append(b, "+ "...)
+ }
+ } else {
+ // Use non-breaking spaces (U+00a0).
+ switch d {
+ case diffUnknown, diffIdentical:
+ b = append(b, " "...)
+ case diffRemoved:
+ b = append(b, "- "...)
+ case diffInserted:
+ b = append(b, "+ "...)
+ }
+ }
+ return repeatCount(n).appendChar(b, '\t')
+}
+
+type repeatCount int
+
+func (n repeatCount) appendChar(b []byte, c byte) []byte {
+ for ; n > 0; n-- {
+ b = append(b, c)
+ }
+ return b
+}
+
+// textNode is a simplified tree-based representation of structured text.
+// Possible node types are textWrap, textList, or textLine.
+type textNode interface {
+ // Len reports the length in bytes of a single-line version of the tree.
+ // Nested textRecord.Diff and textRecord.Comment fields are ignored.
+ Len() int
+ // Equal reports whether the two trees are structurally identical.
+ // Nested textRecord.Diff and textRecord.Comment fields are compared.
+ Equal(textNode) bool
+ // String returns the string representation of the text tree.
+ // It is not guaranteed that len(x.String()) == x.Len(),
+ // nor that x.String() == y.String() implies that x.Equal(y).
+ String() string
+
+ // formatCompactTo formats the contents of the tree as a single-line string
+ // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment
+ // fields are ignored.
+ //
+ // However, not all nodes in the tree should be collapsed as a single-line.
+ // If a node can be collapsed as a single-line, it is replaced by a textLine
+ // node. Since the top-level node cannot replace itself, this also returns
+ // the current node itself.
+ //
+ // This does not mutate the receiver.
+ formatCompactTo([]byte, diffMode) ([]byte, textNode)
+ // formatExpandedTo formats the contents of the tree as a multi-line string
+ // to the provided buffer. In order for column alignment to operate well,
+ // formatCompactTo must be called before calling formatExpandedTo.
+ formatExpandedTo([]byte, diffMode, indentMode) []byte
+}
+
+// textWrap is a wrapper that concatenates a prefix and/or a suffix
+// to the underlying node.
+type textWrap struct {
+ Prefix string // e.g., "bytes.Buffer{"
+ Value textNode // textWrap | textList | textLine
+ Suffix string // e.g., "}"
+}
+
+func (s textWrap) Len() int {
+ return len(s.Prefix) + s.Value.Len() + len(s.Suffix)
+}
+func (s1 textWrap) Equal(s2 textNode) bool {
+ if s2, ok := s2.(textWrap); ok {
+ return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix
+ }
+ return false
+}
+func (s textWrap) String() string {
+ var d diffMode
+ var n indentMode
+ _, s2 := s.formatCompactTo(nil, d)
+ b := n.appendIndent(nil, d) // Leading indent
+ b = s2.formatExpandedTo(b, d, n) // Main body
+ b = append(b, '\n') // Trailing newline
+ return string(b)
+}
+func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
+ n0 := len(b) // Original buffer length
+ b = append(b, s.Prefix...)
+ b, s.Value = s.Value.formatCompactTo(b, d)
+ b = append(b, s.Suffix...)
+ if _, ok := s.Value.(textLine); ok {
+ return b, textLine(b[n0:])
+ }
+ return b, s
+}
+func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
+ b = append(b, s.Prefix...)
+ b = s.Value.formatExpandedTo(b, d, n)
+ b = append(b, s.Suffix...)
+ return b
+}
+
+// textList is a comma-separated list of textWrap or textLine nodes.
+// The list may be formatted as multi-lines or single-line at the discretion
+// of the textList.formatCompactTo method.
+type textList []textRecord
+type textRecord struct {
+ Diff diffMode // e.g., 0 or '-' or '+'
+ Key string // e.g., "MyField"
+ Value textNode // textWrap | textLine
+ Comment fmt.Stringer // e.g., "6 identical fields"
+}
+
+// AppendEllipsis appends a new ellipsis node to the list if none already
+// exists at the end. If cs is non-zero it coalesces the statistics with the
+// previous diffStats.
+func (s *textList) AppendEllipsis(ds diffStats) {
+ hasStats := ds != diffStats{}
+ if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) {
+ if hasStats {
+ *s = append(*s, textRecord{Value: textEllipsis, Comment: ds})
+ } else {
+ *s = append(*s, textRecord{Value: textEllipsis})
+ }
+ return
+ }
+ if hasStats {
+ (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds)
+ }
+}
+
+func (s textList) Len() (n int) {
+ for i, r := range s {
+ n += len(r.Key)
+ if r.Key != "" {
+ n += len(": ")
+ }
+ n += r.Value.Len()
+ if i < len(s)-1 {
+ n += len(", ")
+ }
+ }
+ return n
+}
+
+func (s1 textList) Equal(s2 textNode) bool {
+ if s2, ok := s2.(textList); ok {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i := range s1 {
+ r1, r2 := s1[i], s2[i]
+ if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
+
+func (s textList) String() string {
+ return textWrap{"{", s, "}"}.String()
+}
+
+func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
+ s = append(textList(nil), s...) // Avoid mutating original
+
+ // Determine whether we can collapse this list as a single line.
+ n0 := len(b) // Original buffer length
+ var multiLine bool
+ for i, r := range s {
+ if r.Diff == diffInserted || r.Diff == diffRemoved {
+ multiLine = true
+ }
+ b = append(b, r.Key...)
+ if r.Key != "" {
+ b = append(b, ": "...)
+ }
+ b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff)
+ if _, ok := s[i].Value.(textLine); !ok {
+ multiLine = true
+ }
+ if r.Comment != nil {
+ multiLine = true
+ }
+ if i < len(s)-1 {
+ b = append(b, ", "...)
+ }
+ }
+ // Force multi-lined output when printing a removed/inserted node that
+ // is sufficiently long.
+ if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 {
+ multiLine = true
+ }
+ if !multiLine {
+ return b, textLine(b[n0:])
+ }
+ return b, s
+}
+
+func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte {
+ alignKeyLens := s.alignLens(
+ func(r textRecord) bool {
+ _, isLine := r.Value.(textLine)
+ return r.Key == "" || !isLine
+ },
+ func(r textRecord) int { return len(r.Key) },
+ )
+ alignValueLens := s.alignLens(
+ func(r textRecord) bool {
+ _, isLine := r.Value.(textLine)
+ return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil
+ },
+ func(r textRecord) int { return len(r.Value.(textLine)) },
+ )
+
+ // Format the list as a multi-lined output.
+ n++
+ for i, r := range s {
+ b = n.appendIndent(append(b, '\n'), d|r.Diff)
+ if r.Key != "" {
+ b = append(b, r.Key+": "...)
+ }
+ b = alignKeyLens[i].appendChar(b, ' ')
+
+ b = r.Value.formatExpandedTo(b, d|r.Diff, n)
+ if !r.Value.Equal(textEllipsis) {
+ b = append(b, ',')
+ }
+ b = alignValueLens[i].appendChar(b, ' ')
+
+ if r.Comment != nil {
+ b = append(b, " // "+r.Comment.String()...)
+ }
+ }
+ n--
+
+ return n.appendIndent(append(b, '\n'), d)
+}
+
+func (s textList) alignLens(
+ skipFunc func(textRecord) bool,
+ lenFunc func(textRecord) int,
+) []repeatCount {
+ var startIdx, endIdx, maxLen int
+ lens := make([]repeatCount, len(s))
+ for i, r := range s {
+ if skipFunc(r) {
+ for j := startIdx; j < endIdx && j < len(s); j++ {
+ lens[j] = repeatCount(maxLen - lenFunc(s[j]))
+ }
+ startIdx, endIdx, maxLen = i+1, i+1, 0
+ } else {
+ if maxLen < lenFunc(r) {
+ maxLen = lenFunc(r)
+ }
+ endIdx = i + 1
+ }
+ }
+ for j := startIdx; j < endIdx && j < len(s); j++ {
+ lens[j] = repeatCount(maxLen - lenFunc(s[j]))
+ }
+ return lens
+}
+
+// textLine is a single-line segment of text and is always a leaf node
+// in the textNode tree.
+type textLine []byte
+
+var (
+ textNil = textLine("nil")
+ textEllipsis = textLine("...")
+)
+
+func (s textLine) Len() int {
+ return len(s)
+}
+func (s1 textLine) Equal(s2 textNode) bool {
+ if s2, ok := s2.(textLine); ok {
+ return bytes.Equal([]byte(s1), []byte(s2))
+ }
+ return false
+}
+func (s textLine) String() string {
+ return string(s)
+}
+func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) {
+ return append(b, s...), s
+}
+func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte {
+ return append(b, s...)
+}
+
+type diffStats struct {
+ Name string
+ NumIgnored int
+ NumIdentical int
+ NumRemoved int
+ NumInserted int
+ NumModified int
+}
+
+func (s diffStats) NumDiff() int {
+ return s.NumRemoved + s.NumInserted + s.NumModified
+}
+
+func (s diffStats) Append(ds diffStats) diffStats {
+ assert(s.Name == ds.Name)
+ s.NumIgnored += ds.NumIgnored
+ s.NumIdentical += ds.NumIdentical
+ s.NumRemoved += ds.NumRemoved
+ s.NumInserted += ds.NumInserted
+ s.NumModified += ds.NumModified
+ return s
+}
+
+// String prints a humanly-readable summary of coalesced records.
+//
+// Example:
+// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields"
+func (s diffStats) String() string {
+ var ss []string
+ var sum int
+ labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"}
+ counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified}
+ for i, n := range counts {
+ if n > 0 {
+ ss = append(ss, fmt.Sprintf("%d %v", n, labels[i]))
+ }
+ sum += n
+ }
+
+ // Pluralize the name (adjusting for some obscure English grammar rules).
+ name := s.Name
+ if sum > 1 {
+ name += "s"
+ if strings.HasSuffix(name, "ys") {
+ name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries"
+ }
+ }
+
+ // Format the list according to English grammar (with Oxford comma).
+ switch n := len(ss); n {
+ case 0:
+ return ""
+ case 1, 2:
+ return strings.Join(ss, " and ") + " " + name
+ default:
+ return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name
+ }
+}
+
+type commentString string
+
+func (s commentString) String() string { return string(s) }
diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go
new file mode 100644
index 0000000..83031a7
--- /dev/null
+++ b/vendor/github.com/google/go-cmp/cmp/report_value.go
@@ -0,0 +1,121 @@
+// Copyright 2019, The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE.md file.
+
+package cmp
+
+import "reflect"
+
+// valueNode represents a single node within a report, which is a
+// structured representation of the value tree, containing information
+// regarding which nodes are equal or not.
+type valueNode struct {
+ parent *valueNode
+
+ Type reflect.Type
+ ValueX reflect.Value
+ ValueY reflect.Value
+
+ // NumSame is the number of leaf nodes that are equal.
+ // All descendants are equal only if NumDiff is 0.
+ NumSame int
+ // NumDiff is the number of leaf nodes that are not equal.
+ NumDiff int
+ // NumIgnored is the number of leaf nodes that are ignored.
+ NumIgnored int
+ // NumCompared is the number of leaf nodes that were compared
+ // using an Equal method or Comparer function.
+ NumCompared int
+ // NumTransformed is the number of non-leaf nodes that were transformed.
+ NumTransformed int
+ // NumChildren is the number of transitive descendants of this node.
+ // This counts from zero; thus, leaf nodes have no descendants.
+ NumChildren int
+ // MaxDepth is the maximum depth of the tree. This counts from zero;
+ // thus, leaf nodes have a depth of zero.
+ MaxDepth int
+
+ // Records is a list of struct fields, slice elements, or map entries.
+ Records []reportRecord // If populated, implies Value is not populated
+
+ // Value is the result of a transformation, pointer indirect, of
+ // type assertion.
+ Value *valueNode // If populated, implies Records is not populated
+
+ // TransformerName is the name of the transformer.
+ TransformerName string // If non-empty, implies Value is populated
+}
+type reportRecord struct {
+ Key reflect.Value // Invalid for slice element
+ Value *valueNode
+}
+
+func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) {
+ vx, vy := ps.Values()
+ child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy}
+ switch s := ps.(type) {
+ case StructField:
+ assert(parent.Value == nil)
+ parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child})
+ case SliceIndex:
+ assert(parent.Value == nil)
+ parent.Records = append(parent.Records, reportRecord{Value: child})
+ case MapIndex:
+ assert(parent.Value == nil)
+ parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child})
+ case Indirect:
+ assert(parent.Value == nil && parent.Records == nil)
+ parent.Value = child
+ case TypeAssertion:
+ assert(parent.Value == nil && parent.Records == nil)
+ parent.Value = child
+ case Transform:
+ assert(parent.Value == nil && parent.Records == nil)
+ parent.Value = child
+ parent.TransformerName = s.Name()
+ parent.NumTransformed++
+ default:
+ assert(parent == nil) // Must be the root step
+ }
+ return child
+}
+
+func (r *valueNode) Report(rs Result) {
+ assert(r.MaxDepth == 0) // May only be called on leaf nodes
+
+ if rs.ByIgnore() {
+ r.NumIgnored++
+ } else {
+ if rs.Equal() {
+ r.NumSame++
+ } else {
+ r.NumDiff++
+ }
+ }
+ assert(r.NumSame+r.NumDiff+r.NumIgnored == 1)
+
+ if rs.ByMethod() {
+ r.NumCompared++
+ }
+ if rs.ByFunc() {
+ r.NumCompared++
+ }
+ assert(r.NumCompared <= 1)
+}
+
+func (child *valueNode) PopStep() (parent *valueNode) {
+ if child.parent == nil {
+ return nil
+ }
+ parent = child.parent
+ parent.NumSame += child.NumSame
+ parent.NumDiff += child.NumDiff
+ parent.NumIgnored += child.NumIgnored
+ parent.NumCompared += child.NumCompared
+ parent.NumTransformed += child.NumTransformed
+ parent.NumChildren += child.NumChildren + 1
+ if parent.MaxDepth < child.MaxDepth+1 {
+ parent.MaxDepth = child.MaxDepth + 1
+ }
+ return parent
+}
diff --git a/vendor/github.com/google/go-cmp/cmp/reporter.go b/vendor/github.com/google/go-cmp/cmp/reporter.go
deleted file mode 100644
index 20e9f18..0000000
--- a/vendor/github.com/google/go-cmp/cmp/reporter.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017, The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.md file.
-
-package cmp
-
-import (
- "fmt"
- "reflect"
- "strings"
-
- "github.com/google/go-cmp/cmp/internal/value"
-)
-
-type defaultReporter struct {
- Option
- diffs []string // List of differences, possibly truncated
- ndiffs int // Total number of differences
- nbytes int // Number of bytes in diffs
- nlines int // Number of lines in diffs
-}
-
-var _ reporter = (*defaultReporter)(nil)
-
-func (r *defaultReporter) Report(x, y reflect.Value, eq bool, p Path) {
- if eq {
- return // Ignore equal results
- }
- const maxBytes = 4096
- const maxLines = 256
- r.ndiffs++
- if r.nbytes < maxBytes && r.nlines < maxLines {
- sx := value.Format(x, value.FormatConfig{UseStringer: true})
- sy := value.Format(y, value.FormatConfig{UseStringer: true})
- if sx == sy {
- // Unhelpful output, so use more exact formatting.
- sx = value.Format(x, value.FormatConfig{PrintPrimitiveType: true})
- sy = value.Format(y, value.FormatConfig{PrintPrimitiveType: true})
- }
- s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy)
- r.diffs = append(r.diffs, s)
- r.nbytes += len(s)
- r.nlines += strings.Count(s, "\n")
- }
-}
-
-func (r *defaultReporter) String() string {
- s := strings.Join(r.diffs, "")
- if r.ndiffs == len(r.diffs) {
- return s
- }
- return fmt.Sprintf("%s... %d more differences ...", s, r.ndiffs-len(r.diffs))
-}
diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go b/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go
deleted file mode 100644
index d1518eb..0000000
--- a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2017, The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.md file.
-
-// +build purego appengine js
-
-package cmp
-
-import "reflect"
-
-const supportAllowUnexported = false
-
-func unsafeRetrieveField(reflect.Value, reflect.StructField) reflect.Value {
- panic("unsafeRetrieveField is not implemented")
-}
diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go b/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go
deleted file mode 100644
index 579b655..0000000
--- a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017, The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.md file.
-
-// +build !purego,!appengine,!js
-
-package cmp
-
-import (
- "reflect"
- "unsafe"
-)
-
-const supportAllowUnexported = true
-
-// unsafeRetrieveField uses unsafe to forcibly retrieve any field from a struct
-// such that the value has read-write permissions.
-//
-// The parent struct, v, must be addressable, while f must be a StructField
-// describing the field to retrieve.
-func unsafeRetrieveField(v reflect.Value, f reflect.StructField) reflect.Value {
- return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem()
-}