...

Source file src/github.com/bytedance/sonic/ast/search_test.go

Documentation: github.com/bytedance/sonic/ast

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

View as plain text