1
16
17 package json
18
19 import (
20 gojson "encoding/json"
21 "strings"
22 "testing"
23
24 utiljson "k8s.io/apimachinery/pkg/util/json"
25 )
26
27 type testcase struct {
28 name string
29 data []byte
30 checkErr func(t testing.TB, err error)
31
32 benchmark bool
33 }
34
35 func testcases() []testcase {
36
37 nonNilError := func(t testing.TB, err error) {
38 if err == nil {
39 t.Errorf("expected error, got none")
40 }
41 }
42
43 successOrMaxDepthError := func(t testing.TB, err error) {
44 if err != nil && !strings.Contains(err.Error(), "max depth") {
45 t.Errorf("expected success or error containing 'max depth', got: %v", err)
46 }
47 }
48
49 return []testcase{
50 {
51 name: "3MB of deeply nested slices",
52 checkErr: successOrMaxDepthError,
53 data: []byte(`{"a":` + strings.Repeat(`[`, 3*1024*1024/2) + strings.Repeat(`]`, 3*1024*1024/2) + "}"),
54 },
55 {
56 name: "3MB of unbalanced nested slices",
57 checkErr: nonNilError,
58 data: []byte(`{"a":` + strings.Repeat(`[`, 3*1024*1024)),
59 },
60 {
61 name: "3MB of deeply nested maps",
62 checkErr: successOrMaxDepthError,
63 data: []byte(strings.Repeat(`{"":`, 3*1024*1024/5/2) + "{}" + strings.Repeat(`}`, 3*1024*1024/5/2)),
64 },
65 {
66 name: "3MB of unbalanced nested maps",
67 checkErr: nonNilError,
68 data: []byte(strings.Repeat(`{"":`, 3*1024*1024/5)),
69 },
70 {
71 name: "3MB of empty slices",
72 data: []byte(`{"a":[` + strings.Repeat(`[],`, 3*1024*1024/3-2) + `[]]}`),
73 benchmark: true,
74 },
75 {
76 name: "3MB of slices",
77 data: []byte(`{"a":[` + strings.Repeat(`[0],`, 3*1024*1024/4-2) + `[0]]}`),
78 benchmark: true,
79 },
80 {
81 name: "3MB of empty maps",
82 data: []byte(`{"a":[` + strings.Repeat(`{},`, 3*1024*1024/3-2) + `{}]}`),
83 benchmark: true,
84 },
85 {
86 name: "3MB of maps",
87 data: []byte(`{"a":[` + strings.Repeat(`{"a":0},`, 3*1024*1024/8-2) + `{"a":0}]}`),
88 benchmark: true,
89 },
90 {
91 name: "3MB of ints",
92 data: []byte(`{"a":[` + strings.Repeat(`0,`, 3*1024*1024/2-2) + `0]}`),
93 benchmark: true,
94 },
95 {
96 name: "3MB of floats",
97 data: []byte(`{"a":[` + strings.Repeat(`0.0,`, 3*1024*1024/4-2) + `0.0]}`),
98 benchmark: true,
99 },
100 {
101 name: "3MB of bools",
102 data: []byte(`{"a":[` + strings.Repeat(`true,`, 3*1024*1024/5-2) + `true]}`),
103 benchmark: true,
104 },
105 {
106 name: "3MB of empty strings",
107 data: []byte(`{"a":[` + strings.Repeat(`"",`, 3*1024*1024/3-2) + `""]}`),
108 benchmark: true,
109 },
110 {
111 name: "3MB of strings",
112 data: []byte(`{"a":[` + strings.Repeat(`"abcdefghijklmnopqrstuvwxyz012",`, 3*1024*1024/30-2) + `"abcdefghijklmnopqrstuvwxyz012"]}`),
113 benchmark: true,
114 },
115 {
116 name: "3MB of nulls",
117 data: []byte(`{"a":[` + strings.Repeat(`null,`, 3*1024*1024/5-2) + `null]}`),
118 benchmark: true,
119 },
120 }
121 }
122
123 var decoders = map[string]func([]byte, interface{}) error{
124 "gojson": gojson.Unmarshal,
125 "utiljson": utiljson.Unmarshal,
126 }
127
128 func TestJSONLimits(t *testing.T) {
129 for _, tc := range testcases() {
130 if tc.benchmark {
131 continue
132 }
133 t.Run(tc.name, func(t *testing.T) {
134 for decoderName, decoder := range decoders {
135 t.Run(decoderName, func(t *testing.T) {
136 v := map[string]interface{}{}
137 err := decoder(tc.data, &v)
138
139 if tc.checkErr != nil {
140 tc.checkErr(t, err)
141 } else if err != nil {
142 t.Errorf("unexpected error: %v", err)
143 }
144 })
145 }
146 })
147 }
148 }
149
150 func BenchmarkJSONLimits(b *testing.B) {
151 for _, tc := range testcases() {
152 b.Run(tc.name, func(b *testing.B) {
153 for decoderName, decoder := range decoders {
154 b.Run(decoderName, func(b *testing.B) {
155 for i := 0; i < b.N; i++ {
156 v := map[string]interface{}{}
157 err := decoder(tc.data, &v)
158
159 if tc.checkErr != nil {
160 tc.checkErr(b, err)
161 } else if err != nil {
162 b.Errorf("unexpected error: %v", err)
163 }
164 }
165 })
166 }
167 })
168 }
169 }
170
View as plain text