-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgenerator.go
More file actions
172 lines (135 loc) · 7.17 KB
/
Copy pathgenerator.go
File metadata and controls
172 lines (135 loc) · 7.17 KB
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
package chaff
import (
"fmt"
"github.com/ryanolee/go-chaff/internal/util"
"github.com/ryanolee/go-chaff/rand"
)
type (
Generator interface {
fmt.Stringer
Generate(*GeneratorOptions) interface{}
}
GeneratorOptions struct {
// The source of randomness to use for the given generation.
// Please note that some parts of the generators use different sources of randomness.
// ("regex" generation and "format" strings)
Rand *rand.RandUtil `json:"-"`
// The default minimum number value
DefaultNumberMinimum int `json:"defaultNumberMinimum,omitempty" jsonschema:"title=Default Number Minimum"`
// The default maximum number value
DefaultNumberMaximum int `json:"defaultNumberMaximum,omitempty" jsonschema:"title=Default Number Maximum"`
// The default minimum String length
DefaultStringMinLength int `json:"defaultStringMinLength,omitempty" jsonschema:"title=Default String Minimum Length"`
// The default maximum String length
DefaultStringMaxLength int `json:"defaultStringMaxLength,omitempty" jsonschema:"title=Default String Maximum Length"`
// The default minimum array length
DefaultArrayMinItems int `json:"defaultArrayMinItems,omitempty" jsonschema:"title=Default Array Minimum Items"`
// The default maximum array length
// This will be set min + this inf the event a minimum value is set
DefaultArrayMaxItems int `json:"defaultArrayMaxItems,omitempty" jsonschema:"title=Default Array Maximum Items"`
// The default minimum object properties (Will be ignored if there are fewer properties available)
DefaultObjectMinProperties int `json:"defaultObjectMinProperties,omitempty" jsonschema:"title=Default Object Minimum Properties"`
// The default maximum object properties (Will be ignored if there are fewer properties available)
DefaultObjectMaxProperties int `json:"defaultObjectMaxProperties,omitempty" jsonschema:"title=Default Object Maximum Properties"`
// The maximum dnumber of references to resolve at once in terms of depth (Default: 10)
MaximumReferenceDepth int `json:"maximumReferenceDepth,omitempty" jsonschema:"title=Maximum Reference Depth"`
// In the event that schemas are recursive there is a good chance the generator
// can run forever. This option will bypass the check for cyclic references
// Please defer to the MaximumReferenceDepth option if possible when using this
BypassCyclicReferenceCheck bool `json:"bypassCyclicReferenceCheck,omitempty" jsonschema:"title=Bypass Cyclic Reference Check"`
// Used to keep track of references during a resolution cycle (Used internally and can be ignored)
ReferenceResolver referenceResolver `json:"-"`
// Though technically in some cases a schema may allow for additional
// values it might not always be desireable. this option suppresses fallback_n values
// so that they will only appear to make up a "minimum value" forces them to
SuppressFallbackValues bool `json:"suppressFallbackValues,omitempty" jsonschema:"title=Suppress Fallback Values"`
// The maximum number of times to attempt to generate a unique value
// when using unique* constraints in schemas
MaximumUniqueGeneratorAttempts int `json:"maximumUniqueGeneratorAttempts,omitempty" jsonschema:"title=Maximum Unique Generator Attempts"`
// The maximum number of times to attempt to satisfy "if" statements
// before giving up
MaximumIfAttempts int `json:"maximumIfAttempts,omitempty" jsonschema:"title=Maximum If Attempts"`
// The maximum number of times to attempt to satisfy "oneOf" statements
// before giving up
MaximumOneOfAttempts int `json:"maximumOneOfAttempts,omitempty" jsonschema:"title=Maximum OneOf Attempts"`
// The maximum number of steps to take when generating a value
// after which the the generator will begin to do the "bare minimum" to generate a value
MaximumGenerationSteps int `json:"maximumGenerationSteps,omitempty" jsonschema:"title=Maximum Generation Steps"`
// The maximum number of steps to take before giving up entirely and aborting generation
// This is a hard cap on generation steps to prevent extremely long generation times
CutoffGenerationSteps int `json:"cutoffGenerationSteps,omitempty" jsonschema:"title=Cutoff Generation Steps"`
overallComplexity int `json:"-"`
}
)
func withGeneratorOptionsDefaults(options GeneratorOptions) *GeneratorOptions {
randUtil := options.Rand
if options.Rand == nil {
randUtil = rand.NewRandUtilFromTime()
}
return &GeneratorOptions{
// General
Rand: randUtil,
// Number
DefaultNumberMinimum: util.GetInt(options.DefaultNumberMinimum, 0),
DefaultNumberMaximum: util.GetInt(options.DefaultNumberMaximum, 100),
// String
DefaultStringMinLength: util.GetInt(options.DefaultStringMinLength, 0),
DefaultStringMaxLength: util.GetInt(options.DefaultStringMaxLength, 100),
// Array
DefaultArrayMinItems: util.GetInt(options.DefaultArrayMinItems, 0),
DefaultArrayMaxItems: util.GetInt(options.DefaultArrayMaxItems, 10),
// Object
DefaultObjectMinProperties: util.GetInt(options.DefaultObjectMinProperties, 0),
DefaultObjectMaxProperties: util.GetInt(options.DefaultObjectMaxProperties, 10),
SuppressFallbackValues: util.GetBool(options.SuppressFallbackValues, true),
// References
BypassCyclicReferenceCheck: util.GetBool(options.BypassCyclicReferenceCheck, false),
MaximumReferenceDepth: util.GetInt(options.MaximumReferenceDepth, 10),
ReferenceResolver: referenceResolver{},
// Reattempts
MaximumUniqueGeneratorAttempts: util.GetInt(options.MaximumUniqueGeneratorAttempts, 100),
MaximumIfAttempts: util.GetInt(options.MaximumIfAttempts, 100),
MaximumOneOfAttempts: util.GetInt(options.MaximumOneOfAttempts, 100),
// Generation
MaximumGenerationSteps: util.GetInt(options.MaximumGenerationSteps, 100),
CutoffGenerationSteps: util.GetInt(options.CutoffGenerationSteps, 2000),
overallComplexity: 0,
}
}
func (g *GeneratorOptions) ShouldCutoff() bool {
return g.CutoffGenerationSteps > 0 && g.overallComplexity > g.CutoffGenerationSteps
}
func (g *GeneratorOptions) ShouldMinimize() bool {
return g.MaximumGenerationSteps > 0 && g.overallComplexity > g.MaximumGenerationSteps
}
// ScaledRetryBudget returns a reduced number of retry attempts proportional to
// how close the overall complexity is to the cutoff. When complexity is below
// the "minimize" threshold the full budget is returned. As complexity grows
// towards CutoffGenerationSteps the budget is linearly reduced down to 1.
// This prevents premature reduction on moderately complex schemas while still
// capping runaway retries on deeply cyclic ones.
func (g *GeneratorOptions) ScaledRetryBudget(baseAttempts int) int {
if baseAttempts <= 0 {
return 1
}
// Below the minimize threshold: full budget
if !g.ShouldMinimize() {
return baseAttempts
}
// Past the cutoff: absolute minimum
if g.ShouldCutoff() {
return 1
}
// Linear scale between minimize threshold and cutoff
remaining := float64(g.CutoffGenerationSteps - g.overallComplexity)
window := float64(g.CutoffGenerationSteps - g.MaximumGenerationSteps)
if window <= 0 {
return 1
}
ratio := remaining / window // 1.0 at minimize threshold, 0.0 at cutoff
scaled := int(ratio * float64(baseAttempts))
if scaled < 1 {
scaled = 1
}
return scaled
}