go.uber.org/yarpc@v1.72.1/internal/bufferpool/bufferpool_test.go (about)

     1  // Copyright (c) 2022 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 bufferpool
    22  
    23  import (
    24  	"bytes"
    25  	"io"
    26  	"io/ioutil"
    27  	"math/rand"
    28  	"strings"
    29  	"sync"
    30  	"testing"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func TestBufferWrite(t *testing.T) {
    37  	runTestWithBuffer(t, func(t *testing.T, buf *Buffer) {
    38  		buf.Write([]byte("hello world"))
    39  		assert.Equal(t, "hello world", string(buf.Bytes()), "Unexpected written bytes")
    40  	})
    41  }
    42  
    43  func TestBufferWriteTo(t *testing.T) {
    44  	runTestWithBuffer(t, func(t *testing.T, buf *Buffer) {
    45  		buf.Write([]byte("hello world"))
    46  
    47  		sink := &bytes.Buffer{}
    48  		buf.WriteTo(sink)
    49  		assert.Equal(t, "hello world", sink.String(), "Unexpected written bytes")
    50  	})
    51  }
    52  
    53  func TestBufferRead(t *testing.T) {
    54  	runTestWithBuffer(t, func(t *testing.T, buf *Buffer) {
    55  		io.WriteString(buf, "hello world")
    56  
    57  		got, err := ioutil.ReadAll(buf)
    58  		require.NoError(t, err, "Read failed")
    59  		assert.Equal(t, "hello world", string(got), "Unexpected read bytes")
    60  	})
    61  }
    62  
    63  func TestBufferReadFrom(t *testing.T) {
    64  	runTestWithBuffer(t, func(t *testing.T, buf *Buffer) {
    65  		_, err := buf.ReadFrom(strings.NewReader("hello world"))
    66  		require.NoError(t, err, "ReadFrom failed")
    67  
    68  		assert.Equal(t, "hello world", string(buf.Bytes()), "Unexpected read bytes")
    69  	})
    70  }
    71  
    72  func TestBufferPrePostOp(t *testing.T) {
    73  	runTest(t, func(t *testing.T, pool *Pool) {
    74  		buf := pool.Get()
    75  		defer buf.Release()
    76  
    77  		v := buf.preOp()
    78  		assert.NotPanics(t, func() {
    79  			buf.postOp(v)
    80  		})
    81  
    82  		// Doing the postOp twice will panic
    83  		assert.Panics(t, func() {
    84  			buf.postOp(v)
    85  		})
    86  	})
    87  }
    88  
    89  func TestBufferReuse(t *testing.T) {
    90  	runTest(t, func(t *testing.T, pool *Pool) {
    91  		runConcurrently(t, func() {
    92  			buf := pool.Get()
    93  			assert.Equal(t, 0, len(buf.Bytes()), "Expected zero buffer size")
    94  
    95  			io.WriteString(buf, "test")
    96  			buf.Release()
    97  		})
    98  	})
    99  }
   100  
   101  func TestBufferUseAfterRelease(t *testing.T) {
   102  	runTest(t, func(t *testing.T, pool *Pool) {
   103  		buf := pool.Get()
   104  		buf.Release()
   105  
   106  		assert.Panics(t, func() {
   107  			io.WriteString(buf, "test")
   108  		})
   109  	})
   110  }
   111  
   112  func TestBufferReleaseTwice(t *testing.T) {
   113  	runTest(t, func(t *testing.T, pool *Pool) {
   114  		buf := pool.Get()
   115  
   116  		buf.Release()
   117  		assert.Panics(t, func() {
   118  			buf.Release()
   119  		})
   120  	})
   121  }
   122  
   123  func TestBuffers(t *testing.T) {
   124  	runConcurrently(t, func() {
   125  		buf := Get()
   126  		assert.Zero(t, buf.Len(), "Expected truncated buffer")
   127  
   128  		bs := randBytes(rand.Intn(5000))
   129  		_, err := rand.Read(bs)
   130  		assert.NoError(t, err, "Unexpected error from rand.Read")
   131  		_, err = buf.Write(bs)
   132  		assert.NoError(t, err, "Unexpected error from buffer.Write")
   133  
   134  		assert.Equal(t, buf.Len(), len(bs), "Expected same buffer size")
   135  
   136  		Put(buf)
   137  	})
   138  }
   139  
   140  func runTestWithBuffer(t *testing.T, f func(t *testing.T, buf *Buffer)) {
   141  	runTest(t, func(t *testing.T, pool *Pool) {
   142  		buf := pool.Get()
   143  		defer buf.Release()
   144  
   145  		f(t, buf)
   146  	})
   147  }
   148  
   149  // runTest runs a given test function with pools created using
   150  // different options.
   151  // It also runs the test multiple times to trigger buffer reuse.
   152  func runTest(t *testing.T, f func(t *testing.T, pool *Pool)) {
   153  	const numIterations = 10
   154  
   155  	t.Run("no use-after-free detection", func(t *testing.T) {
   156  		for i := 0; i < numIterations; i++ {
   157  			f(t, NewPool())
   158  		}
   159  	})
   160  
   161  	t.Run("with use-after-free detection", func(t *testing.T) {
   162  		for i := 0; i < numIterations; i++ {
   163  			f(t, NewPool(DetectUseAfterFreeForTests()))
   164  		}
   165  	})
   166  }
   167  
   168  func runConcurrently(t *testing.T, f func()) {
   169  	const numGoroutines = 5
   170  
   171  	var wg sync.WaitGroup
   172  	for i := 0; i < numGoroutines; i++ {
   173  		wg.Add(1)
   174  		go func() {
   175  			defer wg.Done()
   176  
   177  			for j := 0; j < 10; j++ {
   178  				f()
   179  			}
   180  		}()
   181  	}
   182  
   183  	wg.Wait()
   184  }
   185  
   186  func randBytes(n int) []byte {
   187  	buf := make([]byte, n)
   188  	rand.Read(buf)
   189  	return buf
   190  }