github.com/vc42/parquet-go@v0.0.0-20240320194221-1a9adb5f23f5/encoding/fuzz/fuzz.go (about) 1 //go:build go1.18 2 // +build go1.18 3 4 // Package fuzz contains functions to help fuzz test parquet encodings. 5 package fuzz 6 7 import ( 8 "bytes" 9 "math/rand" 10 "testing" 11 12 "github.com/vc42/parquet-go/encoding" 13 "github.com/vc42/parquet-go/encoding/plain" 14 ) 15 16 func EncodeBoolean(f *testing.F, e encoding.Encoding) { 17 encode(f, e, 18 encoding.Encoding.EncodeBoolean, 19 encoding.Encoding.DecodeBoolean, 20 generatePlainBooleanList, 21 ) 22 } 23 24 func EncodeLevels(f *testing.F, e encoding.Encoding) { 25 encode(f, e, 26 encoding.Encoding.EncodeLevels, 27 encoding.Encoding.DecodeLevels, 28 generatePlainValueList(1), 29 ) 30 } 31 32 func EncodeInt32(f *testing.F, e encoding.Encoding) { 33 encode(f, e, 34 encoding.Encoding.EncodeInt32, 35 encoding.Encoding.DecodeInt32, 36 generatePlainValueList(4), 37 ) 38 } 39 40 func EncodeInt64(f *testing.F, e encoding.Encoding) { 41 encode(f, e, 42 encoding.Encoding.EncodeInt64, 43 encoding.Encoding.DecodeInt64, 44 generatePlainValueList(8), 45 ) 46 } 47 48 func EncodeFloat(f *testing.F, e encoding.Encoding) { 49 encode(f, e, 50 encoding.Encoding.EncodeFloat, 51 encoding.Encoding.DecodeFloat, 52 generatePlainValueList(4), 53 ) 54 } 55 56 func EncodeDouble(f *testing.F, e encoding.Encoding) { 57 encode(f, e, 58 encoding.Encoding.EncodeDouble, 59 encoding.Encoding.DecodeDouble, 60 generatePlainValueList(8), 61 ) 62 } 63 64 func EncodeByteArray(f *testing.F, e encoding.Encoding) { 65 encode(f, e, 66 encoding.Encoding.EncodeByteArray, 67 encoding.Encoding.DecodeByteArray, 68 generatePlainByteArrayList, 69 ) 70 } 71 72 type encodingFunc func(enc encoding.Encoding, dst, src []byte) ([]byte, error) 73 74 type generateFunc func(dst, src []byte, prng *rand.Rand) []byte 75 76 func encode(f *testing.F, e encoding.Encoding, encode encodingFunc, decode encodingFunc, generate generateFunc) { 77 const bufferSize = 64 * 1024 78 var err error 79 var buf = make([]byte, bufferSize) 80 var src = make([]byte, bufferSize) 81 var dst = make([]byte, bufferSize) 82 var prng = rand.New(rand.NewSource(0)) 83 84 f.Fuzz(func(t *testing.T, input []byte, seed int64) { 85 prng.Seed(seed) 86 src = generate(src[:0], input, prng) 87 88 dst, err = encode(e, dst, src) 89 if err != nil { 90 t.Error(err) 91 return 92 } 93 94 buf, err = decode(e, buf, dst) 95 if err != nil { 96 t.Error(err) 97 return 98 } 99 100 if !bytes.Equal(buf, src) { 101 t.Error("decoded output does not match the original input") 102 return 103 } 104 105 // Likely invalid inputs, look for panics. 106 buf, _ = decode(e, buf, input) 107 }) 108 } 109 110 func generatePlainBooleanList(dst, src []byte, _ *rand.Rand) []byte { 111 return append(dst, src...) 112 } 113 114 func generatePlainByteArrayList(dst, src []byte, prng *rand.Rand) []byte { 115 limit := len(src)/10 + 1 116 117 for i := 0; i < len(src); { 118 n := prng.Intn(limit) + 1 119 r := len(src) - i 120 if n > r { 121 n = r 122 } 123 dst = plain.AppendByteArray(dst, src[i:i+n]) 124 i += n 125 } 126 127 return dst 128 } 129 130 func generatePlainValueList(size int) func(dst, src []byte, _ *rand.Rand) []byte { 131 return func(dst, src []byte, _ *rand.Rand) []byte { 132 n := (len(src) / size) * size 133 return append(dst, src[:n]...) 134 } 135 }