github.com/Laisky/zap@v1.27.0/array_test.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package zap 22 23 import ( 24 "errors" 25 "fmt" 26 "testing" 27 "time" 28 29 "github.com/Laisky/zap/zapcore" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func BenchmarkBoolsArrayMarshaler(b *testing.B) { 36 // Keep this benchmark here to capture the overhead of the ArrayMarshaler 37 // wrapper. 38 bs := make([]bool, 50) 39 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{}) 40 b.ResetTimer() 41 for i := 0; i < b.N; i++ { 42 Bools("array", bs).AddTo(enc.Clone()) 43 } 44 } 45 46 func BenchmarkBoolsReflect(b *testing.B) { 47 bs := make([]bool, 50) 48 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{}) 49 b.ResetTimer() 50 for i := 0; i < b.N; i++ { 51 Reflect("array", bs).AddTo(enc.Clone()) 52 } 53 } 54 55 func TestArrayWrappers(t *testing.T) { 56 tests := []struct { 57 desc string 58 field Field 59 expected []interface{} 60 }{ 61 {"empty bools", Bools("", []bool{}), []interface{}{}}, 62 {"empty byte strings", ByteStrings("", [][]byte{}), []interface{}{}}, 63 {"empty complex128s", Complex128s("", []complex128{}), []interface{}{}}, 64 {"empty complex64s", Complex64s("", []complex64{}), []interface{}{}}, 65 {"empty durations", Durations("", []time.Duration{}), []interface{}{}}, 66 {"empty float64s", Float64s("", []float64{}), []interface{}{}}, 67 {"empty float32s", Float32s("", []float32{}), []interface{}{}}, 68 {"empty ints", Ints("", []int{}), []interface{}{}}, 69 {"empty int64s", Int64s("", []int64{}), []interface{}{}}, 70 {"empty int32s", Int32s("", []int32{}), []interface{}{}}, 71 {"empty int16s", Int16s("", []int16{}), []interface{}{}}, 72 {"empty int8s", Int8s("", []int8{}), []interface{}{}}, 73 {"empty strings", Strings("", []string{}), []interface{}{}}, 74 {"empty times", Times("", []time.Time{}), []interface{}{}}, 75 {"empty uints", Uints("", []uint{}), []interface{}{}}, 76 {"empty uint64s", Uint64s("", []uint64{}), []interface{}{}}, 77 {"empty uint32s", Uint32s("", []uint32{}), []interface{}{}}, 78 {"empty uint16s", Uint16s("", []uint16{}), []interface{}{}}, 79 {"empty uint8s", Uint8s("", []uint8{}), []interface{}{}}, 80 {"empty uintptrs", Uintptrs("", []uintptr{}), []interface{}{}}, 81 {"bools", Bools("", []bool{true, false}), []interface{}{true, false}}, 82 {"byte strings", ByteStrings("", [][]byte{{1, 2}, {3, 4}}), []interface{}{"\x01\x02", "\x03\x04"}}, 83 {"complex128s", Complex128s("", []complex128{1 + 2i, 3 + 4i}), []interface{}{1 + 2i, 3 + 4i}}, 84 {"complex64s", Complex64s("", []complex64{1 + 2i, 3 + 4i}), []interface{}{complex64(1 + 2i), complex64(3 + 4i)}}, 85 {"durations", Durations("", []time.Duration{1, 2}), []interface{}{time.Nanosecond, 2 * time.Nanosecond}}, 86 {"float64s", Float64s("", []float64{1.2, 3.4}), []interface{}{1.2, 3.4}}, 87 {"float32s", Float32s("", []float32{1.2, 3.4}), []interface{}{float32(1.2), float32(3.4)}}, 88 {"ints", Ints("", []int{1, 2}), []interface{}{1, 2}}, 89 {"int64s", Int64s("", []int64{1, 2}), []interface{}{int64(1), int64(2)}}, 90 {"int32s", Int32s("", []int32{1, 2}), []interface{}{int32(1), int32(2)}}, 91 {"int16s", Int16s("", []int16{1, 2}), []interface{}{int16(1), int16(2)}}, 92 {"int8s", Int8s("", []int8{1, 2}), []interface{}{int8(1), int8(2)}}, 93 {"strings", Strings("", []string{"foo", "bar"}), []interface{}{"foo", "bar"}}, 94 {"times", Times("", []time.Time{time.Unix(0, 0), time.Unix(0, 0)}), []interface{}{time.Unix(0, 0), time.Unix(0, 0)}}, 95 {"uints", Uints("", []uint{1, 2}), []interface{}{uint(1), uint(2)}}, 96 {"uint64s", Uint64s("", []uint64{1, 2}), []interface{}{uint64(1), uint64(2)}}, 97 {"uint32s", Uint32s("", []uint32{1, 2}), []interface{}{uint32(1), uint32(2)}}, 98 {"uint16s", Uint16s("", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}}, 99 {"uint8s", Uint8s("", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}}, 100 {"uintptrs", Uintptrs("", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}}, 101 } 102 103 for _, tt := range tests { 104 enc := zapcore.NewMapObjectEncoder() 105 tt.field.Key = "k" 106 tt.field.AddTo(enc) 107 assert.Equal(t, tt.expected, enc.Fields["k"], "%s: unexpected map contents.", tt.desc) 108 assert.Equal(t, 1, len(enc.Fields), "%s: found extra keys in map: %v", tt.desc, enc.Fields) 109 } 110 } 111 112 func TestObjectsAndObjectValues(t *testing.T) { 113 t.Parallel() 114 115 tests := []struct { 116 desc string 117 give Field 118 want []any 119 }{ 120 { 121 desc: "Objects/nil slice", 122 give: Objects[*emptyObject]("", nil), 123 want: []any{}, 124 }, 125 { 126 desc: "ObjectValues/nil slice", 127 give: ObjectValues[emptyObject]("", nil), 128 want: []any{}, 129 }, 130 { 131 desc: "ObjectValues/empty slice", 132 give: ObjectValues("", []emptyObject{}), 133 want: []any{}, 134 }, 135 { 136 desc: "ObjectValues/single item", 137 give: ObjectValues("", []emptyObject{ 138 {}, 139 }), 140 want: []any{ 141 map[string]any{}, 142 }, 143 }, 144 { 145 desc: "Objects/multiple different objects", 146 give: Objects("", []*fakeObject{ 147 {value: "foo"}, 148 {value: "bar"}, 149 {value: "baz"}, 150 }), 151 want: []any{ 152 map[string]any{"value": "foo"}, 153 map[string]any{"value": "bar"}, 154 map[string]any{"value": "baz"}, 155 }, 156 }, 157 { 158 desc: "ObjectValues/multiple different objects", 159 give: ObjectValues("", []fakeObject{ 160 {value: "foo"}, 161 {value: "bar"}, 162 {value: "baz"}, 163 }), 164 want: []any{ 165 map[string]any{"value": "foo"}, 166 map[string]any{"value": "bar"}, 167 map[string]any{"value": "baz"}, 168 }, 169 }, 170 } 171 172 for _, tt := range tests { 173 tt := tt 174 t.Run(tt.desc, func(t *testing.T) { 175 t.Parallel() 176 177 tt.give.Key = "k" 178 179 enc := zapcore.NewMapObjectEncoder() 180 tt.give.AddTo(enc) 181 assert.Equal(t, tt.want, enc.Fields["k"]) 182 }) 183 } 184 } 185 186 type emptyObject struct{} 187 188 func (*emptyObject) MarshalLogObject(zapcore.ObjectEncoder) error { 189 return nil 190 } 191 192 type fakeObject struct { 193 value string 194 err error // marshaling error, if any 195 } 196 197 func (o *fakeObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { 198 enc.AddString("value", o.value) 199 return o.err 200 } 201 202 func TestObjectsAndObjectValues_marshalError(t *testing.T) { 203 t.Parallel() 204 205 tests := []struct { 206 desc string 207 give Field 208 want []any 209 wantErr string 210 }{ 211 { 212 desc: "Objects", 213 give: Objects("", []*fakeObject{ 214 {value: "foo"}, 215 {value: "bar", err: errors.New("great sadness")}, 216 {value: "baz"}, // does not get marshaled 217 }), 218 want: []any{ 219 map[string]any{"value": "foo"}, 220 map[string]any{"value": "bar"}, 221 }, 222 wantErr: "great sadness", 223 }, 224 { 225 desc: "ObjectValues", 226 give: ObjectValues("", []fakeObject{ 227 {value: "foo"}, 228 {value: "bar", err: errors.New("stuff failed")}, 229 {value: "baz"}, // does not get marshaled 230 }), 231 want: []any{ 232 map[string]any{"value": "foo"}, 233 map[string]any{"value": "bar"}, 234 }, 235 wantErr: "stuff failed", 236 }, 237 } 238 239 for _, tt := range tests { 240 tt := tt 241 t.Run(tt.desc, func(t *testing.T) { 242 t.Parallel() 243 244 tt.give.Key = "k" 245 246 enc := zapcore.NewMapObjectEncoder() 247 tt.give.AddTo(enc) 248 249 require.Contains(t, enc.Fields, "k") 250 assert.Equal(t, tt.want, enc.Fields["k"]) 251 252 // AddTo puts the error in a "%vError" field based on the name of the 253 // original field. 254 require.Contains(t, enc.Fields, "kError") 255 assert.Equal(t, tt.wantErr, enc.Fields["kError"]) 256 }) 257 } 258 } 259 260 type stringerObject struct { 261 value string 262 } 263 264 func (s stringerObject) String() string { 265 return s.value 266 } 267 268 func TestStringers(t *testing.T) { 269 t.Parallel() 270 271 tests := []struct { 272 desc string 273 give Field 274 want []any 275 }{ 276 { 277 desc: "Stringers", 278 give: Stringers("", []stringerObject{ 279 {value: "foo"}, 280 {value: "bar"}, 281 {value: "baz"}, 282 }), 283 want: []any{ 284 "foo", 285 "bar", 286 "baz", 287 }, 288 }, 289 { 290 desc: "Stringers with []fmt.Stringer", 291 give: Stringers("", []fmt.Stringer{ 292 stringerObject{value: "foo"}, 293 stringerObject{value: "bar"}, 294 stringerObject{value: "baz"}, 295 }), 296 want: []any{ 297 "foo", 298 "bar", 299 "baz", 300 }, 301 }, 302 } 303 304 for _, tt := range tests { 305 tt := tt 306 t.Run(tt.desc, func(t *testing.T) { 307 t.Parallel() 308 309 tt.give.Key = "k" 310 311 enc := zapcore.NewMapObjectEncoder() 312 tt.give.AddTo(enc) 313 assert.Equal(t, tt.want, enc.Fields["k"]) 314 }) 315 } 316 }