github.com/m3db/m3@v1.5.0/src/x/serialize/encoder_test.go (about) 1 // Copyright (c) 2018 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 serialize 22 23 import ( 24 "strings" 25 "testing" 26 27 "github.com/m3db/m3/src/x/checked" 28 "github.com/m3db/m3/src/x/ident" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 ) 33 34 func newTestEncoderOpts() TagEncoderOptions { 35 return NewTagEncoderOptions() 36 } 37 38 func newTestTagEncoder() TagEncoder { 39 return newTagEncoder(defaultNewCheckedBytesFn, newTestEncoderOpts(), nil) 40 } 41 42 func TestEmptyEncode(t *testing.T) { 43 e := newTestTagEncoder() 44 require.NoError(t, e.Encode(ident.EmptyTagIterator)) 45 46 bc, ok := e.Data() 47 require.True(t, ok) 48 require.NotNil(t, bc) 49 b := bc.Bytes() 50 require.Len(t, b, 4) 51 require.Equal(t, headerMagicBytes, b[:2]) 52 require.Equal(t, []byte{0x0, 0x0}, b[2:4]) 53 } 54 55 func TestEncodeResetTwice(t *testing.T) { 56 e := newTestTagEncoder() 57 require.NoError(t, e.Encode(ident.EmptyTagIterator)) 58 e.Reset() 59 e.Reset() 60 require.NoError(t, e.Encode(ident.EmptyTagIterator)) 61 } 62 63 func TestInuseEncode(t *testing.T) { 64 e := newTestTagEncoder() 65 require.NoError(t, e.Encode(ident.EmptyTagIterator)) 66 require.Error(t, e.Encode(ident.EmptyTagIterator)) 67 } 68 69 func TestTagEncoderLeavesOriginalIterator(t *testing.T) { 70 e := newTestTagEncoder() 71 tags := ident.NewTagsIterator(ident.NewTags( 72 ident.StringTag("abc", "defg"), 73 ident.StringTag("x", "bar"), 74 )) 75 76 require.Equal(t, 2, tags.Remaining()) 77 require.NoError(t, e.Encode(tags)) 78 require.Equal(t, 2, tags.Remaining()) 79 } 80 81 func TestTagEncoderEmpty(t *testing.T) { 82 e := newTestTagEncoder() 83 tags := ident.MustNewTagStringsIterator("abc", "") 84 require.NoError(t, e.Encode(tags)) 85 86 e = newTestTagEncoder() 87 tags = ident.MustNewTagStringsIterator("", "abc") 88 require.Error(t, e.Encode(tags)) 89 } 90 91 func TestSimpleEncode(t *testing.T) { 92 e := newTestTagEncoder() 93 94 tags := ident.NewTagsIterator(ident.NewTags( 95 ident.StringTag("abc", "defg"), 96 ident.StringTag("x", "bar"), 97 )) 98 require.NoError(t, e.Encode(tags)) 99 100 bc, ok := e.Data() 101 require.True(t, ok) 102 require.NotNil(t, bc) 103 b := bc.Bytes() 104 numExpectedBytes := 2 /* header */ + 2 /* num tags */ + 105 2 /* abc length */ + len("abc") + 106 2 /* defg length */ + len("defg") + 107 2 /* x length */ + len("x") + 108 2 /* bar length */ + len("bar") 109 require.Len(t, b, numExpectedBytes) 110 require.Equal(t, headerMagicBytes, b[:2]) 111 require.Equal(t, encodeUInt16(2, make([]byte, 2)), b[2:4]) 112 require.Equal(t, uint16(3), decodeUInt16(b[4:6])) /* len abc */ 113 require.Equal(t, "abc", string(b[6:9])) 114 require.Equal(t, uint16(4), decodeUInt16(b[9:11])) /* len defg */ 115 require.Equal(t, "defg", string(b[11:15])) 116 require.Equal(t, uint16(1), decodeUInt16(b[15:17])) /* len x */ 117 require.Equal(t, "x", string(b[17:18])) 118 require.Equal(t, uint16(3), decodeUInt16(b[18:20])) /* len bar */ 119 require.Equal(t, "bar", string(b[20:23])) 120 } 121 122 func TestTagEncoderErrorEncoding(t *testing.T) { 123 opts := NewTagEncoderOptions() 124 e := newTagEncoder(defaultNewCheckedBytesFn, opts, nil) 125 maxLiteralLen := int(opts.TagSerializationLimits().MaxTagLiteralLength()) 126 tags := ident.NewTagsIterator(ident.NewTags( 127 ident.StringTag("abc", "defg"), 128 ident.StringTag("x", strings.Repeat("x", 1+maxLiteralLen)), 129 )) 130 131 require.Error(t, e.Encode(tags)) 132 d, ok := e.Data() 133 require.Nil(t, d) 134 require.False(t, ok) 135 136 e.Reset() 137 tags = ident.NewTagsIterator(ident.NewTags( 138 ident.StringTag("abc", "defg"), 139 ident.StringTag("x", strings.Repeat("x", maxLiteralLen)), 140 )) 141 require.NoError(t, e.Encode(tags)) 142 } 143 144 func TestEmptyTagIterEncode(t *testing.T) { 145 ctrl := gomock.NewController(t) 146 defer ctrl.Finish() 147 148 mockBytes := checked.NewMockBytes(ctrl) 149 newBytesFn := func([]byte, checked.BytesOptions) checked.Bytes { 150 return mockBytes 151 } 152 153 iter := ident.NewMockTagIterator(ctrl) 154 155 mockBytes.EXPECT().IncRef() 156 mockBytes.EXPECT().Reset(gomock.Any()) 157 gomock.InOrder( 158 mockBytes.EXPECT().NumRef().Return(0), 159 iter.EXPECT().Rewind(), 160 iter.EXPECT().Remaining().Return(0), 161 iter.EXPECT().Next().Return(false), 162 iter.EXPECT().Err().Return(nil), 163 iter.EXPECT().Rewind(), 164 ) 165 166 enc := newTagEncoder(newBytesFn, newTestEncoderOpts(), nil) 167 require.NoError(t, enc.Encode(iter)) 168 } 169 170 func TestTooManyTags(t *testing.T) { 171 ctrl := gomock.NewController(t) 172 defer ctrl.Finish() 173 174 iter := ident.NewMockTagIterator(ctrl) 175 testOpts := newTestEncoderOpts() 176 177 maxNumTags := testOpts.TagSerializationLimits().MaxNumberTags() 178 gomock.InOrder( 179 iter.EXPECT().Rewind(), 180 iter.EXPECT().Remaining().Return(1+int(maxNumTags)), 181 iter.EXPECT().Rewind(), 182 ) 183 184 enc := newTagEncoder(defaultNewCheckedBytesFn, testOpts, nil) 185 require.Error(t, enc.Encode(iter)) 186 } 187 188 func TestSingleValueTagIterEncode(t *testing.T) { 189 ctrl := gomock.NewController(t) 190 defer ctrl.Finish() 191 192 mockBytes := checked.NewMockBytes(ctrl) 193 newBytesFn := func([]byte, checked.BytesOptions) checked.Bytes { 194 return mockBytes 195 } 196 197 iter := ident.NewMockTagIterator(ctrl) 198 199 mockBytes.EXPECT().IncRef() 200 mockBytes.EXPECT().Reset(gomock.Any()) 201 gomock.InOrder( 202 mockBytes.EXPECT().NumRef().Return(0), 203 iter.EXPECT().Rewind(), 204 iter.EXPECT().Remaining().Return(1), 205 iter.EXPECT().Next().Return(true), 206 iter.EXPECT().Current().Return( 207 ident.StringTag("some", "tag"), 208 ), 209 iter.EXPECT().Next().Return(false), 210 iter.EXPECT().Err().Return(nil), 211 iter.EXPECT().Rewind(), 212 ) 213 214 enc := newTagEncoder(newBytesFn, newTestEncoderOpts(), nil) 215 require.NoError(t, enc.Encode(iter)) 216 217 mockBytes.EXPECT().NumRef().Return(1) 218 require.Error(t, enc.Encode(iter)) 219 220 mockBytes.EXPECT().NumRef().Return(1) 221 mockBytes.EXPECT().Reset(nil) 222 mockBytes.EXPECT().DecRef() 223 enc.Reset() 224 }