mirror of
https://github.com/ii64/sonic.git
synced 2026-06-20 16:45:22 +08:00
test: add fuzz test in Go 1.18 (#208)
This commit is contained in:
parent
cb1702dc8c
commit
d9b9980291
16 changed files with 16070 additions and 0 deletions
10
fuzz/Makefile
Normal file
10
fuzz/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
fuzz:
|
||||
mkdir -p ./testdata/fuzz/FuzzMain
|
||||
git clone https://github.com/dvyukov/go-fuzz-corpus.git ./go-fuzz-corpus/
|
||||
file2fuzz -o ./testdata/fuzz/FuzzMain ./go-fuzz-corpus/json/corpus/* ./corpus/*
|
||||
go test -fuzz=Fuzz -v
|
||||
|
||||
clean:
|
||||
rm -rf ./go-fuzz-corpus/
|
||||
rm -rf ./testdata/
|
||||
76
fuzz/ast_fuzz_test.go
Normal file
76
fuzz/ast_fuzz_test.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// +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 (
|
||||
`testing`
|
||||
`github.com/bytedance/sonic`
|
||||
`github.com/bytedance/sonic/ast`
|
||||
`github.com/stretchr/testify/require`
|
||||
)
|
||||
|
||||
func fuzzASTGetFromObject(t *testing.T, data []byte, m map[string]interface{}) {
|
||||
for k, expv := range(m) {
|
||||
node, err := sonic.Get(data, k)
|
||||
require.NoErrorf(t, err, "error in ast get key -> %s", k)
|
||||
assertAstNode(t, node, expv)
|
||||
}
|
||||
}
|
||||
|
||||
func fuzzASTGetFromArray(t *testing.T, data []byte, a []interface{}) {
|
||||
var i = 0
|
||||
for ; i < len(a); i++ {
|
||||
node, err := sonic.Get(data, i)
|
||||
require.NoErrorf(t, err, "error in ast get index -> %s", i)
|
||||
assertAstNode(t, node, a[i])
|
||||
}
|
||||
_, err := sonic.Get(data, i)
|
||||
require.Errorf(t, err, "error in ast get index -> %s", i)
|
||||
}
|
||||
|
||||
func assertAstNode(t *testing.T, node ast.Node, expv interface{}) {
|
||||
switch node.Type() {
|
||||
case ast.V_NULL: require.Nilf(t, expv, "wrong in ast null")
|
||||
case ast.V_TRUE: fallthrough
|
||||
case ast.V_FALSE:
|
||||
gotv, err := node.Bool()
|
||||
require.NoErrorf(t, err, "error in ast get bool")
|
||||
require.Equalf(t, gotv, expv, "wrong in get bool")
|
||||
case ast.V_STRING:
|
||||
gotv, err := node.String()
|
||||
require.NoErrorf(t, err, "error in ast get string")
|
||||
require.Equalf(t, gotv, expv, "wrong in get string")
|
||||
case ast.V_ARRAY:
|
||||
gotv, err := node.Array()
|
||||
require.NoErrorf(t, err, "error in ast get array")
|
||||
require.Equalf(t, gotv, expv, "wrong in get array")
|
||||
case ast.V_OBJECT:
|
||||
gotv, err := node.Map()
|
||||
require.NoErrorf(t, err, "error in ast get object")
|
||||
require.Equalf(t, gotv, expv, "wrong in get object")
|
||||
case ast.V_NUMBER:
|
||||
gotv, err := node.Float64()
|
||||
require.NoErrorf(t, err, "error in ast get number")
|
||||
require.Equalf(t, gotv, expv, "wrong in get number")
|
||||
case ast.V_ANY:
|
||||
gotv, err := node.Interface()
|
||||
require.NoErrorf(t, err, "error in ast get any")
|
||||
require.Equalf(t, gotv, expv, "wrong in get any")
|
||||
}
|
||||
}
|
||||
1
fuzz/corpus/htmescape3.json
Normal file
1
fuzz/corpus/htmescape3.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
"&&&&&&&&&&&&&&&&&&&&&&&\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2\xe2&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"
|
||||
1
fuzz/corpus/htmlescap2.json
Normal file
1
fuzz/corpus/htmlescap2.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
"{\"\"\u2028\x94\xe2\x00\x00\x00\x00\x00\x00\x00\x00\u2028\x80\u2028\x80\u2028\xe2\u2028\x8a\u2028⑀\xa8\x8a\xa8\xe2\u2028\xe2\u2028\xe2\u2028\xe2\u2000\x8d\xe2\u2028\xe2\u2028\xe2\xe2\xa8\"}"
|
||||
1
fuzz/corpus/htmlescape.json
Normal file
1
fuzz/corpus/htmlescape.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
"&<>\u2028\u2029"
|
||||
1
fuzz/corpus/stringnumber.json
Normal file
1
fuzz/corpus/stringnumber.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
"123"
|
||||
1
fuzz/corpus/stringnumber2.json
Normal file
1
fuzz/corpus/stringnumber2.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
"-1.23e+1"
|
||||
50
fuzz/corpus/struct.json
Normal file
50
fuzz/corpus/struct.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"A": -8111819689795174000,
|
||||
"B": "fQhhLProOY hello \\\" 你好 😊",
|
||||
"C": 3.577476171476308e+307,
|
||||
"D": true,
|
||||
"E": 101,
|
||||
"G": null,
|
||||
"H": {
|
||||
"xSVe": null
|
||||
},
|
||||
"I": {
|
||||
"ziiOUtpFV": "RfJwBZnEwm"
|
||||
},
|
||||
"J": [
|
||||
null,
|
||||
true,
|
||||
[],
|
||||
{}
|
||||
],
|
||||
"K": [
|
||||
"fSiOMJVJdi",
|
||||
"CLpA",
|
||||
"\\",
|
||||
"123",
|
||||
""
|
||||
],
|
||||
"L": {
|
||||
"A": -1231349968732903200,
|
||||
"B": "QUqZKwhY"
|
||||
},
|
||||
"M": {
|
||||
"A": 5011712756157650000,
|
||||
"B": "hWkae"
|
||||
},
|
||||
"N": -8055612484288639000,
|
||||
"O": -412156671407452740,
|
||||
"P": "123",
|
||||
"Q": "-1.23e+1",
|
||||
"R": 0,
|
||||
"T": {},
|
||||
"U": [
|
||||
7233499441428055000,
|
||||
3869227355117358600
|
||||
],
|
||||
"V": 0,
|
||||
"W": "0.123456e-123",
|
||||
"X": {"raw": "json"},
|
||||
"Y": "abc",
|
||||
"Z": "123"
|
||||
}
|
||||
15482
fuzz/corpus/twitter.json
Normal file
15482
fuzz/corpus/twitter.json
Normal file
File diff suppressed because it is too large
Load diff
1
fuzz/corpus/twitterescaped.json
Normal file
1
fuzz/corpus/twitterescaped.json
Normal file
File diff suppressed because one or more lines are too long
173
fuzz/fuzz_test.go
Normal file
173
fuzz/fuzz_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// +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`
|
||||
`testing`
|
||||
`unicode/utf8`
|
||||
`reflect`
|
||||
`os`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`time`
|
||||
|
||||
`github.com/bytedance/sonic`
|
||||
`github.com/stretchr/testify/require`
|
||||
)
|
||||
|
||||
func FuzzMain(f *testing.F) {
|
||||
f.Add([]byte(`{
|
||||
"object": {
|
||||
"slice": [
|
||||
1,
|
||||
2.0,
|
||||
"3",
|
||||
[4],
|
||||
{5: {}}
|
||||
]
|
||||
},
|
||||
"slice": [[]],
|
||||
"string": ":)",
|
||||
"int": 1e5,
|
||||
"float": 3e-9"
|
||||
}`))
|
||||
f.Fuzz(fuzzMain)
|
||||
}
|
||||
|
||||
func fuzzMain(t *testing.T, data []byte) {
|
||||
fuzzValidate(t, data)
|
||||
fuzzHtmlEscape(t, data)
|
||||
// Only fuzz the validate json here, because the default configuration does not have validation in SONIC.
|
||||
if !utf8.Valid(data) || !json.Valid(data) {
|
||||
return
|
||||
}
|
||||
for _, typ := range []func() interface{}{
|
||||
func() interface{} { return new(interface{}) },
|
||||
func() interface{} { return new(map[string]interface{}) },
|
||||
func() interface{} { return new([]interface{}) },
|
||||
func() interface{} { return new(string) },
|
||||
func() interface{} { return new(int64) },
|
||||
func() interface{} { return new(uint64) },
|
||||
func() interface{} { return new(float64) },
|
||||
func() interface{} { return new(json.Number) },
|
||||
func() interface{} { return new(json.RawMessage) },
|
||||
func() interface{} { return new(S) },
|
||||
} {
|
||||
sv, jv := typ(), typ()
|
||||
serr := sonic.Unmarshal([]byte(data), sv)
|
||||
jerr := json.Unmarshal([]byte(data), jv)
|
||||
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||
if jerr != nil {
|
||||
return
|
||||
}
|
||||
require.Equal(t, sv, jv, "different result in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||
sout, serr := sonic.Marshal(sv)
|
||||
jout, jerr := json.Marshal(jv)
|
||||
require.NoError(t, serr, "error in sonic marshal %v", reflect.TypeOf(jv))
|
||||
require.NoError(t, jerr, "error in json marshal %v", reflect.TypeOf(jv))
|
||||
// not comparing here because sonic marshal is different from encoding/json, as:
|
||||
// case 1: 1.23e2 -> `1.23e2` in sonic, but 1.23e2 -> `1.23e+2` in encoding/json
|
||||
// case 2: "\b" -> `\\b` in sonic, but `\u0008` in encoding/json
|
||||
// require.Equal(t, sout, jout, "different in sonic marshal %v", reflect.TypeOf(jv))
|
||||
var _, _ = sout, jout
|
||||
if m, ok := sv.(*map[string]interface{}); ok {
|
||||
fuzzDynamicStruct(t, jout, *m)
|
||||
fuzzASTGetFromObject(t, jout, *m)
|
||||
}
|
||||
if a, ok := sv.(*[]interface{}); ok {
|
||||
fuzzASTGetFromArray(t, jout, *a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type S struct {
|
||||
A int `json:",omitempty"`
|
||||
B string `json:"B1,omitempty"`
|
||||
C float64
|
||||
D bool
|
||||
E uint8
|
||||
// F []byte // unmarshal []byte is different with encoding/json
|
||||
G interface{}
|
||||
H map[string]interface{}
|
||||
I map[string]string
|
||||
J []interface{}
|
||||
K []string
|
||||
L S1
|
||||
M *S1
|
||||
N *int
|
||||
O **int
|
||||
P int `json:",string"`
|
||||
Q float64 `json:",string"`
|
||||
R int `json:"-"`
|
||||
T struct {}
|
||||
U [2]int
|
||||
V uintptr
|
||||
W json.Number
|
||||
X json.RawMessage
|
||||
Y Marshaller
|
||||
Z TextMarshaller
|
||||
}
|
||||
|
||||
|
||||
type S1 struct {
|
||||
A int
|
||||
B string
|
||||
}
|
||||
|
||||
type Marshaller struct {
|
||||
v string
|
||||
}
|
||||
|
||||
func (m *Marshaller) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.v)
|
||||
}
|
||||
|
||||
func (m *Marshaller) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &m.v)
|
||||
}
|
||||
|
||||
type TextMarshaller struct {
|
||||
v int
|
||||
}
|
||||
|
||||
func (k *TextMarshaller) MarshalText() ([]byte, error) {
|
||||
return json.Marshal(k.v)
|
||||
}
|
||||
|
||||
func (k *TextMarshaller) UnmarshalText(data []byte) error {
|
||||
return json.Unmarshal(data, &k.v)
|
||||
}
|
||||
|
||||
var debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
go func () {
|
||||
if !debugAsyncGC {
|
||||
return
|
||||
}
|
||||
println("Begin GC looping...")
|
||||
for {
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
}
|
||||
}()
|
||||
time.Sleep(time.Millisecond)
|
||||
m.Run()
|
||||
}
|
||||
20
fuzz/go.mod
Normal file
20
fuzz/go.mod
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module github.com/bytedance/sonic/fuzz
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.0.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace github.com/bytedance/sonic => ../.
|
||||
40
fuzz/go.sum
Normal file
40
fuzz/go.sum
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/goccy/go-json v0.9.4 h1:L8MLKG2mvVXiQu07qB6hmfqeSYQdOnqPot2GhsIwIaI=
|
||||
github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
|
||||
github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
47
fuzz/other_fuzz_test.go
Normal file
47
fuzz/other_fuzz_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// +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 (
|
||||
`bytes`
|
||||
`testing`
|
||||
`encoding/json`
|
||||
`unicode/utf8`
|
||||
|
||||
`github.com/bytedance/sonic/encoder`
|
||||
`github.com/stretchr/testify/require`
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
)
|
||||
|
||||
func fuzzValidate(t *testing.T, data []byte){
|
||||
jok1 := json.Valid(data)
|
||||
jok2 := utf8.Valid(data)
|
||||
jok := jok1 && jok2
|
||||
sok, _ := encoder.Valid(data)
|
||||
spew.Dump(data, jok1, jok2, sok)
|
||||
require.Equalf(t, jok, sok, "different validate results")
|
||||
}
|
||||
|
||||
func fuzzHtmlEscape(t *testing.T, data []byte){
|
||||
var jdst bytes.Buffer
|
||||
var sdst []byte
|
||||
json.HTMLEscape(&jdst, data)
|
||||
sdst = encoder.HTMLEscape(sdst, data)
|
||||
require.Equalf(t, string(jdst.Bytes()), string(sdst), "different htmlescape results")
|
||||
}
|
||||
165
fuzz/struct_fuzz_test.go
Normal file
165
fuzz/struct_fuzz_test.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
// +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
|
||||
}
|
||||
1
go.work
1
go.work
|
|
@ -3,4 +3,5 @@ go 1.18
|
|||
use (
|
||||
.
|
||||
./generic_test
|
||||
./fuzz
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue