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