Source File
compare.go
Belonging Package
github.com/google/go-cmp/cmp
// 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 file.
// Package cmp determines equality of values.
//
// This package is intended to be a more powerful and safer alternative to
// reflect.DeepEqual for comparing whether two values are semantically equal.
// It is intended to only be used in tests, as performance is not a goal and
// it may panic if it cannot compare the values. Its propensity towards
// panicking means that its unsuitable for production environments where a
// spurious panic may be fatal.
//
// The primary features of cmp are:
//
// - When the default behavior of equality does not suit the test's needs,
// custom equality functions can override the equality operation.
// For example, an equality function may report floats as equal so long as
// they are within some tolerance of each other.
//
// - Types with an Equal method may use that method to determine equality.
// This allows package authors to determine the equality operation
// for the types that they define.
//
// - If no custom equality functions are used and no Equal method is defined,
// 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 Exporter option.
package cmp
import (
)
// TODO(≥go1.18): Use any instead of interface{}.
// 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:
//
// - 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.
// If the number of Transformer and Comparer options in S is non-zero,
// then Equal panics because it is ambiguous which option to use.
// If S contains a single Transformer, then use that to transform
// the current values and recursively call Equal on the output values.
// If S contains a single Comparer, then use that to compare the current values.
// Otherwise, evaluation proceeds to the next rule.
//
// - 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.
//
// - 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.
//
// 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.
//
// 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 (, interface{}, ...Option) bool {
:= newState()
.compareAny(rootStep(, ))
return .result.Equal()
}
// Diff returns a human-readable report of the differences between two values:
// y - x. It returns an empty string if and only if Equal returns true for the
// same input values and options.
//
// 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 from 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 (, interface{}, ...Option) string {
:= newState()
// Optimization: If there are no other reporters, we can optimize for the
// common case where the result is equal (and thus no reported difference).
// This avoids the expensive construction of a difference tree.
if len(.reporters) == 0 {
.compareAny(rootStep(, ))
if .result.Equal() {
return ""
}
.result = diff.Result{} // Reset results
}
:= new(defaultReporter)
.reporters = append(.reporters, reporter{})
.compareAny(rootStep(, ))
:= .String()
if ( == "") != .result.Equal() {
panic("inconsistent difference and equality results")
}
return
}
// rootStep constructs the first path step. If x and y have differing types,
// then they are stored within an empty interface type.
func (, interface{}) PathStep {
:= reflect.ValueOf()
:= reflect.ValueOf()
// If the inputs are different types, auto-wrap them in an empty interface
// so that they have the same parent type.
var reflect.Type
if !.IsValid() || !.IsValid() || .Type() != .Type() {
= anyType
if .IsValid() {
:= reflect.New().Elem()
.Set()
=
}
if .IsValid() {
:= reflect.New().Elem()
.Set()
=
}
} else {
= .Type()
}
return &pathStep{, , }
}
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
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 []exporter // List of exporters for structs with unexported fields
opts Options // List of all fundamental and filter options
}
func ( []Option) *state {
// Always ensure a validator option exists to validate the inputs.
:= &state{opts: Options{validator{}}}
.curPtrs.Init()
.processOption(Options())
return
}
func ( *state) ( Option) {
switch opt := .(type) {
case nil:
case Options:
for , := range {
.()
}
case coreOption:
type interface {
() bool
}
if , := .(); && !.() {
panic(fmt.Sprintf("cannot use an unfiltered option: %v", ))
}
.opts = append(.opts, )
case exporter:
.exporters = append(.exporters, )
case reporter:
.reporters = append(.reporters, )
default:
panic(fmt.Sprintf("unknown option %T", ))
}
}
// 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 ( *state) ( 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.
, := .result, .reporters
.result = diff.Result{} // Reset result
.reporters = nil // Remove reporters to avoid spurious printouts
.compareAny()
:= .result
.result, .reporters = ,
return
}
func ( *state) ( PathStep) {
// Update the path stack.
.curPath.push()
defer .curPath.pop()
for , := range .reporters {
.PushStep()
defer .PopStep()
}
.recChecker.Check(.curPath)
// Cycle-detection for slice elements (see NOTE in compareSlice).
:= .Type()
, := .Values()
if , := .(SliceIndex); && .isSlice && .IsValid() && .IsValid() {
, := .Addr(), .Addr()
if , := .curPtrs.Push(, ); {
.report(, reportByCycle)
return
}
defer .curPtrs.Pop(, )
}
// Rule 1: Check whether an option applies on this node in the value tree.
if .tryOptions(, , ) {
return
}
// Rule 2: Check whether the type has a valid Equal method.
if .tryMethod(, , ) {
return
}
// Rule 3: Compare based on the underlying kind.
switch .Kind() {
case reflect.Bool:
.report(.Bool() == .Bool(), 0)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
.report(.Int() == .Int(), 0)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
.report(.Uint() == .Uint(), 0)
case reflect.Float32, reflect.Float64:
.report(.Float() == .Float(), 0)
case reflect.Complex64, reflect.Complex128:
.report(.Complex() == .Complex(), 0)
case reflect.String:
.report(.String() == .String(), 0)
case reflect.Chan, reflect.UnsafePointer:
.report(.Pointer() == .Pointer(), 0)
case reflect.Func:
.report(.IsNil() && .IsNil(), 0)
case reflect.Struct:
.compareStruct(, , )
case reflect.Slice, reflect.Array:
.compareSlice(, , )
case reflect.Map:
.compareMap(, , )
case reflect.Ptr:
.comparePtr(, , )
case reflect.Interface:
.compareInterface(, , )
default:
panic(fmt.Sprintf("%v kind not handled", .Kind()))
}
}
func ( *state) ( reflect.Type, , reflect.Value) bool {
// Evaluate all filters and apply the remaining options.
if := .opts.filter(, , , ); != nil {
.apply(, , )
return true
}
return false
}
func ( *state) ( reflect.Type, , reflect.Value) bool {
// Check if this type even has an Equal method.
, := .MethodByName("Equal")
if ! || !function.IsType(.Type, function.EqualAssignable) {
return false
}
:= .callTTBFunc(.Func, , )
.report(, reportByMethod)
return true
}
func ( *state) (, reflect.Value, Transform) reflect.Value {
if !.dynChecker.Next() {
return .Call([]reflect.Value{})[0]
}
// Run the function twice and ensure that we get the same results back.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
:= make(chan reflect.Value)
go detectRaces(, , )
:= <-
:= .Call([]reflect.Value{})[0]
if .vx, .vy = , ; !.statelessCompare().Equal() {
// To avoid false-positives with non-reflexive equality operations,
// we sanity check whether a value is equal to itself.
if .vx, .vy = , ; !.statelessCompare().Equal() {
return
}
panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf()))
}
return
}
func ( *state) (, , reflect.Value) bool {
if !.dynChecker.Next() {
return .Call([]reflect.Value{, })[0].Bool()
}
// Swapping the input arguments is sufficient to check that
// f is symmetric and deterministic.
// We run in goroutines so that the race detector (if enabled) can detect
// unsafe mutations to the input.
:= make(chan reflect.Value)
go detectRaces(, , , )
:= <-
:= .Call([]reflect.Value{, })[0].Bool()
if !.IsValid() || .Bool() != {
panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf()))
}
return
}
func ( chan<- reflect.Value, reflect.Value, ...reflect.Value) {
var reflect.Value
defer func() {
recover() // Ignore panics, let the other call to f panic instead
<-
}()
= .Call()[0]
}
func ( *state) ( reflect.Type, , reflect.Value) {
var bool
var , reflect.Value // Addressable versions of vx and vy
var , bool
:= StructField{&structField{}}
for := 0; < .NumField(); ++ {
.typ = .Field().Type
.vx = .Field()
.vy = .Field()
.name = .Field().Name
.idx =
.unexported = !isExported(.name)
if .unexported {
if .name == "_" {
continue
}
// Defer checking of unexported fields until later to give an
// Ignore a chance to ignore the field.
if !.IsValid() || !.IsValid() {
// For retrieveUnexportedField to work, the parent struct must
// be addressable. Create a new copy of the values if
// necessary to make them addressable.
= .CanAddr() || .CanAddr()
= makeAddressable()
= makeAddressable()
}
if ! {
for , := range .exporters {
= || ()
}
= true
}
.mayForce =
.paddr =
.pvx =
.pvy =
.field = .Field()
}
.compareAny()
}
}
func ( *state) ( reflect.Type, , reflect.Value) {
:= .Kind() == reflect.Slice
if && (.IsNil() || .IsNil()) {
.report(.IsNil() && .IsNil(), 0)
return
}
// 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.
:= SliceIndex{&sliceIndex{pathStep: pathStep{typ: .Elem()}, isSlice: }}
:= func(, int) SliceIndex {
if >= 0 {
.vx, .xkey = .Index(),
} else {
.vx, .xkey = reflect.Value{}, -1
}
if >= 0 {
.vy, .ykey = .Index(),
} else {
.vy, .ykey = reflect.Value{}, -1
}
return
}
// 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 , []int
var , []bool
for := 0; < .Len(); ++ {
:= .statelessCompare((, -1)).NumDiff == 0
if ! {
= append(, )
}
= append(, )
}
for := 0; < .Len(); ++ {
:= .statelessCompare((-1, )).NumDiff == 0
if ! {
= append(, )
}
= append(, )
}
// Compute an edit-script for slices vx and vy (excluding ignored elements).
:= diff.Difference(len(), len(), func(, int) diff.Result {
return .statelessCompare(([], []))
})
// Replay the ignore-scripts and the edit-script.
var , int
for < .Len() || < .Len() {
var diff.EditType
switch {
case < len() && []:
= diff.UniqueX
case < len() && []:
= diff.UniqueY
default:
, = [0], [1:]
}
switch {
case diff.UniqueX:
.compareAny((, -1))
++
case diff.UniqueY:
.compareAny((-1, ))
++
default:
.compareAny((, ))
++
++
}
}
}
func ( *state) ( reflect.Type, , reflect.Value) {
if .IsNil() || .IsNil() {
.report(.IsNil() && .IsNil(), 0)
return
}
// Cycle-detection for maps.
if , := .curPtrs.Push(, ); {
.report(, reportByCycle)
return
}
defer .curPtrs.Pop(, )
// We combine and sort the two map keys so that we can perform the
// comparisons in a deterministic order.
:= MapIndex{&mapIndex{pathStep: pathStep{typ: .Elem()}}}
for , := range value.SortKeys(append(.MapKeys(), .MapKeys()...)) {
.vx = .MapIndex()
.vy = .MapIndex()
.key =
if !.vx.IsValid() && !.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 = "consider providing a Comparer to compare the map"
panic(fmt.Sprintf("%#v has map key with NaNs\n%s", .curPath, ))
}
.compareAny()
}
}
func ( *state) ( reflect.Type, , reflect.Value) {
if .IsNil() || .IsNil() {
.report(.IsNil() && .IsNil(), 0)
return
}
// Cycle-detection for pointers.
if , := .curPtrs.Push(, ); {
.report(, reportByCycle)
return
}
defer .curPtrs.Pop(, )
, = .Elem(), .Elem()
.compareAny(Indirect{&indirect{pathStep{.Elem(), , }}})
}
func ( *state) ( reflect.Type, , reflect.Value) {
if .IsNil() || .IsNil() {
.report(.IsNil() && .IsNil(), 0)
return
}
, = .Elem(), .Elem()
if .Type() != .Type() {
.report(false, 0)
return
}
.compareAny(TypeAssertion{&typeAssertion{pathStep{.Type(), , }}})
}
func ( *state) ( bool, resultFlags) {
if &reportByIgnore == 0 {
if {
.result.NumSame++
|= reportEqual
} else {
.result.NumDiff++
|= reportUnequal
}
}
for , := range .reporters {
.Report(Result{flags: })
}
}
// 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 ( *recChecker) ( Path) {
const = 1 << 16
if .next == 0 {
.next =
}
if len() < .next {
return
}
.next <<= 1
// Check whether the same transformer has appeared at least twice.
var []string
:= map[Option]int{}
for , := range {
if , := .(Transform); {
:= .Option()
if [] == 1 { // Transformer was used exactly once before
:= .(*transformer).fnc.Type()
= append(, fmt.Sprintf("%v: %v => %v", , .In(0), .Out(0)))
}
[]++
}
}
if len() > 0 {
const = "recursive set of Transformers detected"
const = "consider using cmpopts.AcyclicTransformer"
:= strings.Join(, "\n\t")
panic(fmt.Sprintf("%s:\n\t%s\n%s", , , ))
}
}
// dynChecker tracks the state needed to periodically perform checks that
// user provided functions are symmetric and deterministic.
// The zero value is safe for immediate use.
type dynChecker struct{ curr, next int }
// Next increments the state and reports whether a check should be performed.
//
// Checks occur every Nth function call, where N is a triangular number:
//
// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ...
//
// See https://en.wikipedia.org/wiki/Triangular_number
//
// This sequence ensures that the cost of checks drops significantly as
// the number of functions calls grows larger.
func ( *dynChecker) () bool {
:= .curr == .next
if {
.curr = 0
.next++
}
.curr++
return
}
// makeAddressable returns a value that is always addressable.
// It returns the input verbatim if it is already addressable,
// otherwise it creates a new value and returns an addressable copy.
func ( reflect.Value) reflect.Value {
if .CanAddr() {
return
}
:= reflect.New(.Type()).Elem()
.Set()
return
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)