...

Source file src/go.uber.org/zap/zapcore/field_test.go

Documentation: go.uber.org/zap/zapcore

     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 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  	//revive:disable:dot-imports
    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  		// panic with an arbitrary object that causes a panic itself
    78  		// when being converted to a string
    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  	// Values outside the UnixNano range were encoded incorrectly (#737, #803).
   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