/
td_trunc_time.go
132 lines (112 loc) · 3.51 KB
/
td_trunc_time.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2018, Maxime Soulé
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
package td
import (
"fmt"
"reflect"
"time"
"github.com/maxatome/go-testdeep/internal/color"
"github.com/maxatome/go-testdeep/internal/ctxerr"
"github.com/maxatome/go-testdeep/internal/types"
)
type tdTruncTime struct {
tdExpectedType
expectedTime time.Time
trunc time.Duration
}
var _ TestDeep = &tdTruncTime{}
// summary(TruncTime): compares time.Time (or assignable) values after
// truncating them
// input(TruncTime): struct(time.Time),ptr(todo)
// TruncTime operator compares time.Time (or assignable) values after
// truncating them to the optional "trunc" duration. See time.Truncate
// for details about the truncation.
//
// If "trunc" is missing, it defaults to 0.
//
// During comparison, location does not matter as time.Equal method is
// used behind the scenes: a time instant in two different locations
// is the same time instant.
//
// Whatever the "trunc" value is, the monotonic clock is stripped
// before the comparison against "expectedTime".
//
// gotDate := time.Date(2018, time.March, 9, 1, 2, 3, 999999999, time.UTC).
// In(time.FixedZone("UTC+2", 2))
//
// expected := time.Date(2018, time.March, 9, 1, 2, 3, 0, time.UTC)
//
// td.Cmp(t, gotDate, td.TruncTime(expected)) // fails, ns differ
// td.Cmp(t, gotDate, td.TruncTime(expected, time.Second)) // succeeds
//
// TypeBehind method returns the reflect.Type of "expectedTime".
func TruncTime(expectedTime interface{}, trunc ...time.Duration) TestDeep {
const usage = "TruncTime(time.Time[, time.Duration])"
if len(trunc) > 1 {
panic(color.TooManyParams(usage))
}
t := tdTruncTime{
tdExpectedType: tdExpectedType{
base: newBase(3),
},
}
if len(trunc) == 1 {
t.trunc = trunc[0]
}
vval := reflect.ValueOf(expectedTime)
t.expectedType = vval.Type()
if t.expectedType == timeType {
t.expectedTime = expectedTime.(time.Time).Truncate(t.trunc)
return &t
}
if t.expectedType.ConvertibleTo(timeType) {
t.expectedTime = vval.Convert(timeType).
Interface().(time.Time).Truncate(t.trunc)
return &t
}
panic(color.Bad("usage: %s, 1st parameter must be time.Time or convertible to time.Time, but not %T",
usage, expectedTime))
}
func (t *tdTruncTime) Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error {
err := t.checkType(ctx, got)
if err != nil {
return ctx.CollectError(err)
}
gotTime, err := getTime(ctx, got, got.Type() != timeType)
if err != nil {
return ctx.CollectError(err)
}
gotTimeTrunc := gotTime.Truncate(t.trunc)
if gotTimeTrunc.Equal(t.expectedTime) {
return nil
}
// Fail
if ctx.BooleanError {
return ctxerr.BooleanError
}
var gotRawStr, gotTruncStr string
if t.expectedType != timeType &&
t.expectedType.Implements(stringerInterface) {
gotRawStr = got.Interface().(fmt.Stringer).String()
gotTruncStr = reflect.ValueOf(gotTimeTrunc).Convert(t.expectedType).
Interface().(fmt.Stringer).String()
} else {
gotRawStr = gotTime.String()
gotTruncStr = gotTimeTrunc.String()
}
return ctx.CollectError(&ctxerr.Error{
Message: "values differ",
Got: types.RawString(gotRawStr + "\ntruncated to:\n" + gotTruncStr),
Expected: t,
})
}
func (t *tdTruncTime) String() string {
if t.expectedType.Implements(stringerInterface) {
return reflect.ValueOf(t.expectedTime).Convert(t.expectedType).
Interface().(fmt.Stringer).String()
}
return t.expectedTime.String()
}