github.com/bytedance/sonic@v1.11.7-0.20240517092252-d2edb31b167b/ast/encode_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 `encoding/json` 21 `runtime` 22 `sync` 23 `testing` 24 `strings` 25 26 `github.com/bytedance/sonic/internal/native/types` 27 `github.com/stretchr/testify/assert` 28 `github.com/stretchr/testify/require` 29 ) 30 31 func TestGC_Encode(t *testing.T) { 32 if debugSyncGC { 33 return 34 } 35 root, err := NewSearcher(_TwitterJson).GetByPath() 36 if err != nil { 37 t.Fatal(err) 38 } 39 root.LoadAll() 40 _, err = root.MarshalJSON() 41 if err != nil { 42 t.Fatal(err) 43 } 44 wg := &sync.WaitGroup{} 45 N := 10000 46 for i:=0; i<N; i++ { 47 wg.Add(1) 48 go func (wg *sync.WaitGroup) { 49 defer wg.Done() 50 root, err := NewSearcher(_TwitterJson).GetByPath() 51 if err != nil { 52 t.Error(err) 53 return 54 } 55 root.Load() 56 _, err = root.MarshalJSON() 57 if err != nil { 58 t.Error(err) 59 return 60 } 61 runtime.GC() 62 }(wg) 63 } 64 wg.Wait() 65 } 66 67 func TestEncodeValue(t *testing.T) { 68 obj := new(_TwitterStruct) 69 if err := json.Unmarshal([]byte(_TwitterJson), obj); err != nil { 70 t.Fatal(err) 71 } 72 // buf, err := encoder.Encode(obj, encoder.EscapeHTML|encoder.SortMapKeys) 73 buf, err := json.Marshal(obj) 74 if err != nil { 75 t.Fatal(err) 76 } 77 quote, err := json.Marshal(_TwitterJson) 78 if err != nil { 79 t.Fatal(err) 80 } 81 type Case struct { 82 node Node 83 exp string 84 err bool 85 } 86 input := []Case{ 87 {NewNull(), "null", false}, 88 {NewBool(true), "true", false}, 89 {NewBool(false), "false", false}, 90 {NewNumber("0.0"), "0.0", false}, 91 {NewString(""), `""`, false}, 92 {NewString(`\"\"`), `"\\\"\\\""`, false}, 93 {NewString(_TwitterJson), string(quote), false}, 94 {NewArray([]Node{}), "[]", false}, 95 {NewArray([]Node{NewString(""), NewNull()}), `["",null]`, false}, 96 {NewArray([]Node{NewBool(true), NewString("true"), NewString("\t")}), `[true,"true","\t"]`, false}, 97 {NewObject([]Pair{Pair{"a", NewNull()}, Pair{"b", NewNumber("0")}}), `{"a":null,"b":0}`, false}, 98 {NewObject([]Pair{Pair{"\ta", NewString("\t")}, Pair{"\bb", NewString("\b")}, Pair{"\nb", NewString("\n")}, Pair{"\ra", NewString("\r")}}),`{"\ta":"\t","\u0008b":"\u0008","\nb":"\n","\ra":"\r"}`, false}, 99 {NewObject([]Pair{}), `{}`, false}, 100 {NewObject([]Pair{Pair{Key: "", Value: NewNull()}}), `{"":null}`, false}, 101 {NewBytes([]byte("hello, world")), `"aGVsbG8sIHdvcmxk"`, false}, 102 {NewAny(obj), string(buf), false}, 103 {NewRaw(`[{ }]`), "[{}]", false}, 104 {Node{}, "", true}, 105 {Node{t: types.ValueType(1)}, "", true}, 106 } 107 for i, c := range input { 108 t.Log(i) 109 buf, err := json.Marshal(&c.node) 110 if c.err { 111 if err == nil { 112 t.Fatal(i) 113 } 114 continue 115 } 116 if err != nil { 117 t.Fatal(i, err) 118 } 119 assert.Equal(t, c.exp, string(buf)) 120 } 121 } 122 123 func TestEncodeNode(t *testing.T) { 124 data := `{"a":[{},[],-0.1,true,false,null,""],"b":0,"c":true,"d":false,"e":null,"g":""}` 125 root, e := NewSearcher(data).GetByPath() 126 if e != nil { 127 t.Fatal(root) 128 } 129 ret, err := root.MarshalJSON() 130 if err != nil { 131 t.Fatal(err) 132 } 133 if string(ret) != data { 134 t.Fatal(string(ret)) 135 } 136 root.skipAllKey() 137 ret, err = root.MarshalJSON() 138 if err != nil { 139 t.Fatal(err) 140 } 141 if string(ret) != data { 142 t.Fatal(string(ret)) 143 } 144 root.loadAllKey() 145 ret, err = root.MarshalJSON() 146 if err != nil { 147 t.Fatal(err) 148 } 149 if string(ret) != data { 150 t.Fatal(string(ret)) 151 } 152 } 153 154 type SortableNode struct { 155 sorted bool 156 *Node 157 } 158 159 func (j *SortableNode) UnmarshalJSON(data []byte) (error) { 160 j.Node = new(Node) 161 return j.Node.UnmarshalJSON(data) 162 } 163 164 func (j *SortableNode) MarshalJSON() ([]byte, error) { 165 if !j.sorted { 166 j.Node.SortKeys(true) 167 j.sorted = true 168 } 169 return j.Node.MarshalJSON() 170 } 171 172 func TestMarshalSort(t *testing.T) { 173 var data = `{"d":3,"a":{"c":1,"b":2},"e":null}` 174 var obj map[string]*SortableNode 175 require.NoError(t, json.Unmarshal([]byte(data), &obj)) 176 out, err := json.Marshal(obj) 177 require.NoError(t, err) 178 require.Equal(t, `{"a":{"b":2,"c":1},"d":3,"e":null}`, string(out)) 179 out, err = json.Marshal(obj) 180 require.NoError(t, err) 181 require.Equal(t, `{"a":{"b":2,"c":1},"d":3,"e":null}`, string(out)) 182 } 183 184 func BenchmarkEncodeRaw_Sonic(b *testing.B) { 185 data := _TwitterJson 186 root, e := NewSearcher(data).GetByPath() 187 if e != nil { 188 b.Fatal(root) 189 } 190 _, err := root.MarshalJSON() 191 if err != nil { 192 b.Fatal(err) 193 } 194 b.SetBytes(int64(len(data))) 195 b.ResetTimer() 196 for i:=0; i<b.N; i++ { 197 _, err := root.MarshalJSON() 198 if err != nil { 199 b.Fatal(err) 200 } 201 } 202 } 203 204 func BenchmarkEncodeSkip_Sonic(b *testing.B) { 205 data := _TwitterJson 206 root, e := NewParser(data).Parse() 207 if e != 0 { 208 b.Fatal(root) 209 } 210 root.skipAllKey() 211 _, err := root.MarshalJSON() 212 if err != nil { 213 b.Fatal(err) 214 } 215 b.SetBytes(int64(len(data))) 216 b.ResetTimer() 217 for i:=0; i<b.N; i++ { 218 _, err := root.MarshalJSON() 219 if err != nil { 220 b.Fatal(err) 221 } 222 } 223 } 224 225 func BenchmarkEncodeLoad_Sonic(b *testing.B) { 226 data := _TwitterJson 227 root, e := NewParser(data).Parse() 228 if e != 0 { 229 b.Fatal(root) 230 } 231 root.loadAllKey() 232 _, err := root.MarshalJSON() 233 if err != nil { 234 b.Fatal(err) 235 } 236 b.SetBytes(int64(len(data))) 237 b.ResetTimer() 238 for i:=0; i<b.N; i++ { 239 _, err := root.MarshalJSON() 240 if err != nil { 241 b.Fatal(err) 242 } 243 } 244 } 245 246 func TestEncodeNone(t *testing.T) { 247 n := NewObject([]Pair{{Key:"a", Value:Node{}}}) 248 out, err := n.MarshalJSON() 249 require.NoError(t, err) 250 require.Equal(t, "{}", string(out)) 251 n = NewObject([]Pair{{Key:"a", Value:NewNull()}, {Key:"b", Value:Node{}}}) 252 out, err = n.MarshalJSON() 253 require.NoError(t, err) 254 require.Equal(t, `{"a":null}`, string(out)) 255 256 n = NewArray([]Node{Node{}}) 257 out, err = n.MarshalJSON() 258 require.NoError(t, err) 259 require.Equal(t, "[]", string(out)) 260 n = NewArray([]Node{NewNull(), Node{}}) 261 out, err = n.MarshalJSON() 262 require.NoError(t, err) 263 require.Equal(t, `[null]`, string(out)) 264 } 265 266 267 type Path = []interface{} 268 269 type testGetApi struct { 270 json string 271 path Path 272 } 273 274 type checkError func(error) bool 275 276 func isSyntaxError(err error) bool { 277 if err == nil { 278 return false 279 } 280 return strings.HasPrefix(err.Error(), `"Syntax error at index`) 281 } 282 283 func isEmptySource(err error) bool { 284 if err == nil { 285 return false 286 } 287 return strings.Contains(err.Error(), "no sources available") 288 } 289 290 func isErrNotExist(err error) bool { 291 return err == ErrNotExist 292 } 293 294 func isErrUnsupportType(err error) bool { 295 return err == ErrUnsupportType 296 } 297 298 func testSyntaxJson(t *testing.T, json string, path ...interface{}) { 299 search := NewSearcher(json) 300 _, err := search.GetByPath(path...) 301 assert.True(t, isSyntaxError(err)) 302 } 303 304 func TestGetFromEmptyJson(t *testing.T) { 305 tests := []testGetApi { 306 { "", nil }, 307 { "", Path{}}, 308 { "", Path{""}}, 309 { "", Path{0}}, 310 { "", Path{"", ""}}, 311 } 312 for _, test := range tests { 313 f := func(t *testing.T) { 314 search := NewSearcher(test.json) 315 _, err := search.GetByPath(test.path...) 316 assert.True(t, isEmptySource(err)) 317 } 318 t.Run(test.json, f) 319 } 320 } 321 322 func TestGetFromSyntaxError(t *testing.T) { 323 tests := []testGetApi { 324 { " \r\n\f\t", Path{} }, 325 { "123.", Path{} }, 326 { "+124", Path{} }, 327 { "-", Path{} }, 328 { "-e123", Path{} }, 329 { "-1.e123", Path{} }, 330 { "-12e456.1", Path{} }, 331 { "-12e.1", Path{} }, 332 { "[", Path{} }, 333 { "{", Path{} }, 334 { "[}", Path{} }, 335 { "{]", Path{} }, 336 { "{,}", Path{} }, 337 { "[,]", Path{} }, 338 { "tru", Path{} }, 339 { "fals", Path{} }, 340 { "nul", Path{} }, 341 { `{"a":"`, Path{"a"} }, 342 { `{"`, Path{} }, 343 { `"`, Path{} }, 344 { `"\"`, Path{} }, 345 { `"\\\"`, Path{} }, 346 { `"hello`, Path{} }, 347 { `{{}}`, Path{} }, 348 { `{[]}`, Path{} }, 349 { `{:,}`, Path{} }, 350 { `{test:error}`, Path{} }, 351 { `{":true}`, Path{} }, 352 { `{"" false}`, Path{} }, 353 { `{ "" : "false }`, Path{} }, 354 { `{"":"",}`, Path{} }, 355 { `{ " test : true}`, Path{} }, 356 { `{ "test" : tru }`, Path{} }, 357 { `{ "test" : true , }`, Path{} }, 358 { `{ {"test" : true , } }`, Path{} }, 359 { `{"test":1. }`, Path{} }, 360 { `{"\\\""`, Path{} }, 361 { `{"\\\"":`, Path{} }, 362 { `{"\\\":",""}`, Path{} }, 363 { `[{]`, Path{} }, 364 { `[tru]`, Path{} }, 365 { `[-1.]`, Path{} }, 366 { `[[]`, Path{} }, 367 { `[[],`, Path{} }, 368 { `[ true , false , [ ]`, Path{} }, 369 { `[true, false, [],`, Path{} }, 370 { `[true, false, [],]`, Path{} }, 371 { `{"key": [true, false, []], "key2": {{}}`, Path{} }, 372 } 373 374 for _, test := range tests { 375 f := func(t *testing.T) { 376 testSyntaxJson(t, test.json, test.path...) 377 path := append(Path{"key"}, test.path...) 378 testSyntaxJson(t, `{"key":` + test.json, path...) 379 path = append(Path{""}, test.path...) 380 testSyntaxJson(t, `{"":` + test.json, path...) 381 path = append(Path{1}, test.path...) 382 testSyntaxJson(t, `["",` + test.json, path...) 383 } 384 t.Run(test.json, f) 385 } 386 } 387 388 // NOTE: GetByPath API not validate the undemanded fields for performance. 389 func TestGetWithInvalidUndemandedField(t *testing.T) { 390 type Any = interface{} 391 tests := []struct { 392 json string 393 path Path 394 exp Any 395 } { 396 { "-0xyz", Path{}, Any(float64(-0))}, 397 { "-12e4xyz", Path{}, Any(float64(-12e4))}, 398 { "truex", Path{}, Any(true)}, 399 { "false,", Path{}, Any(false)}, 400 { `{"a":{,xxx},"b":true}`, Path{"b"}, Any(true)}, 401 { `{"a":[,xxx],"b":true}`, Path{"b"}, Any(true)}, 402 } 403 404 for _, test := range tests { 405 f := func(t *testing.T) { 406 search := NewSearcher(test.json) 407 node, err := search.GetByPath(test.path...) 408 assert.NoError(t, err) 409 v, err := node.Interface() 410 assert.NoError(t, err) 411 assert.Equal(t, v, test.exp) 412 } 413 t.Run(test.json, f) 414 } 415 } 416 417 func TestGet_InvalidPathType(t *testing.T) { 418 assert.Panics(t, assert.PanicTestFunc(func() { 419 data := `{"a":[{"b":true}]}` 420 s := NewSearcher(data) 421 s.GetByPath("a", true) 422 423 s = NewSearcher(data) 424 s.GetByPath("a", nil) 425 426 s = NewSearcher(data) 427 s.GetByPath("a", -1) 428 })) 429 }