storj.io/uplink@v1.13.0/private/storage/streams/buffer/backend_test.go (about)

     1  /*
     2   *   Copyright (c) 2024 Storj Labs, Inc.
     3   *   All rights reserved.
     4   *   See LICENSE for copying information.
     5   */
     6  
     7  package buffer
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/rand"
    12  	"io"
    13  	"testing"
    14  	"testing/iotest"
    15  
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"storj.io/common/testrand"
    19  )
    20  
    21  func FuzzChunkBackend(f *testing.F) {
    22  	f.Add(0)
    23  	f.Add(1)
    24  	f.Add(chunkSize - 1)
    25  	f.Add(chunkSize)
    26  	f.Add(chunkSize + 1)
    27  	f.Add(chunkSize*2 - 1)
    28  	f.Add(chunkSize * 2)
    29  	f.Add(chunkSize*2 + 1)
    30  
    31  	const (
    32  		fuzzCap = chunkSize*2 + 1
    33  	)
    34  
    35  	data := testrand.BytesInt(fuzzCap)
    36  	f.Fuzz(func(t *testing.T, amount int) {
    37  		if amount < 0 || amount > len(data) {
    38  			return
    39  		}
    40  
    41  		amount64 := int64(amount)
    42  
    43  		// cap the data to the fuzz amount
    44  		data := data[:amount]
    45  		backend := NewChunkBackend(amount64)
    46  
    47  		n, err := io.Copy(backend, bytes.NewReader(data))
    48  		require.NoError(t, err)
    49  		require.Equal(t, amount64, n)
    50  		require.NoError(t, iotest.TestReader(io.NewSectionReader(backend, 0, amount64), data))
    51  	})
    52  }
    53  
    54  func TestChunkBackend(t *testing.T) {
    55  	requireWrite := func(tb testing.TB, w io.Writer, data []byte) {
    56  		n, err := w.Write(data)
    57  		require.NoError(t, err)
    58  		require.Equal(t, n, len(data))
    59  	}
    60  
    61  	requireReaderAtHas := func(tb testing.TB, ra io.ReaderAt, pieces ...[]byte) {
    62  		var data []byte
    63  		for _, piece := range pieces {
    64  			data = append(data, piece...)
    65  		}
    66  		require.NoError(t, iotest.TestReader(io.NewSectionReader(ra, 0, int64(len(data))), data))
    67  	}
    68  
    69  	double := testrand.BytesInt(chunkSize * 2)
    70  
    71  	t.Run("writes spanning chunk boundaries", func(t *testing.T) {
    72  		backend := NewChunkBackend(standardMaxEncryptedSegmentSize)
    73  		half := double[:len(double)/4]
    74  		whole := double[:len(double)/2]
    75  		requireWrite(t, backend, half)
    76  		requireWrite(t, backend, whole)
    77  		requireWrite(t, backend, double)
    78  		requireWrite(t, backend, half)
    79  		requireWrite(t, backend, whole)
    80  		requireWrite(t, backend, double)
    81  		requireReaderAtHas(t, backend, half, whole, double, half, whole, double)
    82  	})
    83  
    84  	t.Run("read spanning chunk boundaries", func(t *testing.T) {
    85  		backend := NewChunkBackend(standardMaxEncryptedSegmentSize)
    86  		requireWrite(t, backend, double)
    87  		buf := make([]byte, len(double))
    88  		n, err := backend.ReadAt(buf, 0)
    89  		require.NoError(t, err)
    90  		require.Equal(t, n, len(buf))
    91  		require.Equal(t, buf, double)
    92  	})
    93  
    94  	t.Run("exceed cap", func(t *testing.T) {
    95  		backend := NewChunkBackend(1)
    96  		_, err := backend.Write(double[:2])
    97  		require.ErrorIs(t, err, io.ErrShortWrite)
    98  	})
    99  }
   100  
   101  func TestChunksNeeded(t *testing.T) {
   102  	require.Equal(t, int64(0), chunksNeeded(0))
   103  	require.Equal(t, int64(1), chunksNeeded(1))
   104  	require.Equal(t, int64(1), chunksNeeded(chunkSize-1))
   105  	require.Equal(t, int64(1), chunksNeeded(chunkSize))
   106  	require.Equal(t, int64(2), chunksNeeded(chunkSize+1))
   107  	require.Equal(t, int64(2), chunksNeeded(chunkSize*2-1))
   108  	require.Equal(t, int64(2), chunksNeeded(chunkSize*2))
   109  	require.Equal(t, int64(3), chunksNeeded(chunkSize*2+1))
   110  }
   111  
   112  func BenchmarkBackends(b *testing.B) {
   113  	buf := make([]byte, standardMaxEncryptedSegmentSize)
   114  	_, err := rand.Read(buf)
   115  	require.NoError(b, err)
   116  
   117  	b.ReportAllocs()
   118  	b.ResetTimer()
   119  
   120  	b.Run("memory", func(b *testing.B) {
   121  		for i := 0; i < b.N; i++ {
   122  			backend := NewMemoryBackend(standardMaxEncryptedSegmentSize)
   123  			_, err := io.Copy(backend, bytes.NewReader(buf))
   124  			require.NoError(b, err)
   125  			require.NoError(b, backend.Close())
   126  		}
   127  	})
   128  
   129  	b.Run("chunks", func(b *testing.B) {
   130  		for i := 0; i < b.N; i++ {
   131  			backend := NewChunkBackend(standardMaxEncryptedSegmentSize)
   132  			_, err := io.Copy(backend, bytes.NewReader(buf))
   133  			require.NoError(b, err)
   134  			require.NoError(b, backend.Close())
   135  		}
   136  	})
   137  }