1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package zapcore_test
22
23 import (
24 "errors"
25 "fmt"
26 "math"
27 "net/url"
28 "testing"
29 "time"
30
31 "github.com/stretchr/testify/assert"
32 "github.com/stretchr/testify/require"
33 "go.uber.org/zap"
34
35
36 . "go.uber.org/zap/zapcore"
37 )
38
39 type users int
40
41 func (u users) String() string {
42 return fmt.Sprintf("%d users", int(u))
43 }
44
45 func (u users) MarshalLogObject(enc ObjectEncoder) error {
46 if int(u) < 0 {
47 return errors.New("too few users")
48 }
49 enc.AddInt("users", int(u))
50 return nil
51 }
52
53 func (u users) MarshalLogArray(enc ArrayEncoder) error {
54 if int(u) < 0 {
55 return errors.New("too few users")
56 }
57 for i := 0; i < int(u); i++ {
58 enc.AppendString("user")
59 }
60 return nil
61 }
62
63 type obj struct {
64 kind int
65 }
66
67 func (o *obj) String() string {
68 if o == nil {
69 return "nil obj"
70 }
71
72 if o.kind == 1 {
73 panic("panic with string")
74 } else if o.kind == 2 {
75 panic(errors.New("panic with error"))
76 } else if o.kind == 3 {
77
78
79 panic((*url.URL)(nil))
80 }
81
82 return "obj"
83 }
84
85 type errObj struct {
86 kind int
87 errMsg string
88 }
89
90 func (eobj *errObj) Error() string {
91 if eobj.kind == 1 {
92 panic("panic in Error() method")
93 }
94 return eobj.errMsg
95 }
96
97 func TestUnknownFieldType(t *testing.T) {
98 unknown := Field{Key: "k", String: "foo"}
99 assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.")
100 assert.Panics(t, func() {
101 unknown.AddTo(NewMapObjectEncoder())
102 }, "Expected using a field with unknown type to panic.")
103 }
104
105 func TestFieldAddingError(t *testing.T) {
106 var empty interface{}
107 tests := []struct {
108 t FieldType
109 iface interface{}
110 want interface{}
111 err string
112 }{
113 {t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: "too few users"},
114 {t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: "too few users"},
115 {t: InlineMarshalerType, iface: users(-1), want: nil, err: "too few users"},
116 {t: StringerType, iface: obj{}, want: empty, err: "PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String"},
117 {t: StringerType, iface: &obj{1}, want: empty, err: "PANIC=panic with string"},
118 {t: StringerType, iface: &obj{2}, want: empty, err: "PANIC=panic with error"},
119 {t: StringerType, iface: &obj{3}, want: empty, err: "PANIC=<nil>"},
120 {t: ErrorType, iface: &errObj{kind: 1}, want: empty, err: "PANIC=panic in Error() method"},
121 }
122 for _, tt := range tests {
123 f := Field{Key: "k", Interface: tt.iface, Type: tt.t}
124 enc := NewMapObjectEncoder()
125 assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.")
126 assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.")
127 assert.Equal(t, tt.err, enc.Fields["kError"], "Expected error message in log context.")
128 }
129 }
130
131 func TestFields(t *testing.T) {
132 tests := []struct {
133 t FieldType
134 i int64
135 s string
136 iface interface{}
137 want interface{}
138 }{
139 {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}},
140 {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}},
141 {t: BoolType, i: 0, want: false},
142 {t: ByteStringType, iface: []byte("foo"), want: "foo"},
143 {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},
144 {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},
145 {t: DurationType, i: 1000, want: time.Microsecond},
146 {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},
147 {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},
148 {t: Int64Type, i: 42, want: int64(42)},
149 {t: Int32Type, i: 42, want: int32(42)},
150 {t: Int16Type, i: 42, want: int16(42)},
151 {t: Int8Type, i: 42, want: int8(42)},
152 {t: StringType, s: "foo", want: "foo"},
153 {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},
154 {t: TimeType, i: 1000, want: time.Unix(0, 1000)},
155 {t: Uint64Type, i: 42, want: uint64(42)},
156 {t: Uint32Type, i: 42, want: uint32(42)},
157 {t: Uint16Type, i: 42, want: uint16(42)},
158 {t: Uint8Type, i: 42, want: uint8(42)},
159 {t: UintptrType, i: 42, want: uintptr(42)},
160 {t: ReflectType, iface: users(2), want: users(2)},
161 {t: NamespaceType, want: map[string]interface{}{}},
162 {t: StringerType, iface: users(2), want: "2 users"},
163 {t: StringerType, iface: &obj{}, want: "obj"},
164 {t: StringerType, iface: (*obj)(nil), want: "nil obj"},
165 {t: SkipType, want: interface{}(nil)},
166 {t: StringerType, iface: (*url.URL)(nil), want: "<nil>"},
167 {t: StringerType, iface: (*users)(nil), want: "<nil>"},
168 {t: ErrorType, iface: (*errObj)(nil), want: "<nil>"},
169 }
170
171 for _, tt := range tests {
172 enc := NewMapObjectEncoder()
173 f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}
174 f.AddTo(enc)
175 assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f)
176
177 delete(enc.Fields, "k")
178 assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.")
179
180 assert.True(t, f.Equals(f), "Field does not equal itself")
181 }
182 }
183
184 func TestInlineMarshaler(t *testing.T) {
185 enc := NewMapObjectEncoder()
186
187 topLevelStr := Field{Key: "k", Type: StringType, String: "s"}
188 topLevelStr.AddTo(enc)
189
190 inlineObj := Field{Key: "ignored", Type: InlineMarshalerType, Interface: users(10)}
191 inlineObj.AddTo(enc)
192
193 nestedObj := Field{Key: "nested", Type: ObjectMarshalerType, Interface: users(11)}
194 nestedObj.AddTo(enc)
195
196 assert.Equal(t, map[string]interface{}{
197 "k": "s",
198 "users": 10,
199 "nested": map[string]interface{}{
200 "users": 11,
201 },
202 }, enc.Fields)
203 }
204
205 func TestEquals(t *testing.T) {
206
207 timeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond)
208 timeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond)
209 timeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano())
210 timeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano())
211 require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is > UnixNano range")
212 require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is < UnixNano range")
213
214 tests := []struct {
215 a, b Field
216 want bool
217 }{
218 {
219 a: zap.Int16("a", 1),
220 b: zap.Int32("a", 1),
221 want: false,
222 },
223 {
224 a: zap.String("k", "a"),
225 b: zap.String("k", "a"),
226 want: true,
227 },
228 {
229 a: zap.String("k", "a"),
230 b: zap.String("k2", "a"),
231 want: false,
232 },
233 {
234 a: zap.String("k", "a"),
235 b: zap.String("k", "b"),
236 want: false,
237 },
238 {
239 a: zap.Time("k", time.Unix(1000, 1000)),
240 b: zap.Time("k", time.Unix(1000, 1000)),
241 want: true,
242 },
243 {
244 a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)),
245 b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))),
246 want: false,
247 },
248 {
249 a: zap.Time("k", timeOutOfRangeLow),
250 b: zap.Time("k", timeOutOfRangeLowNano),
251 want: false,
252 },
253 {
254 a: zap.Time("k", timeOutOfRangeHigh),
255 b: zap.Time("k", timeOutOfRangeHighNano),
256 want: false,
257 },
258 {
259 a: zap.Time("k", time.Unix(1000, 1000)),
260 b: zap.Time("k", time.Unix(1000, 2000)),
261 want: false,
262 },
263 {
264 a: zap.Binary("k", []byte{1, 2}),
265 b: zap.Binary("k", []byte{1, 2}),
266 want: true,
267 },
268 {
269 a: zap.Binary("k", []byte{1, 2}),
270 b: zap.Binary("k", []byte{1, 3}),
271 want: false,
272 },
273 {
274 a: zap.ByteString("k", []byte("abc")),
275 b: zap.ByteString("k", []byte("abc")),
276 want: true,
277 },
278 {
279 a: zap.ByteString("k", []byte("abc")),
280 b: zap.ByteString("k", []byte("abd")),
281 want: false,
282 },
283 {
284 a: zap.Ints("k", []int{1, 2}),
285 b: zap.Ints("k", []int{1, 2}),
286 want: true,
287 },
288 {
289 a: zap.Ints("k", []int{1, 2}),
290 b: zap.Ints("k", []int{1, 3}),
291 want: false,
292 },
293 {
294 a: zap.Object("k", users(10)),
295 b: zap.Object("k", users(10)),
296 want: true,
297 },
298 {
299 a: zap.Object("k", users(10)),
300 b: zap.Object("k", users(20)),
301 want: false,
302 },
303 {
304 a: zap.Any("k", map[string]string{"a": "b"}),
305 b: zap.Any("k", map[string]string{"a": "b"}),
306 want: true,
307 },
308 {
309 a: zap.Any("k", map[string]string{"a": "b"}),
310 b: zap.Any("k", map[string]string{"a": "d"}),
311 want: false,
312 },
313 {
314 a: zap.Dict("k", zap.String("a", "b")),
315 b: zap.Dict("k", zap.String("a", "b")),
316 want: true,
317 },
318 {
319 a: zap.Dict("k", zap.String("a", "b")),
320 b: zap.Dict("k", zap.String("a", "d")),
321 want: false,
322 },
323 }
324
325 for _, tt := range tests {
326 assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b)
327 assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b)
328 }
329 }
330
View as plain text