-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplan.go
446 lines (343 loc) · 11.8 KB
/
plan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
// Copyright 2021 Harold Campbell. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package salem
import (
"fmt"
"math/rand"
"reflect"
"time"
)
type RunType uint
type FactoryActionType func(reflect.Type, string) GenType
type SequenceActionType func(int) GenType // func(itemIndex int) GenType
type fieldHandlerType func(int) interface{}
const (
Invalid RunType = iota
MinRun
MaxRun
ExactRun
)
type PlanRun struct {
RunType RunType
Count int
}
type fieldSetter struct {
fieldAction GenType
factoryAction FactoryActionType
fieldSequenceAction SequenceActionType
}
// mapSetter used to hold the generators for keys of values for a map field
type mapSetter struct {
fieldSequenceKeyAction SequenceActionType
fieldSequenceValueAction SequenceActionType
}
type Plan struct {
omittedFields map[string]bool // ignore these fields
ensuredFields map[string]fieldSetter // fields set via ensure
constrainedFields map[string]FieldConstraint // fields constraints
generators map[reflect.Kind]func() interface{}
kindProcessors map[reflect.Kind]processorType
ensuredMapFields map[string]mapSetter // map key fields set via ensure
run *PlanRun
evalItemCountAction func()
mapPlanRun map[string]*PlanRun // plan runs for different Maps
evalMapItemCountAction map[string]func()
fieldHandlers map[string]fieldHandlerType // FieldName -> Handler
parentName string
maxConstraintRetryAttempts int
}
func NewPlan() *Plan {
p := &Plan{}
p.omittedFields = make(map[string]bool)
p.ensuredFields = make(map[string]fieldSetter)
p.constrainedFields = make(map[string]FieldConstraint)
p.generators = make(map[reflect.Kind]func() interface{})
p.maxConstraintRetryAttempts = SuggestedConstraintRetryAttempts
p.ensuredMapFields = make(map[string]mapSetter)
p.mapPlanRun = make(map[string]*PlanRun)
p.evalMapItemCountAction = make(map[string]func())
p.fieldHandlers = make(map[string]fieldHandlerType)
p.initDefaultGenerators()
p.initKindProcessors()
return p
}
func (p *Plan) GetPlanRun() *PlanRun {
return p.run
}
func (p *Plan) GetMapPlanRun(fieldName string) *PlanRun {
return p.mapPlanRun[fieldName]
}
func (p *Plan) SetMaxConstraintsRetryAttempts(maxRetries int) {
p.maxConstraintRetryAttempts = maxRetries
}
func (p *Plan) SetItemCountHandler(handler func()) {
p.evalItemCountAction = handler
}
func (p *Plan) SetMapItemCountHandler(fieldName string, handler func()) {
p.evalMapItemCountAction[fieldName] = handler
}
func (p *Plan) OmitField(fieldName string) {
p.omittedFields[fieldName] = true
}
func (p *Plan) EnsuredFieldValue(fieldName string, sharedValue interface{}) {
setter := fieldSetter{
fieldAction: func() interface{} {
return sharedValue
},
}
p.ensuredFields[fieldName] = setter
}
func (p *Plan) EnsuredFieldValueConstraint(fieldName string, constraint FieldConstraint) {
p.constrainedFields[fieldName] = constraint
}
func (p *Plan) EnsuredFactoryFieldValue(fieldName string, sharedValue interface{}) {
setter := fieldSetter{
factoryAction: makeFactoryAction(sharedValue.(*Factory), p),
}
p.ensuredFields[fieldName] = setter
}
// EnsureSequence returns a seq item based on the item index
func (p *Plan) EnsureSequence(fieldName string, seq []interface{}) {
p.ensuredFields[fieldName] = fieldSetter{fieldSequenceAction: sequenceCallbackCreator(seq)}
}
// EnsureSequenceAcross returns the seq[] items based on the sequence index.
// The sequenceIndex is independent of the item index.
func (p *Plan) EnsureSequenceAcross(fieldName string, seq []interface{}) {
var sequenceIndex int
seqHandler := func(itemIndex int) GenType {
action := func() interface{} {
var val interface{}
if sequenceIndex < len(seq) {
val = seq[sequenceIndex]
}
result := val
sequenceIndex += 1
return result
}
return action
}
p.ensuredFields[fieldName] = fieldSetter{fieldSequenceAction: seqHandler}
}
func (p *Plan) EnsureMapKeySequence(fieldName string, seq []interface{}) {
var setter mapSetter
if _, ok := p.ensuredMapFields[fieldName]; ok {
setter = p.ensuredMapFields[fieldName]
setter.fieldSequenceKeyAction = sequenceCallbackCreator(seq)
} else {
setter = mapSetter{fieldSequenceKeyAction: sequenceCallbackCreator(seq)}
}
p.ensuredMapFields[fieldName] = setter
}
func (p *Plan) EnsureMapValueSequence(fieldName string, seq []interface{}) {
var setter mapSetter
if _, ok := p.ensuredMapFields[fieldName]; ok {
setter = p.ensuredMapFields[fieldName]
setter.fieldSequenceValueAction = sequenceCallbackCreator(seq)
} else {
setter = mapSetter{fieldSequenceValueAction: sequenceCallbackCreator(seq)}
}
p.ensuredMapFields[fieldName] = setter
}
func sequenceCallbackCreator(seq []interface{}) func(itemIndex int) GenType {
return func(itemIndex int) GenType {
action := func() interface{} {
var val interface{}
if itemIndex < len(seq) {
val = seq[itemIndex]
}
result := val
return result
}
return action
}
}
// EnsureMayKeySequence returns a seq item based on the item index
func (p *Plan) EnsureMayKeySequence(fieldName string, seq []interface{}) {
seqHandler := func(itemIndex int) GenType {
action := func() interface{} {
var val interface{}
if itemIndex < len(seq) {
val = seq[itemIndex]
}
result := val
return result
}
return action
}
p.ensuredFields[fieldName] = fieldSetter{fieldSequenceAction: seqHandler}
}
func (p *Plan) SetRunCount(runType RunType, n int) {
p.run = &PlanRun{
RunType: runType,
Count: n,
}
}
func (p *Plan) SetMapRunCount(fieldName string, runType RunType, n int) {
p.mapPlanRun[fieldName] = &PlanRun{
RunType: runType,
Count: n,
}
}
func (p *Plan) AddFieldHandler(fieldName string, handler fieldHandlerType) {
if p.fieldHandlers[fieldName] != nil {
panic(fmt.Sprintf("There is already a fieldHandler assigned to to the `%v` field. Remove the fieldhandler first.", fieldName))
}
p.fieldHandlers[fieldName] = handler
}
func (p *Plan) RemoveFieldHandler(fieldName string) {
p.fieldHandlers[fieldName] = nil
}
func (p *Plan) CopyParentConstraints(pp *Plan) {
for k, v := range pp.ensuredFields {
p.ensuredFields[k] = v
}
for k, v := range pp.omittedFields {
p.omittedFields[k] = v
}
}
func (p *Plan) Run(f *Factory) []interface{} {
rand.Seed(time.Now().UnixNano())
p.evalItemCountAction()
items := make([]interface{}, 0, p.run.Count)
for itemIndex := 0; itemIndex < p.run.Count; itemIndex++ {
mockType := reflect.TypeOf(f.rootType)
items = append(items, p.generateRandomMock(mockType, itemIndex))
}
return items
}
func (p *Plan) generateRandomMock(mockType reflect.Type, itemIndex int) interface{} {
rand.Seed(time.Now().UnixNano())
// Create an mock instance of the struct
newMockPtr := reflect.New(mockType)
newElm := newMockPtr.Elem()
if mockType.Kind() == reflect.Ptr {
ptrType := mockType.Elem()
newMockPtr = reflect.New(ptrType) // Make an instance based on the pointer type
newElm = newMockPtr.Elem()
mockType = newElm.Type()
}
if isPrimitiveKind(mockType) {
generator := p.GetKindGenerator(mockType.Kind())
val := generator()
return val
}
for i := 0; i < mockType.NumField(); i++ {
fieldName := mockType.Field(i).Name
iField := newElm.Field(i) // Get related instance field in the mock instance
if !iField.CanSet() {
continue // Skip private instance fields
}
qualifiedName := distinctFileName(p.parentName, fieldName)
if p.omittedFields[qualifiedName] == true {
continue // Skip omitted fields
}
val := p.generateValue(iField.Type(), itemIndex, qualifiedName)
if !val.IsValid() {
continue
}
iField.Set(val)
}
return newMockPtr.Elem().Interface()
}
func (p *Plan) generateValue(fieldType reflect.Type, itemIndex int, qualifiedName string) reflect.Value {
if p.fieldHandlers[qualifiedName] != nil {
generator := p.fieldHandlers[qualifiedName]
val := generator(itemIndex)
return reflect.ValueOf(val)
}
generator := p.getValueGenerator(fieldType, itemIndex, qualifiedName)
constraint := p.constrainedFields[qualifiedName]
if constraint == nil { // Generate field value and exit since no constraint
return p.generateFieldValue(generator, fieldType, itemIndex, qualifiedName)
}
isValueFromEnsureAction := p.ensuredFields[qualifiedName].factoryAction != nil || p.ensuredFields[qualifiedName].fieldAction != nil
if isValueFromEnsureAction { // Ensure Ensured field meets constraint
val := p.generateFieldValue(generator, fieldType, itemIndex, qualifiedName)
if !constraint.IsValid(val.Interface()) {
panic(fmt.Sprintf("Constraint clashes with one of your Ensure methods. Invalid FieldConstraint for field '%v'. Constraint: %#v.", qualifiedName, constraint))
}
return val
}
var attempt = 0
var val reflect.Value
for { // Try till constraint is met or give up after maxConstraintRetryAttempts attemps
val = p.generateFieldValue(generator, fieldType, itemIndex, qualifiedName)
attempt += 1
if constraint.IsValid(val.Interface()) {
break
}
if attempt > p.maxConstraintRetryAttempts {
panic(fmt.Sprintf("Unable to meet constraint %v after '%v' tries", constraint, p.maxConstraintRetryAttempts))
}
}
return val
}
func (p *Plan) getValueGenerator(fieldType reflect.Type, itemIndex int, qualifiedName string) GenType {
if p.ensuredFields[qualifiedName].factoryAction != nil {
return p.ensuredFields[qualifiedName].factoryAction(fieldType, qualifiedName)
} else if p.ensuredFields[qualifiedName].fieldSequenceAction != nil {
return p.ensuredFields[qualifiedName].fieldSequenceAction(itemIndex)
} else if p.ensuredFields[qualifiedName].fieldAction != nil {
return p.ensuredFields[qualifiedName].fieldAction
}
return p.GetKindGenerator(fieldType.Kind())
}
func (p *Plan) generateFieldValue(generator GenType, fieldType reflect.Type, itemIndex int, qualifiedName string) reflect.Value {
if isPrimitiveKind(fieldType) {
val := generator()
return reflect.ValueOf(val)
}
k := fieldType.Kind()
processor := p.kindProcessors[k]
if processor == nil {
fmt.Printf("[updateFieldValue] (Unknow type) %v \n", fieldType.Name())
panic(fmt.Sprintf("[updateFieldValue] Unsupported type: %#v kind:%v", fieldType, k))
}
return processor(generator, fieldType, itemIndex, qualifiedName)
}
// toPtr converts obj to *obj
func toPtr(obj reflect.Value) reflect.Value {
vp := reflect.New(reflect.TypeOf(obj.Interface()))
vp.Elem().Set(reflect.ValueOf(obj.Interface()))
return vp
}
func makeFactoryAction(fac *Factory, currentPlan *Plan) FactoryActionType {
return func(fieldType reflect.Type, qualifiedName string) func() interface{} {
fieldPtr := reflect.New(fieldType) // Make an instance based on the pointer type
fieldVal := fieldPtr.Elem()
// Extract the type from the slice and assign an instance to the rootType.
// I want to go from []examples.Person to example.Person
fac.rootType = reflect.New(reflect.TypeOf(fieldVal.Interface()).Elem()).Elem().Interface() // Overwrite the factory with the public field type
return func() interface{} { // The actual generator
fac.plan.parentName = qualifiedName
fac.plan.CopyParentConstraints(currentPlan)
result := fac.Execute()
return result
}
}
}
func distinctFileName(parentFieldName string, fieldName string) string {
if parentFieldName == "" {
return fieldName
}
return fmt.Sprintf("%s.%s", parentFieldName, fieldName)
}
func isPrimitiveKind(t reflect.Type) bool {
k := t.Kind()
switch k {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Float32, reflect.Float64,
reflect.String:
return true
}
return false
}
func isPrtPrimitiveKind(t reflect.Type) bool {
if t.Kind() != reflect.Ptr {
return false
}
return isPrimitiveKind(t.Elem())
}