aboutsummaryrefslogtreecommitdiff
path: root/cmp/cmpopts
diff options
context:
space:
mode:
authorRoger Peppe <rogpeppe@gmail.com>2019-08-30 00:54:27 +0200
committerJoe Tsai <joetsai@digital-static.net>2019-08-29 15:54:27 -0700
commitb1c9c4891a6525d98001fea424c8926c6d77bb56 (patch)
tree916d9bb570740fd541c0a918a4bd40af1b0094d9 /cmp/cmpopts
parent2d0692c2e9617365a95b295612ac0d4415ba4627 (diff)
downloadgo-cmp-b1c9c4891a6525d98001fea424c8926c6d77bb56.tar.gz
cmpopts: add EquateApproxTime (#158)
Diffstat (limited to 'cmp/cmpopts')
-rw-r--r--cmp/cmpopts/equate.go36
-rw-r--r--cmp/cmpopts/util_test.go64
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),