github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/topic/topicreadercommon/message_content_pool_test.go (about) 1 package topicreadercommon 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "testing" 8 9 "github.com/stretchr/testify/require" 10 "go.uber.org/mock/gomock" 11 12 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 13 ) 14 15 func BenchmarkConsumeContent(b *testing.B) { 16 b.ReportAllocs() 17 content := []byte("asd") 18 reader := bytes.NewReader(content) 19 msg := PublicMessage{data: newOneTimeReader(reader)} 20 for i := 0; i < b.N; i++ { 21 reader.Reset(content) 22 msg.dataConsumed = false 23 msg.data = newOneTimeReader(reader) 24 err := msg.UnmarshalTo(emptyConsumer{}) 25 if err != nil { 26 b.Fatal() 27 } 28 } 29 } 30 31 func TestCallbackOnReaderContent(t *testing.T) { 32 expectedData := []byte(`asdf`) 33 newReader := func() io.Reader { 34 return bytes.NewReader(expectedData) 35 } 36 37 t.Run("ZeroEstimatedSize", func(t *testing.T) { 38 mc := gomock.NewController(t) 39 p := NewMockPool(mc) 40 p.EXPECT().Get().Return(nil) 41 p.EXPECT().Put(gomock.Any()).Do(func(val any) { 42 buf := val.(*bytes.Buffer) 43 require.NotNil(t, buf) 44 require.Equal(t, 0, buf.Len()) 45 require.Equal(t, minInitializeBufferSize, buf.Cap()) 46 }) 47 48 called := false 49 err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error { 50 called = true 51 require.Equal(t, expectedData, data) 52 53 return nil 54 })) 55 require.NoError(t, err) 56 require.True(t, called) 57 }) 58 t.Run("MiddleEstimatedSize", func(t *testing.T) { 59 estimatedSize := minInitializeBufferSize + 10 60 61 mc := gomock.NewController(t) 62 p := NewMockPool(mc) 63 p.EXPECT().Get().Return(nil).Times(2) 64 65 var targetCapacity int 66 p.EXPECT().Put(gomock.Any()).DoAndReturn(func(val any) { 67 buf := val.(*bytes.Buffer) 68 targetCapacity = buf.Cap() 69 }) 70 71 p.EXPECT().Put(gomock.Any()).DoAndReturn(func(val any) { 72 buf := val.(*bytes.Buffer) 73 require.NotNil(t, buf) 74 require.Equal(t, 0, buf.Len()) 75 76 // check about target capacity same as without read - that mean no reallocations while read 77 require.Equal(t, targetCapacity, buf.Cap()) 78 }) 79 80 // first call with empty reader - for check internal capacity without reallocation 81 _ = callbackOnReaderContent(p, ErrReader(io.EOF), estimatedSize, testFuncConsumer(func(data []byte) error { 82 require.Empty(t, data) 83 84 return nil 85 })) 86 87 // real call 88 called := false 89 err := callbackOnReaderContent(p, newReader(), estimatedSize, testFuncConsumer(func(data []byte) error { 90 require.Equal(t, expectedData, data) 91 called = true 92 93 return nil 94 })) 95 require.NoError(t, err) 96 require.True(t, called) 97 }) 98 t.Run("LargeEstimatedSize", func(t *testing.T) { 99 mc := gomock.NewController(t) 100 p := NewMockPool(mc) 101 p.EXPECT().Get().Return(nil) 102 p.EXPECT().Put(gomock.Any()).DoAndReturn(func(val any) { 103 buf := val.(*bytes.Buffer) 104 require.NotNil(t, buf) 105 require.Equal(t, 0, buf.Len()) 106 require.Equal(t, maxInitialBufferSize, buf.Cap()) 107 }) 108 109 called := false 110 err := callbackOnReaderContent(p, newReader(), maxInitialBufferSize+10, testFuncConsumer(func(data []byte) error { 111 require.Equal(t, expectedData, data) 112 called = true 113 114 return nil 115 })) 116 require.NoError(t, err) 117 require.True(t, called) 118 }) 119 t.Run("UseBufferFromPool", func(t *testing.T) { 120 poolBuf := &bytes.Buffer{} 121 mc := gomock.NewController(t) 122 p := NewMockPool(mc) 123 p.EXPECT().Get().Return(poolBuf) 124 p.EXPECT().Put(gomock.Any()).DoAndReturn(func(buf any) { 125 require.Same(t, poolBuf, buf) 126 }) 127 128 called := false 129 err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error { 130 require.Equal(t, expectedData, data) 131 called = true 132 133 return nil 134 })) 135 require.NoError(t, err) 136 require.True(t, called) 137 }) 138 t.Run("ReturnErrorFromReader", func(t *testing.T) { 139 testErr := errors.New("test") 140 err := callbackOnReaderContent(globalReadMessagePool, ErrReader(testErr), 0, nil) 141 require.ErrorIs(t, err, testErr) 142 require.False(t, xerrors.IsYdb(err)) 143 }) 144 t.Run("ReturnErrorFromUnmarshal", func(t *testing.T) { 145 testErr := errors.New("test") 146 err := callbackOnReaderContent( 147 globalReadMessagePool, 148 ErrReader(io.EOF), 149 0, 150 testFuncConsumer(func(data []byte) error { 151 return testErr 152 }), 153 ) 154 require.ErrorIs(t, err, testErr) 155 require.False(t, xerrors.IsYdb(err)) 156 }) 157 } 158 159 type emptyConsumer struct{} 160 161 func (emptyConsumer) UnmarshalYDBTopicMessage([]byte) error { 162 return nil 163 } 164 165 type testFuncConsumer func([]byte) error 166 167 func (c testFuncConsumer) UnmarshalYDBTopicMessage(data []byte) error { 168 return c(data) 169 } 170 171 // ErrReader returns an io.Reader that returns 0, err from all Read calls. 172 // copy for use with go pre 1.16 173 func ErrReader(err error) io.Reader { 174 return &errReader{err: err} 175 } 176 177 type errReader struct { 178 err error 179 } 180 181 func (r *errReader) Read(p []byte) (int, error) { 182 return 0, r.err 183 }