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  }