github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/topic/topicreaderinternal/message_content_pool_test.go (about)

     1  package topicreaderinternal
     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(buf *bytes.Buffer) {
    42  			require.NotNil(t, buf)
    43  			require.Equal(t, 0, buf.Len())
    44  			require.Equal(t, minInitializeBufferSize, buf.Cap())
    45  		})
    46  
    47  		called := false
    48  		err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error {
    49  			called = true
    50  			require.Equal(t, expectedData, data)
    51  
    52  			return nil
    53  		}))
    54  		require.NoError(t, err)
    55  		require.True(t, called)
    56  	})
    57  	t.Run("MiddleEstimatedSize", func(t *testing.T) {
    58  		estimatedSize := minInitializeBufferSize + 10
    59  
    60  		mc := gomock.NewController(t)
    61  		p := NewMockPool(mc)
    62  		p.EXPECT().Get().Return(nil).Times(2)
    63  
    64  		var targetCapacity int
    65  		p.EXPECT().Put(gomock.Any()).Do(func(buf *bytes.Buffer) {
    66  			targetCapacity = buf.Cap()
    67  		})
    68  
    69  		p.EXPECT().Put(gomock.Any()).Do(func(buf *bytes.Buffer) {
    70  			require.NotNil(t, buf)
    71  			require.Equal(t, 0, buf.Len())
    72  
    73  			// check about target capacity same as without read - that mean no reallocations while read
    74  			require.Equal(t, targetCapacity, buf.Cap())
    75  		})
    76  
    77  		// first call with empty reader - for check internal capacity without reallocation
    78  		_ = callbackOnReaderContent(p, ErrReader(io.EOF), estimatedSize, testFuncConsumer(func(data []byte) error {
    79  			require.Empty(t, data)
    80  
    81  			return nil
    82  		}))
    83  
    84  		// real call
    85  		called := false
    86  		err := callbackOnReaderContent(p, newReader(), estimatedSize, testFuncConsumer(func(data []byte) error {
    87  			require.Equal(t, expectedData, data)
    88  			called = true
    89  
    90  			return nil
    91  		}))
    92  		require.NoError(t, err)
    93  		require.True(t, called)
    94  	})
    95  	t.Run("LargeEstimatedSize", func(t *testing.T) {
    96  		mc := gomock.NewController(t)
    97  		p := NewMockPool(mc)
    98  		p.EXPECT().Get().Return(nil)
    99  		p.EXPECT().Put(gomock.Any()).Do(func(buf *bytes.Buffer) {
   100  			require.NotNil(t, buf)
   101  			require.Equal(t, 0, buf.Len())
   102  			require.Equal(t, maxInitialBufferSize, buf.Cap())
   103  		})
   104  
   105  		called := false
   106  		err := callbackOnReaderContent(p, newReader(), maxInitialBufferSize+10, testFuncConsumer(func(data []byte) error {
   107  			require.Equal(t, expectedData, data)
   108  			called = true
   109  
   110  			return nil
   111  		}))
   112  		require.NoError(t, err)
   113  		require.True(t, called)
   114  	})
   115  	t.Run("UseBufferFromPool", func(t *testing.T) {
   116  		poolBuf := &bytes.Buffer{}
   117  		mc := gomock.NewController(t)
   118  		p := NewMockPool(mc)
   119  		p.EXPECT().Get().Return(poolBuf)
   120  		p.EXPECT().Put(gomock.Any()).Do(func(buf *bytes.Buffer) {
   121  			require.Same(t, poolBuf, buf)
   122  		})
   123  
   124  		called := false
   125  		err := callbackOnReaderContent(p, newReader(), 0, testFuncConsumer(func(data []byte) error {
   126  			require.Equal(t, expectedData, data)
   127  			called = true
   128  
   129  			return nil
   130  		}))
   131  		require.NoError(t, err)
   132  		require.True(t, called)
   133  	})
   134  	t.Run("ReturnErrorFromReader", func(t *testing.T) {
   135  		testErr := errors.New("test")
   136  		err := callbackOnReaderContent(globalReadMessagePool, ErrReader(testErr), 0, nil)
   137  		require.ErrorIs(t, err, testErr)
   138  		require.False(t, xerrors.IsYdb(err))
   139  	})
   140  	t.Run("ReturnErrorFromUnmarshal", func(t *testing.T) {
   141  		testErr := errors.New("test")
   142  		err := callbackOnReaderContent(
   143  			globalReadMessagePool,
   144  			ErrReader(io.EOF),
   145  			0,
   146  			testFuncConsumer(func(data []byte) error {
   147  				return testErr
   148  			}),
   149  		)
   150  		require.ErrorIs(t, err, testErr)
   151  		require.False(t, xerrors.IsYdb(err))
   152  	})
   153  }
   154  
   155  type emptyConsumer struct{}
   156  
   157  func (emptyConsumer) UnmarshalYDBTopicMessage([]byte) error {
   158  	return nil
   159  }
   160  
   161  type testFuncConsumer func([]byte) error
   162  
   163  func (c testFuncConsumer) UnmarshalYDBTopicMessage(data []byte) error {
   164  	return c(data)
   165  }
   166  
   167  // ErrReader returns an io.Reader that returns 0, err from all Read calls.
   168  // copy for use with go pre 1.16
   169  func ErrReader(err error) io.Reader {
   170  	return &errReader{err: err}
   171  }
   172  
   173  type errReader struct {
   174  	err error
   175  }
   176  
   177  func (r *errReader) Read(p []byte) (int, error) {
   178  	return 0, r.err
   179  }