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 }