Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust heuristic for line-based versus byte-based diffing (#299) #312

Closed
wants to merge 100 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
a02fa9f
Refactor reporter implementation (#112)
dsnet Feb 26, 2019
7d31622
Rename unsafe_x.go as export_x.go (#117)
dsnet Feb 26, 2019
3e44f05
Rename {NDiff,NSame} as {NumDiff,NumSame} (#118)
dsnet Feb 26, 2019
ce5a20d
Update to go1.12 (#120)
dsnet Feb 26, 2019
7586b66
Add Values method to PathStep (#119)
dsnet Feb 26, 2019
cc11d21
Augment Report to indicate how comparison was determined (#122)
dsnet Feb 26, 2019
fd81a2b
Evaluate options even if values are invalid (#121)
dsnet Feb 27, 2019
64cb04e
Add BenchmarkBytes (#125)
dsnet Feb 27, 2019
2e500c5
Add validator option once at state creation (#128)
dsnet Feb 28, 2019
c812816
Nudge people to use custom comparers rather than Ignore/Allow Unexpor…
LMMilewski Feb 28, 2019
2940eda
Implement a unified difference reporter (#124)
dsnet Mar 12, 2019
3177a94
Export the Reporter API (#123)
dsnet Mar 12, 2019
0376dcf
Add IgnoreSliceElements and IgnoreMapEntries helpers (#126)
dsnet Mar 12, 2019
49488b4
Use concrete types for path steps (#129)
dsnet Mar 12, 2019
b5cce89
Implement specialized diffing for slices (#131)
dsnet Mar 12, 2019
6f77996
Improve clarity of compareAny (#132)
dsnet Mar 12, 2019
917e382
Invoke String when formatting map keys (#142)
crawshaw May 27, 2019
1b31600
Document the reason for output instability (#145)
dsnet Jun 6, 2019
00cb0dc
Fixed typo in formatDiffList (#148)
muesli Aug 1, 2019
6d8cafd
Simplify code (#149)
muesli Aug 1, 2019
208900a
Fix updating of maxLineLen (#147)
muesli Aug 5, 2019
2d0692c
cmp/internal/value: fix handling of negative zero for floats (#152)
dsnet Aug 5, 2019
b1c9c48
cmpopts: add EquateApproxTime (#158)
rogpeppe Aug 29, 2019
481baca
Make retrieveUnexportedField pass Go 1.14's checkptr validation (#169)
bradfitz Oct 28, 2019
776445f
Print type name in unexported panic (#171)
dsnet Nov 5, 2019
e1f03df
Add Exporter option (#176)
dsnet Dec 16, 2019
3838af3
Adjust style of EquateApproxTime (#177)
dsnet Dec 16, 2019
340f1eb
Add EquateErrors helper (#178)
dsnet Dec 16, 2019
5a6f757
Add support for comparing graphs (#85)
dsnet Dec 16, 2019
5915021
Update README.md to use go.dev for documentation (#190)
dsnet Feb 27, 2020
6fdcbe1
Update tested Go versions (#188)
dsnet Feb 27, 2020
f6dc95b
Document the test-only intentions of this package (#189)
dsnet Feb 27, 2020
cb8c7f8
Fix typo on example (#193)
morrowc Mar 29, 2020
049b73f
Add reporterTests to TestDiff (#198)
178inaba May 13, 2020
0c08307
Refactor tests to use golden test files (#200)
dsnet May 13, 2020
7e5cb83
Format units in decimal except bytes (#199)
178inaba May 15, 2020
aa7c82a
Do not use custom format for nil slice (#201)
at-ishikawa May 17, 2020
d08c604
Permit use of IgnoreFields with unexported fields (#203)
dsnet May 20, 2020
4a83f56
Optimize Diff for frequent equality (#204)
dsnet May 26, 2020
1776240
Forcibly export fields for use by the reporter
dsnet Jun 8, 2020
11c4583
Avoid leaking implementation details of the exporter (#206)
dsnet Jun 8, 2020
23a2b56
Fix exporter to handle nil interface values (#207)
dsnet Jun 9, 2020
367e530
Mention minimally supported Go version in TODO (#209)
dsnet Jun 10, 2020
9b30031
Batch reporter output for simple lists of textLine elements (#208)
dsnet Jun 10, 2020
88849e8
Allow batched diffing of slices with a custom comparer (#210)
dsnet Jun 10, 2020
a171aa7
Use raw string literal syntax only for valid UTF-8 (#211)
dsnet Jun 10, 2020
0cd6169
Use custom triple-quote syntax for diffing string literals (#212)
dsnet Jun 10, 2020
7c9a834
Introduce deliberate instability to difference output (#214)
dsnet Jun 10, 2020
0d296f9
Limit number of printed differences for variable-length composites (#…
dsnet Jun 10, 2020
f1780cf
Limit verbosity of reporter output (#215)
dsnet Jun 12, 2020
44914b3
Disambiguate reporter output (#216)
dsnet Jun 12, 2020
1227731
Fix documentation on IgnoreFields (#220)
dsnet Jun 17, 2020
c49bfce
Update test case names (#218)
dsnet Jun 17, 2020
77ae86f
Improve reporting of values with cycles (#217)
dsnet Jun 18, 2020
d669b04
Swallow panic when calling String or Error (#221)
dsnet Jun 22, 2020
1536a0c
Adjust panic for IgnoreUnexported and IgnoreFields (#228)
ko30005 Jul 14, 2020
9680bfa
Use triple-quote formatting for multiline strings (#229)
dsnet Jul 21, 2020
036ffc7
Fix Diff documentation (#231)
ernest-galbrun Jul 29, 2020
db9de43
Add testing for Go1.15 (#232)
dsnet Aug 12, 2020
d2fcc89
Suggest use of cmpopts.EquateErrors (#234)
dsnet Aug 18, 2020
d713870
Fix license headers (#236)
dsnet Sep 23, 2020
566225a
Add an example for IgnoreFields (#205)
colinnewell Oct 4, 2020
ab46b8b
Adjust for reflect.Type.NumMethod change in Go1.16 (#240)
dsnet Oct 20, 2020
d3c8501
Revert "Adjust for reflect.Type.NumMethod change in Go1.16 (#240)" (#…
dsnet Nov 12, 2020
0a3ecd3
Fix Diff documentation (#237)
dsnet Nov 12, 2020
ade6b74
Use GitHub actions for testing (#246)
dsnet Nov 23, 2020
449e17c
Fix non-determinism in diffing algorithm (#247)
dsnet Nov 24, 2020
ec71d6d
Impose verbosity limit when formatting map keys (#248)
dsnet Nov 24, 2020
3a98a11
cmp/cmpopts: use errors.Is with ≥go1.13 in compareErrors (#251)
tklauser Feb 5, 2021
e9947a2
Run tests on Go 1.16 (#252)
tklauser Feb 20, 2021
dc6435e
De-virtualize interfaces for specialized diffing (#254)
dsnet Mar 3, 2021
8fa37b4
Fix reporter verbosity bug (#253)
dsnet Mar 3, 2021
1ee4af8
Fix typo in path.go (#256)
eltociear Apr 12, 2021
c5c3378
Cleanup edit groups after coalescing (#259)
dsnet May 25, 2021
9181d1e
Avoid diffing by lines if inefficient (#260)
dsnet May 25, 2021
d103655
Print as text if mostly text (#258)
dsnet May 25, 2021
248ccff
Fix staticcheck findings (#262)
dsnet May 27, 2021
290a6a2
Avoid shadowing variable (#263)
dsnet May 27, 2021
d5fcb38
Fix textual printing of byte slices
dsnet Jul 19, 2021
402949e
Merge pull request #266 from dsnet/fix-format
neild Jul 19, 2021
395a0ac
Use sha256 in test (#268)
dsnet Jul 22, 2021
3ee52c8
Fix spelling mistakes (#271)
dsnet Sep 16, 2021
9094ef9
Change build status badge (#269)
jbl428 Sep 16, 2021
f1773ad
Use any alias instead of interface{} (#276)
dsnet Oct 12, 2021
6faefd0
Reduce minimum length for specialize string diffing (#275)
dsnet Oct 12, 2021
f59cd61
Update minimum supported version to go1.11 (#281)
neild Dec 7, 2021
3242228
Drop hacks to work around Go reflection bugs in Go1.9 (#282)
dsnet Dec 7, 2021
039e37c
Add //go:build lines (#285)
tklauser Jan 4, 2022
79433ac
Run tests on Go 1.18 (#290)
Aoang Mar 22, 2022
4664e24
Fix printing of types in reporter output (#293)
dsnet Apr 25, 2022
71220fc
Use string formatting for slice of bytes (#294)
dsnet Apr 25, 2022
63c2960
remove xerrors (#292)
catatsuy Apr 26, 2022
f144a35
Additional cleanup with Go 1.13 as minimal version (#295)
dsnet Apr 26, 2022
a53d7e0
Use reflect.Value.IsZero (#297)
dsnet Jun 6, 2022
14ad8a0
Format with Go 1.19 formatter (#304)
dsnet Jul 13, 2022
5dac6aa
Fix typo in Result documentation (#300)
dsnet Aug 30, 2022
f36a68d
Pre-declare global type variables (#302)
dsnet Aug 30, 2022
6606d4d
Use value.TypeString in PathStep.String (#306)
dsnet Aug 30, 2022
377d283
Run tests on Go 1.19 (#309)
Aoang Aug 31, 2022
a97318b
Adjust heuristic for line-based versus byte-based diffing (#299)
dsnet Sep 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
@@ -0,0 +1,21 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.13.x, 1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x, 1.19.x]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Test
run: go test -v -race ./...
- name: Format
if: matrix.go-version == '1.19.x'
run: diff -u <(echo -n) <(gofmt -d .)
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

10 changes: 5 additions & 5 deletions README.md
@@ -1,7 +1,7 @@
# Package for equality of Go values

[![GoDoc](https://godoc.org/github.com/google/go-cmp/cmp?status.svg)][godoc]
[![Build Status](https://travis-ci.org/google/go-cmp.svg?branch=master)][travis]
[![GoDev](https://img.shields.io/static/v1?label=godev&message=reference&color=00add8)][godev]
[![Build Status](https://github.com/google/go-cmp/actions/workflows/test.yml/badge.svg?branch=master)][actions]

This package is intended to be a more powerful and safer alternative to
`reflect.DeepEqual` for comparing whether two values are semantically equal.
Expand All @@ -24,12 +24,12 @@ The primary features of `cmp` are:
by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly
compared using the `AllowUnexported` option.

See the [GoDoc documentation][godoc] for more information.
See the [documentation][godev] for more information.

This is not an official Google product.

[godoc]: https://godoc.org/github.com/google/go-cmp/cmp
[travis]: https://travis-ci.org/google/go-cmp
[godev]: https://pkg.go.dev/github.com/google/go-cmp/cmp
[actions]: https://github.com/google/go-cmp/actions

## Install

Expand Down
69 changes: 68 additions & 1 deletion cmp/cmpopts/equate.go
@@ -1,13 +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.
// license that can be found in the LICENSE file.

// Package cmpopts provides common options for the cmp package.
package cmpopts

import (
"errors"
"math"
"reflect"
"time"

"github.com/google/go-cmp/cmp"
)
Expand Down Expand Up @@ -40,6 +42,7 @@ func isEmpty(x, y interface{}) bool {
// The fraction and margin must be non-negative.
//
// The mathematical expression used is equivalent to:
//
// |x-y| ≤ max(fraction*min(|x|, |y|), margin)
//
// EquateApprox can be used in conjunction with EquateNaNs.
Expand Down Expand Up @@ -87,3 +90,67 @@ func areNaNsF64s(x, y float64) bool {
func areNaNsF32s(x, y float32) bool {
return areNaNsF64s(float64(x), float64(y))
}

// EquateApproxTime returns a Comparer option that determines two non-zero
// time.Time values to be equal if they are within some margin of one another.
// If both times have a monotonic clock reading, then the monotonic time
// difference will be used. The margin must be non-negative.
func EquateApproxTime(margin time.Duration) cmp.Option {
if margin < 0 {
panic("margin must be a non-negative number")
}
a := timeApproximator{margin}
return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare))
}

func areNonZeroTimes(x, y time.Time) bool {
return !x.IsZero() && !y.IsZero()
}

type timeApproximator struct {
margin time.Duration
}

func (a timeApproximator) compare(x, y time.Time) bool {
// Avoid subtracting times to avoid overflow when the
// difference is larger than the largest representable duration.
if x.After(y) {
// Ensure x is always before y
x, y = y, x
}
// We're within the margin if x+margin >= y.
// Note: time.Time doesn't have AfterOrEqual method hence the negation.
return !x.Add(a.margin).Before(y)
}

// AnyError is an error that matches any non-nil error.
var AnyError anyError

type anyError struct{}

func (anyError) Error() string { return "any error" }
func (anyError) Is(err error) bool { return err != nil }

// EquateErrors returns a Comparer option that determines errors to be equal
// if errors.Is reports them to match. The AnyError error can be used to
// match any non-nil error.
func EquateErrors() cmp.Option {
return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors))
}

// areConcreteErrors reports whether x and y are types that implement error.
// The input types are deliberately of the interface{} type rather than the
// error type so that we can handle situations where the current type is an
// interface{}, but the underlying concrete types both happen to implement
// the error interface.
func areConcreteErrors(x, y interface{}) bool {
_, ok1 := x.(error)
_, ok2 := y.(error)
return ok1 && ok2
}

func compareErrors(x, y interface{}) bool {
xe := x.(error)
ye := y.(error)
return errors.Is(xe, ye) || errors.Is(ye, xe)
}
130 changes: 130 additions & 0 deletions cmp/cmpopts/example_test.go
@@ -0,0 +1,130 @@
// Copyright 2020, 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 cmpopts_test

import (
"fmt"
"net"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/go-cmp/cmp/internal/flags"
)

func init() {
flags.Deterministic = true
}

// Use IgnoreFields to ignore fields on a struct type when comparing
// by providing a value of the type and the field names to ignore.
// Typically, a zero value of the type is used (e.g., foo.MyStruct{}).
func ExampleIgnoreFields_testing() {
// Let got be the hypothetical value obtained from some logic under test
// and want be the expected golden data.
got, want := MakeGatewayInfo()

// While the specified fields will be semantically ignored for the comparison,
// the fields may be printed in the diff when displaying entire values
// that are already determined to be different.
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(Client{}, "IPAddress")); diff != "" {
t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff)
}

// Output:
// MakeGatewayInfo() mismatch (-want +got):
// cmpopts_test.Gateway{
// SSID: "CoffeeShopWiFi",
// - IPAddress: s"192.168.0.2",
// + IPAddress: s"192.168.0.1",
// NetMask: s"ffff0000",
// Clients: []cmpopts_test.Client{
// ... // 3 identical elements
// {Hostname: "espresso", ...},
// {Hostname: "latte", LastSeen: s"2009-11-10 23:00:23 +0000 UTC", ...},
// + {
// + Hostname: "americano",
// + IPAddress: s"192.168.0.188",
// + LastSeen: s"2009-11-10 23:03:05 +0000 UTC",
// + },
// },
// }
}

type (
Gateway struct {
SSID string
IPAddress net.IP
NetMask net.IPMask
Clients []Client
}
Client struct {
Hostname string
IPAddress net.IP
LastSeen time.Time
}
)

func MakeGatewayInfo() (x, y Gateway) {
x = Gateway{
SSID: "CoffeeShopWiFi",
IPAddress: net.IPv4(192, 168, 0, 1),
NetMask: net.IPv4Mask(255, 255, 0, 0),
Clients: []Client{{
Hostname: "ristretto",
IPAddress: net.IPv4(192, 168, 0, 116),
}, {
Hostname: "aribica",
IPAddress: net.IPv4(192, 168, 0, 104),
LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
}, {
Hostname: "macchiato",
IPAddress: net.IPv4(192, 168, 0, 153),
LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
}, {
Hostname: "espresso",
IPAddress: net.IPv4(192, 168, 0, 121),
}, {
Hostname: "latte",
IPAddress: net.IPv4(192, 168, 0, 219),
LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
}, {
Hostname: "americano",
IPAddress: net.IPv4(192, 168, 0, 188),
LastSeen: time.Date(2009, time.November, 10, 23, 3, 5, 0, time.UTC),
}},
}
y = Gateway{
SSID: "CoffeeShopWiFi",
IPAddress: net.IPv4(192, 168, 0, 2),
NetMask: net.IPv4Mask(255, 255, 0, 0),
Clients: []Client{{
Hostname: "ristretto",
IPAddress: net.IPv4(192, 168, 0, 116),
}, {
Hostname: "aribica",
IPAddress: net.IPv4(192, 168, 0, 104),
LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
}, {
Hostname: "macchiato",
IPAddress: net.IPv4(192, 168, 0, 153),
LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
}, {
Hostname: "espresso",
IPAddress: net.IPv4(192, 168, 0, 121),
}, {
Hostname: "latte",
IPAddress: net.IPv4(192, 168, 0, 221),
LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
}},
}
return x, y
}

var t fakeT

type fakeT struct{}

func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }