2
0
Fork 0
mirror of https://github.com/ii64/sonic.git synced 2026-06-21 00:46:43 +08:00
sonic/fuzz/struct_fuzz_test.go

165 lines
No EOL
4.3 KiB
Go

// +build go1.18
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sonic_fuzz
import (
`encoding/json`
`math/rand`
`fmt`
`reflect`
`strconv`
`strings`
`testing`
`github.com/bytedance/sonic`
`github.com/stretchr/testify/require`
)
func generateNullType() reflect.Type {
tab := []reflect.Type {
reflect.TypeOf(int(0)),
reflect.TypeOf(uint(0)),
reflect.TypeOf("string"),
reflect.TypeOf(struct{}{}),
reflect.TypeOf(json.Number("0")),
reflect.TypeOf([]interface{}{}),
reflect.TypeOf(map[string]interface{}{}),
}
return tab[int(rand.Int() % len(tab))]
}
func generateNumberType() reflect.Type {
tab := []reflect.Type {
reflect.TypeOf(float64(0)),
reflect.TypeOf(int64(0)),
reflect.TypeOf(uintptr(0)),
reflect.TypeOf(uint64(0)),
reflect.TypeOf(json.Number("0")),
}
return tab[int(rand.Int() % len(tab))]
}
func generatePointerType(ft reflect.Type) reflect.Type {
if ft == nil {
ft = generateNullType()
}
ftp := reflect.TypeOf(reflect.New(ft).Interface())
ftpp := reflect.TypeOf(reflect.New(ftp).Interface())
ftppp := reflect.TypeOf(reflect.New(ftpp).Interface())
tab := []reflect.Type { ft, ftp, ftpp, ftppp }
return tab[int(rand.Int() % len(tab))]
}
func generateJSONTag(name string) reflect.StructTag {
var opt string
name = strings.Split(name, ",")[0] // remove origin "," in tag name
switch int(rand.Int() % 5) {
case 0: return reflect.StructTag(`json:"-"`) // always omitted
case 1: return reflect.StructTag("") // empty tag
case 2: opt = "" // empty opt
case 3: opt = "omitempty"
case 4: opt = "string"
}
return reflect.StructTag(fmt.Sprintf(`json:"%s,%s"`, name, opt))
}
// Map2StructType generate random dynamic Golang Struct by Golang Map
func Map2StructType(m map[string]interface{}, maxDepth int) reflect.Type {
if maxDepth <= 0 {
return reflect.TypeOf(map[string]interface{}{})
}
fields := make([]reflect.StructField, 0)
i := 0
for k, v := range m {
/* skip the empty key */
if k == "" {
continue
}
/* set exported field name */
fn := "F" + strconv.Itoa(i)
/* set field type */
ft := reflect.TypeOf(v)
if ft == nil {
ft = generateNullType()
} else {
switch ft.Kind() {
case reflect.Map: ft = Map2StructType(v.(map[string]interface{}), maxDepth-1)
case reflect.Float64: ft = generateNumberType()
}
}
ft = generatePointerType(ft)
ef := reflect.StructField {
Name: fn,
Type: ft,
Tag : generateJSONTag(k),
}
fields = append(fields, ef)
i += 1
/* insert some private field randomly */
if int(rand.Int() % 3) != 0 {
continue
}
fn = "p" + strconv.Itoa(i)
pf := reflect.StructField {
Name: fn,
Type: ft,
PkgPath: "sonic_fuzz",
}
fields = append(fields, pf)
}
rt := reflect.StructOf(fields)
return rt
}
const _MAX_STRUCT_DEPTH = 30
// fuzzDynamicStruct is schema-based fuzz testing,
// a struct type is a JSON schema.
func fuzzDynamicStruct(t *testing.T, data []byte, v map[string]interface{}) {
typ := Map2StructType(v, _MAX_STRUCT_DEPTH)
sv := reflect.New(typ).Interface()
jv := reflect.New(typ).Interface()
// Pretouch fuzz
err := sonic.Pretouch(typ)
require.NoErrorf(t, err, "error in sonic pretouch struct %v", typ)
// Unmarshal fuzz
serr := sonic.Unmarshal(data, &sv)
jerr := json.Unmarshal(data, &jv)
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal %v", typ)
if serr != nil {
return
}
require.Equal(t, sv, jv, "different result in sonic unmarshal %v", typ)
// Marshal fuzz
sout, serr := sonic.Marshal(sv)
jout, jerr := json.Marshal(jv)
require.NoError(t, serr, "error in sonic marshal %v", typ)
require.NoError(t, jerr, "error in json marshal %v", typ)
// not comparing here because sonic marshal is different from encoding/json, as:
// require.Equalf(t, sout, jout, "different in sonic marshal %#v", typ)
var _, _ = sout, jout
}