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  }