github.com/bytedance/sonic@v1.11.7-0.20240517092252-d2edb31b167b/ast/search_test.go (about) 1 /* 2 * Copyright 2021 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 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 // A limitation of the race detecting is 8128. 40 // See https://github.com/golang/go/issues/43898 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 // object 118 {`{}`, []interface{}{"b"}, ErrNotExist}, 119 {` { } `, []interface{}{"b"}, ErrNotExist}, 120 {`{"a":null}`, []interface{}{"b"}, ErrNotExist}, 121 // This should be invalid char errors. 122 // {`{"a":null}`, []interface{}{"a", "b"}, ErrNotExist}, 123 // {`{"a":null}`, []interface{}{"a", 0}, ErrNotExist}, 124 // {`{"a":null}`, []interface{}{"a", "b", 0}, ErrNotExist}, 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 // array 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 }