1
2
3
4
5 package json
6
7 import (
8 "bytes"
9 "math"
10 "math/rand"
11 "reflect"
12 "strings"
13 "testing"
14 )
15
16 func indentNewlines(s string) string {
17 return strings.Join(strings.Split(s, "\n"), "\n\t")
18 }
19
20 func stripWhitespace(s string) string {
21 return strings.Map(func(r rune) rune {
22 if r == ' ' || r == '\n' || r == '\r' || r == '\t' {
23 return -1
24 }
25 return r
26 }, s)
27 }
28
29 func TestValid(t *testing.T) {
30 tests := []struct {
31 CaseName
32 data string
33 ok bool
34 }{
35 {Name(""), `foo`, false},
36 {Name(""), `}{`, false},
37 {Name(""), `{]`, false},
38 {Name(""), `{}`, true},
39 {Name(""), `{"foo":"bar"}`, true},
40 {Name(""), `{"foo":"bar","bar":{"baz":["qux"]}}`, true},
41 }
42 for _, tt := range tests {
43 t.Run(tt.Name, func(t *testing.T) {
44 if ok := Valid([]byte(tt.data)); ok != tt.ok {
45 t.Errorf("%s: Valid(`%s`) = %v, want %v", tt.Where, tt.data, ok, tt.ok)
46 }
47 })
48 }
49 }
50
51 func TestCompactAndIndent(t *testing.T) {
52 tests := []struct {
53 CaseName
54 compact string
55 indent string
56 }{
57 {Name(""), `1`, `1`},
58 {Name(""), `{}`, `{}`},
59 {Name(""), `[]`, `[]`},
60 {Name(""), `{"":2}`, "{\n\t\"\": 2\n}"},
61 {Name(""), `[3]`, "[\n\t3\n]"},
62 {Name(""), `[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
63 {Name(""), `{"x":1}`, "{\n\t\"x\": 1\n}"},
64 {Name(""), `[true,false,null,"x",1,1.5,0,-5e+2]`, `[
65 true,
66 false,
67 null,
68 "x",
69 1,
70 1.5,
71 0,
72 -5e+2
73 ]`},
74 {Name(""), "{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"},
75 }
76 var buf bytes.Buffer
77 for _, tt := range tests {
78 t.Run(tt.Name, func(t *testing.T) {
79 buf.Reset()
80 if err := Compact(&buf, []byte(tt.compact)); err != nil {
81 t.Errorf("%s: Compact error: %v", tt.Where, err)
82 } else if got := buf.String(); got != tt.compact {
83 t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
84 }
85
86 buf.Reset()
87 if err := Compact(&buf, []byte(tt.indent)); err != nil {
88 t.Errorf("%s: Compact error: %v", tt.Where, err)
89 } else if got := buf.String(); got != tt.compact {
90 t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
91 }
92
93 buf.Reset()
94 if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
95 t.Errorf("%s: Indent error: %v", tt.Where, err)
96 } else if got := buf.String(); got != tt.indent {
97 t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
98 }
99
100 buf.Reset()
101 if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
102 t.Errorf("%s: Indent error: %v", tt.Where, err)
103 } else if got := buf.String(); got != tt.indent {
104 t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
105 }
106 })
107 }
108 }
109
110 func TestCompactSeparators(t *testing.T) {
111
112
113 tests := []struct {
114 CaseName
115 in, compact string
116 }{
117 {Name(""), "{\"\u2028\": 1}", "{\"\u2028\":1}"},
118 {Name(""), "{\"\u2029\" :2}", "{\"\u2029\":2}"},
119 }
120 for _, tt := range tests {
121 t.Run(tt.Name, func(t *testing.T) {
122 var buf bytes.Buffer
123 if err := Compact(&buf, []byte(tt.in)); err != nil {
124 t.Errorf("%s: Compact error: %v", tt.Where, err)
125 } else if got := buf.String(); got != tt.compact {
126 t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
127 }
128 })
129 }
130 }
131
132
133
134 func TestCompactBig(t *testing.T) {
135 initBig()
136 var buf bytes.Buffer
137 if err := Compact(&buf, jsonBig); err != nil {
138 t.Fatalf("Compact error: %v", err)
139 }
140 b := buf.Bytes()
141 if !bytes.Equal(b, jsonBig) {
142 t.Error("Compact:")
143 diff(t, b, jsonBig)
144 return
145 }
146 }
147
148 func TestIndentBig(t *testing.T) {
149 t.Parallel()
150 initBig()
151 var buf bytes.Buffer
152 if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
153 t.Fatalf("Indent error: %v", err)
154 }
155 b := buf.Bytes()
156 if len(b) == len(jsonBig) {
157
158
159 t.Fatalf("Indent did not expand the input")
160 }
161
162
163 var buf1 bytes.Buffer
164 if err := Indent(&buf1, b, "", "\t"); err != nil {
165 t.Fatalf("Indent error: %v", err)
166 }
167 b1 := buf1.Bytes()
168 if !bytes.Equal(b1, b) {
169 t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig):")
170 diff(t, b1, b)
171 return
172 }
173
174
175 buf1.Reset()
176 if err := Compact(&buf1, b); err != nil {
177 t.Fatalf("Compact error: %v", err)
178 }
179 b1 = buf1.Bytes()
180 if !bytes.Equal(b1, jsonBig) {
181 t.Error("Compact(Indent(jsonBig)) != jsonBig:")
182 diff(t, b1, jsonBig)
183 return
184 }
185 }
186
187 func TestIndentErrors(t *testing.T) {
188 tests := []struct {
189 CaseName
190 in string
191 err error
192 }{
193 {Name(""), `{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
194 {Name(""), `{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
195 }
196 for _, tt := range tests {
197 t.Run(tt.Name, func(t *testing.T) {
198 slice := make([]uint8, 0)
199 buf := bytes.NewBuffer(slice)
200 if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
201 if !reflect.DeepEqual(err, tt.err) {
202 t.Fatalf("%s: Indent error:\n\tgot: %v\n\twant: %v", tt.Where, err, tt.err)
203 }
204 }
205 })
206 }
207 }
208
209 func diff(t *testing.T, a, b []byte) {
210 t.Helper()
211 for i := 0; ; i++ {
212 if i >= len(a) || i >= len(b) || a[i] != b[i] {
213 j := i - 10
214 if j < 0 {
215 j = 0
216 }
217 t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
218 return
219 }
220 }
221 }
222
223 func trim(b []byte) []byte {
224 return b[:min(len(b), 20)]
225 }
226
227
228
229 var jsonBig []byte
230
231 func initBig() {
232 n := 10000
233 if testing.Short() {
234 n = 100
235 }
236 b, err := Marshal(genValue(n))
237 if err != nil {
238 panic(err)
239 }
240 jsonBig = b
241 }
242
243 func genValue(n int) any {
244 if n > 1 {
245 switch rand.Intn(2) {
246 case 0:
247 return genArray(n)
248 case 1:
249 return genMap(n)
250 }
251 }
252 switch rand.Intn(3) {
253 case 0:
254 return rand.Intn(2) == 0
255 case 1:
256 return rand.NormFloat64()
257 case 2:
258 return genString(30)
259 }
260 panic("unreachable")
261 }
262
263 func genString(stddev float64) string {
264 n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
265 c := make([]rune, n)
266 for i := range c {
267 f := math.Abs(rand.NormFloat64()*64 + 32)
268 if f > 0x10ffff {
269 f = 0x10ffff
270 }
271 c[i] = rune(f)
272 }
273 return string(c)
274 }
275
276 func genArray(n int) []any {
277 f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
278 if f > n {
279 f = n
280 }
281 if f < 1 {
282 f = 1
283 }
284 x := make([]any, f)
285 for i := range x {
286 x[i] = genValue(((i+1)*n)/f - (i*n)/f)
287 }
288 return x
289 }
290
291 func genMap(n int) map[string]any {
292 f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
293 if f > n {
294 f = n
295 }
296 if n > 0 && f == 0 {
297 f = 1
298 }
299 x := make(map[string]any)
300 for i := 0; i < f; i++ {
301 x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
302 }
303 return x
304 }
305
View as plain text