github.com/segmentio/parquet-go@v0.0.0-20230712180008-5d42db8f0d47/internal/quick/quick.go (about)

     1  package quick
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/rand"
     7  	"reflect"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  var DefaultConfig = Config{
    13  	Sizes: []int{
    14  		0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    15  		10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    16  		20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
    17  		30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    18  		99, 100, 101,
    19  		127, 128, 129,
    20  		255, 256, 257,
    21  		1000, 1023, 1024, 1025,
    22  		2000, 2095, 2048, 2049,
    23  		4000, 4095, 4096, 4097,
    24  	},
    25  	Seed: 0,
    26  }
    27  
    28  // Check is inspired by the standard quick.Check package, but enhances the
    29  // API and tests arrays of larger sizes than the maximum of 50 hardcoded in
    30  // testing/quick.
    31  func Check(f interface{}) error {
    32  	return DefaultConfig.Check(f)
    33  }
    34  
    35  type Config struct {
    36  	Sizes []int
    37  	Seed  int64
    38  }
    39  
    40  func (c *Config) Check(f interface{}) error {
    41  	v := reflect.ValueOf(f)
    42  	r := rand.New(rand.NewSource(c.Seed))
    43  	t := v.Type().In(0)
    44  
    45  	makeValue := MakeValueFuncOf(t.Elem())
    46  	makeArray := func(n int) reflect.Value {
    47  		array := reflect.MakeSlice(t, n, n)
    48  		for i := 0; i < n; i++ {
    49  			makeValue(array.Index(i), r)
    50  		}
    51  		return array
    52  	}
    53  
    54  	if makeArray == nil {
    55  		panic("cannot run quick check on function with input of type " + v.Type().In(0).String())
    56  	}
    57  
    58  	for _, n := range c.Sizes {
    59  		for i := 0; i < 3; i++ {
    60  			in := makeArray(n)
    61  			ok := v.Call([]reflect.Value{in})
    62  			if !ok[0].Bool() {
    63  				return fmt.Errorf("test #%d: failed on input of size %d: %#v\n", i+1, n, in.Interface())
    64  			}
    65  		}
    66  	}
    67  	return nil
    68  
    69  }
    70  
    71  type MakeValueFunc func(reflect.Value, *rand.Rand)
    72  
    73  func MakeValueFuncOf(t reflect.Type) MakeValueFunc {
    74  	switch t {
    75  	case reflect.TypeOf(time.Time{}):
    76  		return func(v reflect.Value, r *rand.Rand) {
    77  			// TODO: This is a hack to support the matching of times in a precision
    78  			// other than nanosecond by generating times rounded to the second. A
    79  			// better solution would be to update columns types to add a compare
    80  			// function.
    81  			sec := r.Int63n(2524608000) // 2050-01-01
    82  			v.Set(reflect.ValueOf(time.Unix(sec, 0).UTC()))
    83  		}
    84  	}
    85  
    86  	switch t.Kind() {
    87  	case reflect.Bool:
    88  		return func(v reflect.Value, r *rand.Rand) {
    89  			v.SetBool((r.Int() % 2) != 0)
    90  		}
    91  
    92  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    93  		return func(v reflect.Value, r *rand.Rand) {
    94  			v.SetInt(r.Int63n(math.MaxInt32))
    95  		}
    96  
    97  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    98  		return func(v reflect.Value, r *rand.Rand) {
    99  			v.SetUint(r.Uint64())
   100  		}
   101  
   102  	case reflect.Float32, reflect.Float64:
   103  		return func(v reflect.Value, r *rand.Rand) {
   104  			v.SetFloat(r.Float64())
   105  		}
   106  
   107  	case reflect.String:
   108  		return func(v reflect.Value, r *rand.Rand) {
   109  			const characters = "1234567890qwertyuiopasdfghjklzxcvbnm"
   110  			s := new(strings.Builder)
   111  			n := r.Intn(10)
   112  			for i := 0; i < n; i++ {
   113  				s.WriteByte(characters[i])
   114  			}
   115  			v.SetString(s.String())
   116  		}
   117  
   118  	case reflect.Array:
   119  		makeElem := MakeValueFuncOf(t.Elem())
   120  		return func(v reflect.Value, r *rand.Rand) {
   121  			for i, n := 0, v.Len(); i < n; i++ {
   122  				makeElem(v.Index(i), r)
   123  			}
   124  		}
   125  
   126  	case reflect.Slice:
   127  		switch e := t.Elem(); e.Kind() {
   128  		case reflect.Uint8:
   129  			return func(v reflect.Value, r *rand.Rand) {
   130  				b := make([]byte, r.Intn(50))
   131  				r.Read(b)
   132  				v.SetBytes(b)
   133  			}
   134  		default:
   135  			makeElem := MakeValueFuncOf(t.Elem())
   136  			return func(v reflect.Value, r *rand.Rand) {
   137  				n := r.Intn(10)
   138  				s := reflect.MakeSlice(t, n, n)
   139  				for i := 0; i < n; i++ {
   140  					makeElem(s.Index(i), r)
   141  				}
   142  				v.Set(s)
   143  			}
   144  		}
   145  
   146  	case reflect.Map:
   147  		makeKey := MakeValueFuncOf(t.Key())
   148  		makeElem := MakeValueFuncOf(t.Elem())
   149  		return func(v reflect.Value, r *rand.Rand) {
   150  			m := reflect.MakeMap(t)
   151  			n := r.Intn(10)
   152  			k := reflect.New(t.Key()).Elem()
   153  			e := reflect.New(t.Elem()).Elem()
   154  			for i := 0; i < n; i++ {
   155  				makeKey(k, r)
   156  				makeElem(e, r)
   157  				m.SetMapIndex(k, e)
   158  			}
   159  			v.Set(m)
   160  		}
   161  
   162  	case reflect.Struct:
   163  		fields := make([]reflect.StructField, 0, t.NumField())
   164  		makeValues := make([]MakeValueFunc, 0, cap(fields))
   165  		for i, n := 0, cap(fields); i < n; i++ {
   166  			if f := t.Field(i); f.PkgPath == "" { // skip unexported fields
   167  				fields = append(fields, f)
   168  				makeValues = append(makeValues, MakeValueFuncOf(f.Type))
   169  			}
   170  		}
   171  		return func(v reflect.Value, r *rand.Rand) {
   172  			for i := range fields {
   173  				makeValues[i](v.FieldByIndex(fields[i].Index), r)
   174  			}
   175  		}
   176  
   177  	case reflect.Ptr:
   178  		t = t.Elem()
   179  		makeValue := MakeValueFuncOf(t)
   180  		return func(v reflect.Value, r *rand.Rand) {
   181  			v.Set(reflect.New(t))
   182  			makeValue(v.Elem(), r)
   183  		}
   184  
   185  	default:
   186  		panic("quick.Check does not support test values of type " + t.String())
   187  	}
   188  }