|
| 1 | +package testutil |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "reflect" |
| 6 | + "time" |
| 7 | +) |
| 8 | + |
| 9 | +type Random struct { |
| 10 | + String func() string |
| 11 | + Bool func() bool |
| 12 | + Int func() int64 |
| 13 | + Uint func() uint64 |
| 14 | + Float func() float64 |
| 15 | + Complex func() complex128 |
| 16 | +} |
| 17 | + |
| 18 | +func NewRandom() *Random { |
| 19 | + // Guaranteed to be random... |
| 20 | + return &Random{ |
| 21 | + String: func() string { return "foo" }, |
| 22 | + Bool: func() bool { return true }, |
| 23 | + Int: func() int64 { return 500 }, |
| 24 | + Uint: func() uint64 { return 126 }, |
| 25 | + Float: func() float64 { return 3.14 }, |
| 26 | + Complex: func() complex128 { return 6.24 }, |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +// PopulateStruct does a best effort to populate a struct with random values. |
| 31 | +func PopulateStruct(s interface{}, r *Random) error { |
| 32 | + if r == nil { |
| 33 | + r = NewRandom() |
| 34 | + } |
| 35 | + |
| 36 | + v := reflect.ValueOf(s) |
| 37 | + if v.Kind() != reflect.Ptr || v.IsNil() { |
| 38 | + return fmt.Errorf("s must be a non-nil pointer") |
| 39 | + } |
| 40 | + |
| 41 | + v = v.Elem() |
| 42 | + if v.Kind() != reflect.Struct { |
| 43 | + return fmt.Errorf("s must be a pointer to a struct") |
| 44 | + } |
| 45 | + |
| 46 | + t := v.Type() |
| 47 | + for i := 0; i < t.NumField(); i++ { |
| 48 | + field := t.Field(i) |
| 49 | + fieldName := field.Name |
| 50 | + |
| 51 | + fieldValue := v.Field(i) |
| 52 | + if !fieldValue.CanSet() { |
| 53 | + continue // Skip if field is unexported |
| 54 | + } |
| 55 | + |
| 56 | + nv, err := populateValue(fieldValue, r) |
| 57 | + if err != nil { |
| 58 | + return fmt.Errorf("%s : %w", fieldName, err) |
| 59 | + } |
| 60 | + v.Field(i).Set(nv) |
| 61 | + } |
| 62 | + |
| 63 | + return nil |
| 64 | +} |
| 65 | + |
| 66 | +func populateValue(v reflect.Value, r *Random) (reflect.Value, error) { |
| 67 | + var err error |
| 68 | + |
| 69 | + // Handle some special cases |
| 70 | + switch v.Type() { |
| 71 | + case reflect.TypeOf(time.Time{}): |
| 72 | + v.Set(reflect.ValueOf(time.Date(2020, 5, 2, 5, 19, 21, 30, time.UTC))) |
| 73 | + return v, nil |
| 74 | + } |
| 75 | + |
| 76 | + switch v.Kind() { |
| 77 | + case reflect.Struct: |
| 78 | + if err := PopulateStruct(v.Addr().Interface(), r); err != nil { |
| 79 | + return v, err |
| 80 | + } |
| 81 | + case reflect.String: |
| 82 | + v.SetString(r.String()) |
| 83 | + case reflect.Bool: |
| 84 | + v.SetBool(true) |
| 85 | + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| 86 | + v.SetInt(r.Int()) |
| 87 | + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| 88 | + v.SetUint(r.Uint()) |
| 89 | + case reflect.Float32, reflect.Float64: |
| 90 | + v.SetFloat(r.Float()) |
| 91 | + case reflect.Complex64, reflect.Complex128: |
| 92 | + v.SetComplex(r.Complex()) |
| 93 | + case reflect.Array: |
| 94 | + for i := 0; i < v.Len(); i++ { |
| 95 | + nv, err := populateValue(v.Index(i), r) |
| 96 | + if err != nil { |
| 97 | + return v, fmt.Errorf("array index %d : %w", i, err) |
| 98 | + } |
| 99 | + v.Index(i).Set(nv) |
| 100 | + } |
| 101 | + case reflect.Map: |
| 102 | + m := reflect.MakeMap(v.Type()) |
| 103 | + |
| 104 | + // Set a value in the map |
| 105 | + k := reflect.New(v.Type().Key()) |
| 106 | + kv := reflect.New(v.Type().Elem()) |
| 107 | + k, err = populateValue(k, r) |
| 108 | + if err != nil { |
| 109 | + return v, fmt.Errorf("map key : %w", err) |
| 110 | + } |
| 111 | + kv, err = populateValue(kv, r) |
| 112 | + if err != nil { |
| 113 | + return v, fmt.Errorf("map value : %w", err) |
| 114 | + } |
| 115 | + |
| 116 | + m.SetMapIndex(k, kv) |
| 117 | + return m, nil |
| 118 | + case reflect.Pointer: |
| 119 | + return populateValue(v.Elem(), r) |
| 120 | + case reflect.Slice: |
| 121 | + s := reflect.MakeSlice(v.Type(), 2, 2) |
| 122 | + sv, err := populateValue(reflect.New(v.Type().Elem()), r) |
| 123 | + if err != nil { |
| 124 | + return v, fmt.Errorf("slice value : %w", err) |
| 125 | + } |
| 126 | + |
| 127 | + s.Index(0).Set(sv) |
| 128 | + s.Index(1).Set(sv) |
| 129 | + //reflect.AppendSlice(s, sv) |
| 130 | + |
| 131 | + return s, nil |
| 132 | + case reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func, reflect.Interface: |
| 133 | + // Unsupported |
| 134 | + return v, fmt.Errorf("%s is not supported", v.Kind()) |
| 135 | + default: |
| 136 | + return v, fmt.Errorf("unsupported kind %s", v.Kind()) |
| 137 | + } |
| 138 | + return v, nil |
| 139 | +} |
0 commit comments