1package jlexer
2
3import (
4	"bytes"
5	"encoding/json"
6	"reflect"
7	"testing"
8)
9
10func TestString(t *testing.T) {
11	for i, test := range []struct {
12		toParse   string
13		want      string
14		wantError bool
15	}{
16		{toParse: `"simple string"`, want: "simple string"},
17		{toParse: " \r\r\n\t  " + `"test"`, want: "test"},
18		{toParse: `"\n\t\"\/\\\f\r"`, want: "\n\t\"/\\\f\r"},
19		{toParse: `"\u0020"`, want: " "},
20		{toParse: `"\u0020-\t"`, want: " -\t"},
21		{toParse: `"\ufffd\uFFFD"`, want: "\ufffd\ufffd"},
22		{toParse: `"\ud83d\ude00"`, want: "��"},
23		{toParse: `"\ud83d\ude08"`, want: "��"},
24		{toParse: `"\ud8"`, wantError: true},
25
26		{toParse: `"test"junk`, want: "test"},
27
28		{toParse: `5`, wantError: true},    // not a string
29		{toParse: `"\x"`, wantError: true}, // invalid escape
30		{toParse: `"\ud800"`, want: "�"},   // invalid utf-8 char; return replacement char
31	} {
32		{
33			l := Lexer{Data: []byte(test.toParse)}
34
35			got := l.String()
36			if got != test.want {
37				t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want)
38			}
39			err := l.Error()
40			if err != nil && !test.wantError {
41				t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err)
42			} else if err == nil && test.wantError {
43				t.Errorf("[%d, %q] String() ok; want error", i, test.toParse)
44			}
45		}
46		{
47			l := Lexer{Data: []byte(test.toParse)}
48
49			got := l.StringIntern()
50			if got != test.want {
51				t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want)
52			}
53			err := l.Error()
54			if err != nil && !test.wantError {
55				t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err)
56			} else if err == nil && test.wantError {
57				t.Errorf("[%d, %q] String() ok; want error", i, test.toParse)
58			}
59		}
60	}
61}
62
63func TestStringIntern(t *testing.T) {
64	data := []byte(`"string interning test"`)
65	var l Lexer
66
67	allocsPerRun := testing.AllocsPerRun(1000, func() {
68		l = Lexer{Data: data}
69		_ = l.StringIntern()
70	})
71	if allocsPerRun != 0 {
72		t.Fatalf("expected 0 allocs, got %f", allocsPerRun)
73	}
74
75	allocsPerRun = testing.AllocsPerRun(1000, func() {
76		l = Lexer{Data: data}
77		_ = l.String()
78	})
79	if allocsPerRun != 1 {
80		t.Fatalf("expected 1 allocs, got %f", allocsPerRun)
81	}
82}
83
84func TestBytes(t *testing.T) {
85	for i, test := range []struct {
86		toParse   string
87		want      string
88		wantError bool
89	}{
90		{toParse: `"c2ltcGxlIHN0cmluZw=="`, want: "simple string"},
91		{toParse: " \r\r\n\t  " + `"dGVzdA=="`, want: "test"},
92
93		{toParse: `5`, wantError: true},                     // not a JSON string
94		{toParse: `"foobar"`, wantError: true},              // not base64 encoded
95		{toParse: `"c2ltcGxlIHN0cmluZw="`, wantError: true}, // invalid base64 padding
96	} {
97		l := Lexer{Data: []byte(test.toParse)}
98
99		got := l.Bytes()
100		if bytes.Compare(got, []byte(test.want)) != 0 {
101			t.Errorf("[%d, %q] Bytes() = %v; want: %v", i, test.toParse, got, []byte(test.want))
102		}
103		err := l.Error()
104		if err != nil && !test.wantError {
105			t.Errorf("[%d, %q] Bytes() error: %v", i, test.toParse, err)
106		} else if err == nil && test.wantError {
107			t.Errorf("[%d, %q] Bytes() ok; want error", i, test.toParse)
108		}
109	}
110}
111
112func TestNumber(t *testing.T) {
113	for i, test := range []struct {
114		toParse   string
115		want      string
116		wantError bool
117	}{
118		{toParse: "123", want: "123"},
119		{toParse: "-123", want: "-123"},
120		{toParse: "\r\n12.35", want: "12.35"},
121		{toParse: "12.35e+1", want: "12.35e+1"},
122		{toParse: "12.35e-15", want: "12.35e-15"},
123		{toParse: "12.35E-15", want: "12.35E-15"},
124		{toParse: "12.35E15", want: "12.35E15"},
125
126		{toParse: `"a"`, wantError: true},
127		{toParse: "123junk", wantError: true},
128		{toParse: "1.2.3", wantError: true},
129		{toParse: "1e2e3", wantError: true},
130		{toParse: "1e2.3", wantError: true},
131	} {
132		l := Lexer{Data: []byte(test.toParse)}
133
134		got := l.number()
135		if got != test.want {
136			t.Errorf("[%d, %q] number() = %v; want %v", i, test.toParse, got, test.want)
137		}
138		err := l.Error()
139		if err != nil && !test.wantError {
140			t.Errorf("[%d, %q] number() error: %v", i, test.toParse, err)
141		} else if err == nil && test.wantError {
142			t.Errorf("[%d, %q] number() ok; want error", i, test.toParse)
143		}
144	}
145}
146
147func TestBool(t *testing.T) {
148	for i, test := range []struct {
149		toParse   string
150		want      bool
151		wantError bool
152	}{
153		{toParse: "true", want: true},
154		{toParse: "false", want: false},
155
156		{toParse: "1", wantError: true},
157		{toParse: "truejunk", wantError: true},
158		{toParse: `false"junk"`, wantError: true},
159		{toParse: "True", wantError: true},
160		{toParse: "False", wantError: true},
161	} {
162		l := Lexer{Data: []byte(test.toParse)}
163
164		got := l.Bool()
165		if got != test.want {
166			t.Errorf("[%d, %q] Bool() = %v; want %v", i, test.toParse, got, test.want)
167		}
168		err := l.Error()
169		if err != nil && !test.wantError {
170			t.Errorf("[%d, %q] Bool() error: %v", i, test.toParse, err)
171		} else if err == nil && test.wantError {
172			t.Errorf("[%d, %q] Bool() ok; want error", i, test.toParse)
173		}
174	}
175}
176
177func TestSkipRecursive(t *testing.T) {
178	for i, test := range []struct {
179		toParse   string
180		left      string
181		wantError bool
182	}{
183		{toParse: "5, 4", left: ", 4"},
184		{toParse: "[5, 6], 4", left: ", 4"},
185		{toParse: "[5, [7,8]]: 4", left: ": 4"},
186
187		{toParse: `{"a":1}, 4`, left: ", 4"},
188		{toParse: `{"a":1, "b":{"c": 5}, "e":[12,15]}, 4`, left: ", 4"},
189
190		// array start/end chars in a string
191		{toParse: `[5, "]"], 4`, left: ", 4"},
192		{toParse: `[5, "\"]"], 4`, left: ", 4"},
193		{toParse: `[5, "["], 4`, left: ", 4"},
194		{toParse: `[5, "\"["], 4`, left: ", 4"},
195
196		// object start/end chars in a string
197		{toParse: `{"a}":1}, 4`, left: ", 4"},
198		{toParse: `{"a\"}":1}, 4`, left: ", 4"},
199		{toParse: `{"a{":1}, 4`, left: ", 4"},
200		{toParse: `{"a\"{":1}, 4`, left: ", 4"},
201
202		// object with double slashes at the end of string
203		{toParse: `{"a":"hey\\"}, 4`, left: ", 4"},
204	} {
205		l := Lexer{Data: []byte(test.toParse)}
206
207		l.SkipRecursive()
208
209		got := string(l.Data[l.pos:])
210		if got != test.left {
211			t.Errorf("[%d, %q] SkipRecursive() left = %v; want %v", i, test.toParse, got, test.left)
212		}
213		err := l.Error()
214		if err != nil && !test.wantError {
215			t.Errorf("[%d, %q] SkipRecursive() error: %v", i, test.toParse, err)
216		} else if err == nil && test.wantError {
217			t.Errorf("[%d, %q] SkipRecursive() ok; want error", i, test.toParse)
218		}
219	}
220}
221
222func TestInterface(t *testing.T) {
223	for i, test := range []struct {
224		toParse   string
225		want      interface{}
226		wantError bool
227	}{
228		{toParse: "null", want: nil},
229		{toParse: "true", want: true},
230		{toParse: `"a"`, want: "a"},
231		{toParse: "5", want: float64(5)},
232
233		{toParse: `{}`, want: map[string]interface{}{}},
234		{toParse: `[]`, want: []interface{}{}},
235
236		{toParse: `{"a": "b"}`, want: map[string]interface{}{"a": "b"}},
237		{toParse: `[5]`, want: []interface{}{float64(5)}},
238
239		{toParse: `{"a":5 , "b" : "string"}`, want: map[string]interface{}{"a": float64(5), "b": "string"}},
240		{toParse: `["a", 5 , null, true]`, want: []interface{}{"a", float64(5), nil, true}},
241
242		{toParse: `{"a" "b"}`, wantError: true},
243		{toParse: `{"a": "b",}`, wantError: true},
244		{toParse: `{"a":"b","c" "b"}`, wantError: true},
245		{toParse: `{"a": "b","c":"d",}`, wantError: true},
246		{toParse: `{,}`, wantError: true},
247
248		{toParse: `[1, 2,]`, wantError: true},
249		{toParse: `[1  2]`, wantError: true},
250		{toParse: `[,]`, wantError: true},
251	} {
252		l := Lexer{Data: []byte(test.toParse)}
253
254		got := l.Interface()
255		if !reflect.DeepEqual(got, test.want) {
256			t.Errorf("[%d, %q] Interface() = %v; want %v", i, test.toParse, got, test.want)
257		}
258		err := l.Error()
259		if err != nil && !test.wantError {
260			t.Errorf("[%d, %q] Interface() error: %v", i, test.toParse, err)
261		} else if err == nil && test.wantError {
262			t.Errorf("[%d, %q] Interface() ok; want error", i, test.toParse)
263		}
264	}
265}
266
267func TestConsumed(t *testing.T) {
268	for i, test := range []struct {
269		toParse   string
270		wantError bool
271	}{
272		{toParse: "", wantError: false},
273		{toParse: "   ", wantError: false},
274		{toParse: "\r\n", wantError: false},
275		{toParse: "\t\t", wantError: false},
276
277		{toParse: "{", wantError: true},
278	} {
279		l := Lexer{Data: []byte(test.toParse)}
280		l.Consumed()
281
282		err := l.Error()
283		if err != nil && !test.wantError {
284			t.Errorf("[%d, %q] Consumed() error: %v", i, test.toParse, err)
285		} else if err == nil && test.wantError {
286			t.Errorf("[%d, %q] Consumed() ok; want error", i, test.toParse)
287		}
288	}
289}
290
291func TestJsonNumber(t *testing.T) {
292	for i, test := range []struct {
293		toParse        string
294		want           json.Number
295		wantLexerError bool
296		wantValue      interface{}
297		wantValueError bool
298	}{
299		{toParse: `10`, want: json.Number("10"), wantValue: int64(10)},
300		{toParse: `0`, want: json.Number("0"), wantValue: int64(0)},
301		{toParse: `0.12`, want: json.Number("0.12"), wantValue: 0.12},
302		{toParse: `25E-4`, want: json.Number("25E-4"), wantValue: 25e-4},
303
304		{toParse: `"10"`, want: json.Number("10"), wantValue: int64(10)},
305		{toParse: `"0"`, want: json.Number("0"), wantValue: int64(0)},
306		{toParse: `"0.12"`, want: json.Number("0.12"), wantValue: 0.12},
307		{toParse: `"25E-4"`, want: json.Number("25E-4"), wantValue: 25e-4},
308
309		{toParse: `"foo"`, want: json.Number("foo"), wantValueError: true},
310		{toParse: `null`, want: json.Number(""), wantValueError: true},
311
312		{toParse: `"a""`, want: json.Number("a"), wantValueError: true},
313
314		{toParse: `[1]`, want: json.Number(""), wantLexerError: true, wantValueError: true},
315		{toParse: `{}`, want: json.Number(""), wantLexerError: true, wantValueError: true},
316		{toParse: `a`, want: json.Number(""), wantLexerError: true, wantValueError: true},
317	} {
318		l := Lexer{Data: []byte(test.toParse)}
319
320		got := l.JsonNumber()
321		if got != test.want {
322			t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want)
323		}
324
325		err := l.Error()
326		if err != nil && !test.wantLexerError {
327			t.Errorf("[%d, %q] JsonNumber() lexer error: %v", i, test.toParse, err)
328		} else if err == nil && test.wantLexerError {
329			t.Errorf("[%d, %q] JsonNumber() ok; want lexer error", i, test.toParse)
330		}
331
332		var valueErr error
333		var gotValue interface{}
334		switch test.wantValue.(type) {
335		case float64:
336			gotValue, valueErr = got.Float64()
337		default:
338			gotValue, valueErr = got.Int64()
339		}
340
341		if !reflect.DeepEqual(gotValue, test.wantValue) && !test.wantLexerError && !test.wantValueError {
342			t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, gotValue, test.wantValue)
343		}
344
345		if valueErr != nil && !test.wantValueError {
346			t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, valueErr)
347		} else if valueErr == nil && test.wantValueError {
348			t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse)
349		}
350	}
351}
352
353func TestFetchStringUnterminatedString(t *testing.T) {
354	for _, test := range []struct {
355		data []byte
356	}{
357		{data: []byte(`"sting without trailing quote`)},
358		{data: []byte(`"\"`)},
359		{data: []byte{'"'}},
360	} {
361		l := Lexer{Data: test.data}
362		l.fetchString()
363		if l.pos > len(l.Data) {
364			t.Errorf("fetchString(%s): pos=%v should not be greater than length of Data = %v", test.data, l.pos, len(l.Data))
365		}
366		if l.Error() == nil {
367			t.Errorf("fetchString(%s): should add parsing error", test.data)
368		}
369	}
370}
371