1// Copyright (C) MongoDB, Inc. 2014-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6//
7// Based on github.com/golang/go by The Go Authors
8// See THIRD-PARTY-NOTICES for original license terms.
9
10package json
11
12import (
13	"bytes"
14	"math"
15	"math/rand"
16	"reflect"
17	"testing"
18)
19
20// Tests of simple examples.
21
22type example struct {
23	compact string
24	indent  string
25}
26
27var examples = []example{
28	{`1`, `1`},
29	{`{}`, `{}`},
30	{`[]`, `[]`},
31	{`{"":2}`, "{\n\t\"\": 2\n}"},
32	{`[3]`, "[\n\t3\n]"},
33	{`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
34	{`{"x":1}`, "{\n\t\"x\": 1\n}"},
35	{ex1, ex1i},
36}
37
38var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
39
40var ex1i = `[
41	true,
42	false,
43	null,
44	"x",
45	1,
46	1.5,
47	0,
48	-5e+2
49]`
50
51func TestCompact(t *testing.T) {
52	var buf bytes.Buffer
53	for _, tt := range examples {
54		buf.Reset()
55		if err := Compact(&buf, []byte(tt.compact)); err != nil {
56			t.Errorf("Compact(%#q): %v", tt.compact, err)
57		} else if s := buf.String(); s != tt.compact {
58			t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
59		}
60
61		buf.Reset()
62		if err := Compact(&buf, []byte(tt.indent)); err != nil {
63			t.Errorf("Compact(%#q): %v", tt.indent, err)
64			continue
65		} else if s := buf.String(); s != tt.compact {
66			t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
67		}
68	}
69}
70
71func TestCompactSeparators(t *testing.T) {
72	// U+2028 and U+2029 should be escaped inside strings.
73	// They should not appear outside strings.
74	tests := []struct {
75		in, compact string
76	}{
77		{"{\"\u2028\": 1}", `{"\u2028":1}`},
78		{"{\"\u2029\" :2}", `{"\u2029":2}`},
79	}
80	for _, tt := range tests {
81		var buf bytes.Buffer
82		if err := Compact(&buf, []byte(tt.in)); err != nil {
83			t.Errorf("Compact(%q): %v", tt.in, err)
84		} else if s := buf.String(); s != tt.compact {
85			t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
86		}
87	}
88}
89
90func TestIndent(t *testing.T) {
91	var buf bytes.Buffer
92	for _, tt := range examples {
93		buf.Reset()
94		if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
95			t.Errorf("Indent(%#q): %v", tt.indent, err)
96		} else if s := buf.String(); s != tt.indent {
97			t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
98		}
99
100		buf.Reset()
101		if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
102			t.Errorf("Indent(%#q): %v", tt.compact, err)
103			continue
104		} else if s := buf.String(); s != tt.indent {
105			t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
106		}
107	}
108}
109
110// Tests of a large random structure.
111
112func TestCompactBig(t *testing.T) {
113	initBig()
114	var buf bytes.Buffer
115	if err := Compact(&buf, jsonBig); err != nil {
116		t.Fatalf("Compact: %v", err)
117	}
118	b := buf.Bytes()
119	if !bytes.Equal(b, jsonBig) {
120		t.Error("Compact(jsonBig) != jsonBig")
121		diff(t, b, jsonBig)
122		return
123	}
124}
125
126func TestIndentBig(t *testing.T) {
127	initBig()
128	var buf bytes.Buffer
129	if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
130		t.Fatalf("Indent1: %v", err)
131	}
132	b := buf.Bytes()
133	if len(b) == len(jsonBig) {
134		// jsonBig is compact (no unnecessary spaces);
135		// indenting should make it bigger
136		t.Fatalf("Indent(jsonBig) did not get bigger")
137	}
138
139	// should be idempotent
140	var buf1 bytes.Buffer
141	if err := Indent(&buf1, b, "", "\t"); err != nil {
142		t.Fatalf("Indent2: %v", err)
143	}
144	b1 := buf1.Bytes()
145	if !bytes.Equal(b1, b) {
146		t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
147		diff(t, b1, b)
148		return
149	}
150
151	// should get back to original
152	buf1.Reset()
153	if err := Compact(&buf1, b); err != nil {
154		t.Fatalf("Compact: %v", err)
155	}
156	b1 = buf1.Bytes()
157	if !bytes.Equal(b1, jsonBig) {
158		t.Error("Compact(Indent(jsonBig)) != jsonBig")
159		diff(t, b1, jsonBig)
160		return
161	}
162}
163
164type indentErrorTest struct {
165	in  string
166	err error
167}
168
169var indentErrorTests = []indentErrorTest{
170	{`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
171	{`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
172}
173
174func TestIndentErrors(t *testing.T) {
175	for i, tt := range indentErrorTests {
176		var slice []uint8
177		buf := bytes.NewBuffer(slice)
178		if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
179			if !reflect.DeepEqual(err, tt.err) {
180				t.Errorf("#%d: Indent: %#v", i, err)
181				continue
182			}
183		}
184	}
185}
186
187func TestNextValueBig(t *testing.T) {
188	initBig()
189	var scan scanner
190	item, rest, err := nextValue(jsonBig, &scan)
191	if err != nil {
192		t.Fatalf("nextValue: %s", err)
193	}
194	if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] {
195		t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
196	}
197	if len(rest) != 0 {
198		t.Errorf("invalid rest: %d", len(rest))
199	}
200
201	item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan)
202	if err != nil {
203		t.Fatalf("nextValue extra: %s", err)
204	}
205	if len(item) != len(jsonBig) {
206		t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
207	}
208	if string(rest) != "HELLO WORLD" {
209		t.Errorf("invalid rest: %d", len(rest))
210	}
211}
212
213var benchScan scanner
214
215func BenchmarkSkipValue(b *testing.B) {
216	initBig()
217	for i := 0; i < b.N; i++ {
218		nextValue(jsonBig, &benchScan)
219	}
220	b.SetBytes(int64(len(jsonBig)))
221}
222
223func diff(t *testing.T, a, b []byte) {
224	for i := 0; ; i++ {
225		if i >= len(a) || i >= len(b) || a[i] != b[i] {
226			j := i - 10
227			if j < 0 {
228				j = 0
229			}
230			t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
231			return
232		}
233	}
234}
235
236func trim(b []byte) []byte {
237	if len(b) > 20 {
238		return b[0:20]
239	}
240	return b
241}
242
243// Generate a random JSON object.
244
245var jsonBig []byte
246
247func initBig() {
248	n := 10000
249	if testing.Short() {
250		n = 100
251	}
252	b, err := Marshal(genValue(n))
253	if err != nil {
254		panic(err)
255	}
256	jsonBig = b
257}
258
259func genValue(n int) interface{} {
260	if n > 1 {
261		switch rand.Intn(2) {
262		case 0:
263			return genArray(n)
264		case 1:
265			return genMap(n)
266		}
267	}
268	switch rand.Intn(3) {
269	case 0:
270		return rand.Intn(2) == 0
271	case 1:
272		return rand.NormFloat64()
273	case 2:
274		return genString(30)
275	}
276	panic("unreachable")
277}
278
279func genString(stddev float64) string {
280	n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
281	c := make([]rune, n)
282	for i := range c {
283		f := math.Abs(rand.NormFloat64()*64 + 32)
284		if f > 0x10ffff {
285			f = 0x10ffff
286		}
287		c[i] = rune(f)
288	}
289	return string(c)
290}
291
292func genArray(n int) []interface{} {
293	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
294	if f > n {
295		f = n
296	}
297	if f < 1 {
298		f = 1
299	}
300	x := make([]interface{}, f)
301	for i := range x {
302		x[i] = genValue(((i+1)*n)/f - (i*n)/f)
303	}
304	return x
305}
306
307func genMap(n int) map[string]interface{} {
308	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
309	if f > n {
310		f = n
311	}
312	if n > 0 && f == 0 {
313		f = 1
314	}
315	x := make(map[string]interface{})
316	for i := 0; i < f; i++ {
317		x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
318	}
319	return x
320}
321