1
16
17 package ast
18
19 import (
20 "math"
21 "runtime"
22 "strconv"
23 "strings"
24 "sync"
25 "testing"
26
27 "github.com/stretchr/testify/assert"
28 )
29
30 func TestGC_Search(t *testing.T) {
31 if debugSyncGC {
32 return
33 }
34 _, err := NewSearcher(_TwitterJson).GetByPath("statuses", 0, "id")
35 if err != nil {
36 t.Fatal(err)
37 }
38 wg := &sync.WaitGroup{}
39
40
41 N := 5000
42 for i := 0; i < N; i++ {
43 wg.Add(1)
44 go func(wg *sync.WaitGroup) {
45 defer wg.Done()
46 _, err := NewSearcher(_TwitterJson).GetByPath("statuses", 0, "id")
47 if err != nil {
48 t.Error(err)
49 return
50 }
51 runtime.GC()
52 }(wg)
53 }
54 wg.Wait()
55 }
56
57 func TestExportErrorInvalidChar(t *testing.T) {
58 data := `{"a":]`
59 p := NewSearcher(data)
60 _, err := p.GetByPath("a")
61 if err == nil {
62 t.Fatal()
63 }
64 if strings.Index(err.Error(), `"Syntax error at `) != 0 {
65 t.Fatal(err)
66 }
67
68 data = `:"b"]`
69 p = NewSearcher(data)
70 _, err = p.GetByPath("a")
71 if err == nil {
72 t.Fatal()
73 }
74 if err.Error() != `"Syntax error at index 0: invalid char\n\n\t:\"b\"]\n\t^....\n"` {
75 t.Fatal(err)
76 }
77
78 data = `{:"b"]`
79 p = NewSearcher(data)
80 _, err = p.GetByPath("a")
81 if err == nil {
82 t.Fatal()
83 }
84 if err.Error() != `"Syntax error at index 1: invalid char\n\n\t{:\"b\"]\n\t.^....\n"` {
85 t.Fatal(err)
86 }
87
88 data = `{`
89 p = NewSearcher(data)
90 _, err = p.GetByPath("he")
91 if err == nil {
92 t.Fatal()
93 }
94 if err == ErrNotExist {
95 t.Fatal(err)
96 }
97
98 data = `[`
99 p = NewSearcher(data)
100 _, err = p.GetByPath(0)
101 if err == nil {
102 t.Fatal()
103 }
104 if err == ErrNotExist {
105 t.Fatal(err)
106 }
107 }
108
109 type testExportError struct {
110 data string
111 path []interface{}
112 err error
113 }
114
115 func TestExportErrNotExist(t *testing.T) {
116 tests := []testExportError{
117
118 {`{}`, []interface{}{"b"}, ErrNotExist},
119 {` { } `, []interface{}{"b"}, ErrNotExist},
120 {`{"a":null}`, []interface{}{"b"}, ErrNotExist},
121
122
123
124
125 {`{"":{"b":123}}`, []interface{}{"b"}, ErrNotExist},
126 {`{"":{"b":123}}`, []interface{}{"", ""}, ErrNotExist},
127 {`{"a":{"b":123}}`, []interface{}{"b"}, ErrNotExist},
128 {`{"a":{"b":123}}`, []interface{}{"a", "c"}, ErrNotExist},
129 {`{"a":{"c": null, "b":{}}}`, []interface{}{"a", "b", "c"}, ErrNotExist},
130 {`{"a":{"b":123}}`, []interface{}{"b", "b"}, ErrNotExist},
131 {`{"\"\\":{"b":123}}`, []interface{}{"\"", "b"}, ErrNotExist},
132 {`{"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\\":{"b":123}}`,
133 []interface{}{"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"", "b"}, ErrNotExist},
134
135
136 {`[]`, []interface{}{0}, ErrNotExist},
137 {`[]`, []interface{}{1}, ErrNotExist},
138 {` [ ] `, []interface{}{0}, ErrNotExist},
139 {`[null]`, []interface{}{1}, ErrNotExist},
140 {`[null, ["null", 123]]`, []interface{}{2}, ErrNotExist},
141 {`[null, true , false, 14, 2.35, -46, "hello7", "\"8"]`, []interface{}{8}, ErrNotExist},
142 {`[{}]`, []interface{}{1}, ErrNotExist},
143 {`[[]]`, []interface{}{1}, ErrNotExist},
144 {`[[],[{},{}, []],{}]`, []interface{}{3}, ErrNotExist},
145 }
146
147 for _, test := range tests {
148 f := func(t *testing.T) {
149 p := NewSearcher(test.data)
150 node, err := p.GetByPath(test.path...)
151 if err != test.err || node.Exists() {
152 t.Fatal(err)
153 }
154 }
155 t.Run(test.data, f)
156 }
157 }
158
159 func TestSearcher_GetByPath(t *testing.T) {
160 s := NewSearcher(` { "xx" : [] ,"yy" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ] } `)
161
162 node, e := s.GetByPath("test", 0)
163 a, _ := node.Bool()
164 if e != nil || a != true {
165 t.Fatalf("node: %v, err: %v", node, e)
166 }
167
168 node, e = s.GetByPath("test", 1)
169 b, _ := node.Float64()
170 if e != nil || b != 0.1 {
171 t.Fatalf("node: %v, err: %v", node, e)
172 }
173
174 node, e = s.GetByPath("test", 2)
175 c, _ := node.String()
176 if e != nil || c != "abc" {
177 t.Fatalf("node: %v, err: %v", node, e)
178 }
179
180 node, e = s.GetByPath("test", 3)
181 arr, _ := node.Array()
182 if e != nil || arr[0] != "h" {
183 t.Fatalf("node: %v, err: %v", node, e)
184 }
185
186 node, e = s.GetByPath("test", 4, "a")
187 d, _ := node.String()
188 if e != nil || d != "bc" {
189 t.Fatalf("node: %v, err: %v", node, e)
190 }
191 }
192
193 type testGetByPath struct {
194 json string
195 path []interface{}
196 value interface{}
197 ok bool
198 }
199
200 func TestSearcher_GetByPathSingle(t *testing.T) {
201 type Path = []interface{}
202 const Ok = true
203 const Error = false
204 tests := []testGetByPath{
205 {`true`, Path{}, true, Ok},
206 {`false`, Path{}, false, Ok},
207 {`null`, Path{}, nil, Ok},
208 {`12345`, Path{}, 12345.0, Ok},
209 {`12345.6789`, Path{}, 12345.6789, Ok},
210 {`"abc"`, Path{}, "abc", Ok},
211 {`"a\"\\bc"`, Path{}, "a\"\\bc", Ok},
212 {`{"a":1}`, Path{"a"}, 1.0, Ok},
213 {`{"":1}`, Path{""}, 1.0, Ok},
214 {`{"":{"":1}}`, Path{"", ""}, 1.0, Ok},
215 {`[1,2,3]`, Path{0}, 1.0, Ok},
216 {`[1,2,3]`, Path{1}, 2.0, Ok},
217 {`[1,2,3]`, Path{2}, 3.0, Ok},
218
219 {`tru`, Path{}, nil, Error},
220 {`fal`, Path{}, nil, Error},
221 {`nul`, Path{}, nil, Error},
222 {`{"a":1`, Path{}, nil, Error},
223 {`x12345.6789`, Path{}, nil, Error},
224 {`"abc`, Path{}, nil, Error},
225 {`"a\"\\bc`, Path{}, nil, Error},
226 {`"a\"\`, Path{}, nil, Error},
227 {`{"a":`, Path{"a"}, nil, Error},
228 {`[1,2,3]`, Path{4}, nil, Error},
229 {`[1,2,3]`, Path{"a"}, nil, Error},
230 }
231 for _, test := range tests {
232 t.Run(test.json, func(t *testing.T) {
233 s := NewSearcher(test.json)
234 node, err1 := s.GetByPath(test.path...)
235 assert.Equal(t, test.ok, err1 == nil)
236
237 value, err2 := node.Interface()
238 assert.Equal(t, test.value, value)
239 assert.Equal(t, test.ok, err2 == nil)
240 })
241 }
242 }
243
244 func TestSearcher_GetByPathErr(t *testing.T) {
245 s := NewSearcher(` { "xx" : [] ,"yy" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ], "err1":[a, ] , "err2":{ ,"x":"xx"} } `)
246 node, e := s.GetByPath("zz")
247 if e == nil {
248 t.Fatalf("node: %v, err: %v", node, e)
249 }
250 s.parser.p = 0
251 node, e = s.GetByPath("xx", 4)
252 if e == nil {
253 t.Fatalf("node: %v, err: %v", node, e)
254 }
255 s.parser.p = 0
256 node, e = s.GetByPath("yy", "a")
257 if e == nil {
258 t.Fatalf("node: %v, err: %v", node, e)
259 }
260 s.parser.p = 0
261 node, e = s.GetByPath("test", 2, "x")
262 if e == nil {
263 t.Fatalf("node: %v, err: %v", node, e)
264 }
265 s.parser.p = 0
266 node, e = s.GetByPath("err1", 0)
267 if e == nil {
268 t.Fatalf("node: %v, err: %v", node, e)
269 }
270 s.parser.p = 0
271 node, e = s.GetByPath("err2", "x")
272 if e == nil {
273 t.Fatalf("node: %v, err: %v", node, e)
274 }
275 }
276
277 func TestLoadIndex(t *testing.T) {
278 node, err := NewSearcher(`{"a":[-0, 1, -1.2, -1.2e-10]}`).GetByPath("a")
279 if err != nil {
280 t.Fatal(err)
281 }
282 a, _ := node.Index(3).Float64()
283 assert.Equal(t, -1.2e-10, a)
284 m, _ := node.Array()
285 assert.Equal(t, m, []interface{}{
286 float64(0),
287 float64(1),
288 -1.2,
289 -1.2e-10,
290 })
291 }
292
293 func TestSearchNotExist(t *testing.T) {
294 s := NewSearcher(` { "xx" : [ 0, "" ] ,"yy" :{ "2": "" } } `)
295 node, e := s.GetByPath("xx", 2)
296 if node.Exists() {
297 t.Fatalf("node: %v, err: %v", node, e)
298 }
299 node, e = s.GetByPath("xx", 1)
300 if e != nil || !node.Exists() {
301 t.Fatalf("node: %v, err: %v", node, e)
302 }
303 node, e = s.GetByPath("yy", "3")
304 if node.Exists() {
305 t.Fatalf("node: %v, err: %v", node, e)
306 }
307 node, e = s.GetByPath("yy", "2")
308 if e != nil || !node.Exists() {
309 t.Fatalf("node: %v, err: %v", node, e)
310 }
311 }
312
313 func BenchmarkGetOne_Sonic(b *testing.B) {
314 b.SetBytes(int64(len(_TwitterJson)))
315 ast := NewSearcher(_TwitterJson)
316 for i := 0; i < b.N; i++ {
317 node, err := ast.GetByPath("statuses", 3, "id")
318 if err != nil {
319 b.Fatal(err)
320 }
321 x, _ := node.Int64()
322 if x != 249279667666817024 {
323 b.Fatal(node.Interface())
324 }
325 }
326 }
327
328 func BenchmarkGetFull_Sonic(b *testing.B) {
329 ast := NewSearcher(_TwitterJson)
330 b.SetBytes(int64(len(_TwitterJson)))
331 b.ReportAllocs()
332 b.ResetTimer()
333 for i := 0; i < b.N; i++ {
334 node, err := ast.GetByPath()
335 if err != nil || node.Type() != V_OBJECT {
336 b.Fatal(err)
337 }
338 }
339 }
340
341 func BenchmarkGetWithManyCompare_Sonic(b *testing.B) {
342 b.SetBytes(int64(len(_LotsCompare)))
343 ast := NewSearcher(_LotsCompare)
344 for i := 0; i < b.N; i++ {
345 node, err := ast.GetByPath("is")
346 if err != nil {
347 b.Fatal(err)
348 }
349 x, _ := node.Int64()
350 if x != 1 {
351 b.Fatal(node.Interface())
352 }
353 }
354 }
355
356 func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
357 b.SetBytes(int64(len(_TwitterJson)))
358 b.RunParallel(func(pb *testing.PB) {
359 ast := NewSearcher(_TwitterJson)
360 for pb.Next() {
361 node, err := ast.GetByPath("statuses", 3, "id")
362 if err != nil {
363 b.Fatal(err)
364 }
365 x, _ := node.Int64()
366 if x != 249279667666817024 {
367 b.Fatal(node.Interface())
368 }
369 }
370 })
371 }
372
373 func BenchmarkSetOne_Sonic(b *testing.B) {
374 node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
375 if err != nil {
376 b.Fatal(err)
377 }
378 n := NewNumber(strconv.Itoa(math.MaxInt32))
379 _, err = node.Set("id", n)
380 if err != nil {
381 b.Fatal(err)
382 }
383 b.SetBytes(int64(len(_TwitterJson)))
384 b.ReportAllocs()
385 b.ResetTimer()
386 for i := 0; i < b.N; i++ {
387 node, _ := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
388 _, _ = node.Set("id", n)
389 }
390 }
391
392 func BenchmarkSetOne_Parallel_Sonic(b *testing.B) {
393 node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
394 if err != nil {
395 b.Fatal(err)
396 }
397 n := NewNumber(strconv.Itoa(math.MaxInt32))
398 _, err = node.Set("id", n)
399 if err != nil {
400 b.Fatal(err)
401 }
402 b.SetBytes(int64(len(_TwitterJson)))
403 b.ReportAllocs()
404 b.ResetTimer()
405 b.RunParallel(func(pb *testing.PB) {
406 for pb.Next() {
407 node, _ := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
408 _, _ = node.Set("id", n)
409 }
410 })
411 }
412
View as plain text