...

Source file src/go.uber.org/zap/array_test.go

Documentation: go.uber.org/zap

     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  	"go.uber.org/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  }
   317  

View as plain text