@@ -19,6 +19,8 @@ package thrift
1919import (
2020 "context"
2121 "fmt"
22+ "math/rand"
23+ "time"
2224
2325 "github.com/cloudwego/dynamicgo/internal/util_test"
2426)
@@ -55,3 +57,297 @@ func FnRequest(fn *FunctionDescriptor) *TypeDescriptor {
5557 // let-it-fail: it panic when something is nil
5658 return fn .Request ().Struct ().Fields ()[0 ].Type ()
5759}
60+
61+ type FuzzDataOptions struct {
62+ // MaxDepth is the max depth of the generated data
63+ MaxDepth int
64+ // MaxWidth is the max width (map/list/string length) of the generated data
65+ MaxWidth int
66+ // DefaultFieldsRatio is the default ratio of fields to be filled in a struct
67+ DefaultFieldsRatio float64
68+ // OptionalFieldsRatio is the ratio of optional fields to be filled in a struct
69+ OptionalFieldsRatio float64
70+ }
71+
72+ // MakeFuzzData generates random data based on the given TypeDescriptor
73+ // It returns a map[string]interface{} like structure for complex types
74+ func MakeFuzzData (reqDesc * TypeDescriptor , opts FuzzDataOptions ) (interface {}, error ) {
75+ // Set default options
76+ if opts .MaxDepth <= 0 {
77+ opts .MaxDepth = 3
78+ }
79+ if opts .MaxWidth <= 0 {
80+ opts .MaxWidth = 1
81+ }
82+ if opts .DefaultFieldsRatio <= 0 {
83+ opts .DefaultFieldsRatio = 0.8
84+ }
85+ if opts .OptionalFieldsRatio <= 0 {
86+ opts .OptionalFieldsRatio = 0.5
87+ }
88+
89+ rng := rand .New (rand .NewSource (time .Now ().UnixNano ()))
90+ return makeFuzzValue (reqDesc , opts , rng , 0 )
91+ }
92+
93+ func makeFuzzValue (desc * TypeDescriptor , opts FuzzDataOptions , rng * rand.Rand , depth int ) (interface {}, error ) {
94+ if desc == nil {
95+ return nil , nil
96+ }
97+
98+ // Check depth limit
99+ if depth >= opts .MaxDepth {
100+ return getDefaultValue (desc .Type ()), nil
101+ }
102+
103+ switch desc .Type () {
104+ case BOOL :
105+ return rng .Intn (2 ) == 1 , nil
106+
107+ case BYTE : // I08 is the same as BYTE
108+ return int8 (rng .Intn (256 ) - 128 ), nil
109+
110+ case I16 :
111+ return int16 (rng .Intn (65536 ) - 32768 ), nil
112+
113+ case I32 :
114+ return rng .Int31 (), nil
115+
116+ case I64 :
117+ return rng .Int63 (), nil
118+
119+ case DOUBLE :
120+ return rng .Float64 () * 1000 , nil
121+
122+ case STRING :
123+ return generateRandomString (rng , opts .MaxWidth ), nil
124+
125+ case LIST :
126+ return makeFuzzList (desc , opts , rng , depth )
127+
128+ case SET :
129+ return makeFuzzList (desc , opts , rng , depth )
130+
131+ case MAP :
132+ return makeFuzzMap (desc , opts , rng , depth )
133+
134+ case STRUCT :
135+ return makeFuzzStruct (desc , opts , rng , depth )
136+
137+ default :
138+ return nil , nil
139+ }
140+ }
141+
142+ func makeFuzzList (desc * TypeDescriptor , opts FuzzDataOptions , rng * rand.Rand , depth int ) (interface {}, error ) {
143+ elemDesc := desc .Elem ()
144+ if elemDesc == nil {
145+ return []interface {}{}, nil
146+ }
147+
148+ size := rng .Intn (opts .MaxWidth )
149+ list := make ([]interface {}, size )
150+
151+ for i := 0 ; i < size ; i ++ {
152+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
153+ if err != nil {
154+ return nil , err
155+ }
156+ list [i ] = val
157+ }
158+
159+ return list , nil
160+ }
161+
162+ func makeFuzzMap (desc * TypeDescriptor , opts FuzzDataOptions , rng * rand.Rand , depth int ) (interface {}, error ) {
163+ keyDesc := desc .Key ()
164+ elemDesc := desc .Elem ()
165+
166+ if keyDesc == nil || elemDesc == nil {
167+ return make (map [string ]interface {}), nil
168+ }
169+
170+ size := rng .Intn (opts .MaxWidth )
171+
172+ switch keyDesc .Type () {
173+ case STRING :
174+ result := make (map [string ]interface {}, size )
175+ for i := 0 ; i < size ; i ++ {
176+ key := generateRandomString (rng , 8 )
177+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
178+ if err != nil {
179+ return nil , err
180+ }
181+ result [key ] = val
182+ }
183+ return result , nil
184+ case I32 :
185+ result := make (map [int32 ]interface {}, size )
186+ for i := 0 ; i < size ; i ++ {
187+ key := int32 (i )
188+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
189+ if err != nil {
190+ return nil , err
191+ }
192+ result [key ] = val
193+ }
194+ return result , nil
195+ case I64 :
196+ result := make (map [int64 ]interface {}, size )
197+ for i := 0 ; i < size ; i ++ {
198+ key := int64 (i )
199+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
200+ if err != nil {
201+ return nil , err
202+ }
203+ result [key ] = val
204+ }
205+ return result , nil
206+ case I16 :
207+ result := make (map [int16 ]interface {}, size )
208+ for i := 0 ; i < size ; i ++ {
209+ key := int16 (i )
210+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
211+ if err != nil {
212+ return nil , err
213+ }
214+ result [key ] = val
215+ }
216+ return result , nil
217+ case BYTE : // I08
218+ result := make (map [int8 ]interface {}, size )
219+ for i := 0 ; i < size ; i ++ {
220+ key := int8 (i )
221+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
222+ if err != nil {
223+ return nil , err
224+ }
225+ result [key ] = val
226+ }
227+ return result , nil
228+ case BOOL :
229+ result := make (map [bool ]interface {}, 2 )
230+ // ensure we can have up to two entries: true/false
231+ for i := 0 ; i < size && i < 2 ; i ++ {
232+ key := (i % 2 == 0 )
233+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
234+ if err != nil {
235+ return nil , err
236+ }
237+ result [key ] = val
238+ }
239+ return result , nil
240+ case DOUBLE :
241+ result := make (map [float64 ]interface {}, size )
242+ for i := 0 ; i < size ; i ++ {
243+ key := rng .Float64 ()
244+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
245+ if err != nil {
246+ return nil , err
247+ }
248+ result [key ] = val
249+ }
250+ return result , nil
251+ default :
252+ // Fallback to string key if unsupported key type
253+ result := make (map [string ]interface {}, size )
254+ for i := 0 ; i < size ; i ++ {
255+ key := fmt .Sprintf ("key_%d_%s" , i , generateRandomString (rng , 5 ))
256+ val , err := makeFuzzValue (elemDesc , opts , rng , depth + 1 )
257+ if err != nil {
258+ return nil , err
259+ }
260+ result [key ] = val
261+ }
262+ return result , nil
263+ }
264+ }
265+
266+ func makeFuzzStruct (desc * TypeDescriptor , opts FuzzDataOptions , rng * rand.Rand , depth int ) (interface {}, error ) {
267+ if desc .Struct () == nil {
268+ return make (map [string ]interface {}), nil
269+ }
270+
271+ result := make (map [string ]interface {})
272+ fields := desc .Struct ().Fields ()
273+
274+ for _ , field := range fields {
275+ if field == nil {
276+ continue
277+ }
278+
279+ // Decide whether to include this field based on requireness
280+ shouldInclude := shouldIncludeField (field , opts , rng )
281+ if ! shouldInclude {
282+ continue
283+ }
284+
285+ fieldDesc := field .Type ()
286+ if fieldDesc == nil {
287+ continue
288+ }
289+
290+ val , err := makeFuzzValue (fieldDesc , opts , rng , depth + 1 )
291+ if err != nil {
292+ return nil , err
293+ }
294+
295+ result [field .Name ()] = val
296+ }
297+
298+ return result , nil
299+ }
300+
301+ func shouldIncludeField (field * FieldDescriptor , opts FuzzDataOptions , rng * rand.Rand ) bool {
302+ req := field .Required ()
303+
304+ // Always include required fields
305+ if req == RequiredRequireness {
306+ return true
307+ }
308+
309+ // Include default fields with high probability
310+ if req == DefaultRequireness {
311+ return rng .Float64 () < opts .DefaultFieldsRatio
312+ }
313+
314+ // Include optional fields with lower probability
315+ return rng .Float64 () < opts .OptionalFieldsRatio
316+ }
317+
318+ func generateRandomString (rng * rand.Rand , maxLen int ) string {
319+ const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
320+ length := rng .Intn (maxLen ) + 1
321+ result := make ([]byte , length )
322+ for i := 0 ; i < length ; i ++ {
323+ result [i ] = charset [rng .Intn (len (charset ))]
324+ }
325+ return string (result )
326+ }
327+
328+ func getDefaultValue (typ Type ) interface {} {
329+ switch typ {
330+ case BOOL :
331+ return false
332+ case BYTE : // I08 is the same as BYTE
333+ return int8 (0 )
334+ case I16 :
335+ return int16 (0 )
336+ case I32 :
337+ return int32 (0 )
338+ case I64 :
339+ return int64 (0 )
340+ case DOUBLE :
341+ return float64 (0 )
342+ case STRING :
343+ return ""
344+ case LIST , SET :
345+ return []interface {}{}
346+ case MAP :
347+ return make (map [string ]interface {})
348+ case STRUCT :
349+ return make (map [string ]interface {})
350+ default :
351+ return nil
352+ }
353+ }
0 commit comments