diff options
author | Roger Peppe <rogpeppe@gmail.com> | 2019-08-30 00:54:27 +0200 |
---|---|---|
committer | Joe Tsai <joetsai@digital-static.net> | 2019-08-29 15:54:27 -0700 |
commit | b1c9c4891a6525d98001fea424c8926c6d77bb56 (patch) | |
tree | 916d9bb570740fd541c0a918a4bd40af1b0094d9 /cmp/cmpopts | |
parent | 2d0692c2e9617365a95b295612ac0d4415ba4627 (diff) | |
download | go-cmp-b1c9c4891a6525d98001fea424c8926c6d77bb56.tar.gz |
cmpopts: add EquateApproxTime (#158)
Diffstat (limited to 'cmp/cmpopts')
-rw-r--r-- | cmp/cmpopts/equate.go | 36 | ||||
-rw-r--r-- | cmp/cmpopts/util_test.go | 64 |
2 files changed, 100 insertions, 0 deletions
diff --git a/cmp/cmpopts/equate.go b/cmp/cmpopts/equate.go index 41bbddc..fea57bc 100644 --- a/cmp/cmpopts/equate.go +++ b/cmp/cmpopts/equate.go @@ -8,6 +8,7 @@ package cmpopts import ( "math" "reflect" + "time" "github.com/google/go-cmp/cmp" ) @@ -87,3 +88,38 @@ func areNaNsF64s(x, y float64) bool { func areNaNsF32s(x, y float32) bool { return areNaNsF64s(float64(x), float64(y)) } + +// EquateApproxTime returns a Comparer options that +// determine two time.Time values to be equal if they +// are within the given time interval of one another. +// Note that if both times have a monotonic clock reading, +// the monotonic time difference will be used. +// +// The zero time is treated specially: it is only considered +// equal to another zero time value. +// +// It will panic if margin is negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("negative duration in EquateApproxTime") + } + return cmp.FilterValues(func(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() + }, cmp.Comparer(timeApproximator{margin}.compare)) +} + +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 representible 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) +} diff --git a/cmp/cmpopts/util_test.go b/cmp/cmpopts/util_test.go index ed0fbb1..3c01cac 100644 --- a/cmp/cmpopts/util_test.go +++ b/cmp/cmpopts/util_test.go @@ -451,6 +451,64 @@ func TestOptions(t *testing.T) { wantEqual: true, reason: "equal because named type is transformed to float64", }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(0)}, + wantEqual: true, + reason: "equal because times are identical", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because time is exactly at the allowed margin", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because time is exactly at the allowed margin (negative)", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3*time.Second - 1)}, + reason: "not equal because time is outside allowed margin", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3*time.Second - 1)}, + reason: "not equal because time is outside allowed margin (negative)", + }, { + label: "EquateApproxTime", + x: time.Time{}, + y: time.Time{}, + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because both times are zero", + }, { + label: "EquateApproxTime", + x: time.Time{}, + y: time.Time{}.Add(1), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + reason: "not equal because zero time is always not equal not non-zero", + }, { + label: "EquateApproxTime", + x: time.Time{}.Add(1), + y: time.Time{}, + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + reason: "not equal because zero time is always not equal not non-zero", + }, { + label: "EquateApproxTime", + x: time.Date(2409, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2000, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + reason: "time difference overflows time.Duration", + }, { label: "IgnoreFields", x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, @@ -889,6 +947,12 @@ func TestPanic(t *testing.T) { args: args(0.0, math.Inf(+1)), reason: "margin of infinity is valid", }, { + label: "EquateApproxTime", + fnc: EquateApproxTime, + args: args(time.Duration(-1)), + wantPanic: "negative duration in EquateApproxTime", + reason: "negative duration is invalid", + }, { label: "SortSlices", fnc: SortSlices, args: args(strings.Compare), |