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  }