Skip to content

Commit

Permalink
cmpopts: add EquateApproxTime (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
rogpeppe authored and dsnet committed Aug 29, 2019
1 parent 2d0692c commit b1c9c48
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
36 changes: 36 additions & 0 deletions cmp/cmpopts/equate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package cmpopts
import (
"math"
"reflect"
"time"

"github.com/google/go-cmp/cmp"
)
Expand Down Expand Up @@ -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)
}
64 changes: 64 additions & 0 deletions cmp/cmpopts/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,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}}}},
Expand Down Expand Up @@ -888,6 +946,12 @@ func TestPanic(t *testing.T) {
fnc: EquateApprox,
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,
Expand Down

0 comments on commit b1c9c48

Please sign in to comment.