// 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 (e.g., [time.Time.Equal]) 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 [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported])// or explicitly compared using the [Exporter] option.
package cmpimport ()// 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., [github.com/google/go-cmp/cmp/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 [github.com/google/go-cmp/cmp/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// [github.com/google/go-cmp/cmp/cmpopts.SortMaps].// Empty non-nil maps and nil maps are not equal; to equate empty maps,// consider using [github.com/google/go-cmp/cmp/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.iflen(.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.varreflect.Typeif !.IsValid() || !.IsValid() || .Type() != .Type() { = anyTypeif .IsValid() { := reflect.New().Elem() .Set() = }if .IsValid() { := reflect.New().Elem() .Set() = } } else { = .Type() }return &pathStep{, , }}typestatestruct {// These fields represent the "comparison state". // Calling statelessCompare must not result in observable changes to these.resultdiff.Result// The current result of comparisoncurPathPath// The current path in the value treecurPtrspointerPath// The current set of visited pointersreporters []reporter// Optional reporters// recChecker checks for infinite cycles applying the same set of // transformers upon the output of itself.recCheckerrecChecker// dynChecker triggers pseudo-random checks for option correctness. // It is safe for statelessCompare to mutate this value.dynCheckerdynChecker// These fields, once set by processOption, will not change.exporters []exporter// List of exporters for structs with unexported fieldsoptsOptions// 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) {casenil:caseOptions:for , := range { .() }casecoreOption:typeinterface { () bool }if , := .(); && !.() {panic(fmt.Sprintf("cannot use an unfiltered option: %v", )) } .opts = append(.opts, )caseexporter: .exporters = append(.exporters, )casereporter: .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() {casereflect.Bool: .report(.Bool() == .Bool(), 0)casereflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: .report(.Int() == .Int(), 0)casereflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: .report(.Uint() == .Uint(), 0)casereflect.Float32, reflect.Float64: .report(.Float() == .Float(), 0)casereflect.Complex64, reflect.Complex128: .report(.Complex() == .Complex(), 0)casereflect.String: .report(.String() == .String(), 0)casereflect.Chan, reflect.UnsafePointer: .report(.Pointer() == .Pointer(), 0)casereflect.Func: .report(.IsNil() && .IsNil(), 0)casereflect.Struct: .compareStruct(, , )casereflect.Slice, reflect.Array: .compareSlice(, , )casereflect.Map: .compareMap(, , )casereflect.Ptr: .comparePtr(, , )casereflect.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(, , )returntrue }returnfalse}func ( *state) ( reflect.Type, , reflect.Value) bool {// Check if this type even has an Equal method. , := .MethodByName("Equal")if ! || !function.IsType(.Type, function.EqualAssignable) {returnfalse } := .callTTBFunc(.Func, , ) .report(, reportByMethod)returntrue}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(chanreflect.Value)godetectRaces(, , ) := <- := .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(chanreflect.Value)godetectRaces(, , , ) := <- := .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) {varreflect.Valuedeferfunc() {recover() // Ignore panics, let the other call to f panic instead <- }() = .Call()[0]}func ( *state) ( reflect.Type, , reflect.Value) {varboolvar , reflect.Value// Addressable versions of vx and vyvar , 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.Sliceif && (.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 , []intvar , []boolfor := 0; < .Len(); ++ { := .statelessCompare((, -1)).NumDiff == 0if ! { = append(, ) } = append(, ) }for := 0; < .Len(); ++ { := .statelessCompare((-1, )).NumDiff == 0if ! { = 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 , intfor < .Len() || < .Len() {vardiff.EditTypeswitch {case < len() && []: = diff.UniqueXcase < len() && []: = diff.UniqueYdefault: , = [0], [1:] }switch {casediff.UniqueX: .compareAny((, -1)) ++casediff.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 , := rangevalue.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.typerecCheckerstruct{ nextint }// 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 << 16if .next == 0 { .next = }iflen() < .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))) } []++ } }iflen() > 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.typedynCheckerstruct{ curr, nextint }// 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 == .nextif { .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 Goldsv0.7.6. (GOOS=linux GOARCH=amd64)