github.com/youryharchenko/gjson@v0.0.0-20161216162241-e3f249f4fdf3/gjson_test.go (about) 1 package gjson 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "io" 9 "math/rand" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/buger/jsonparser" 15 "github.com/mailru/easyjson/jlexer" 16 fflib "github.com/pquerna/ffjson/fflib/v1" 17 ) 18 19 // TestRandomData is a fuzzing test that throws random data at the Parse 20 // function looking for panics. 21 func TestRandomData(t *testing.T) { 22 var lstr string 23 defer func() { 24 if v := recover(); v != nil { 25 println("'" + hex.EncodeToString([]byte(lstr)) + "'") 26 println("'" + lstr + "'") 27 panic(v) 28 } 29 }() 30 rand.Seed(time.Now().UnixNano()) 31 b := make([]byte, 200) 32 for i := 0; i < 2000000; i++ { 33 n, err := rand.Read(b[:rand.Int()%len(b)]) 34 if err != nil { 35 t.Fatal(err) 36 } 37 lstr = string(b[:n]) 38 GetBytes([]byte(lstr), "zzzz") 39 Parse(lstr) 40 } 41 } 42 43 func TestRandomValidStrings(t *testing.T) { 44 rand.Seed(time.Now().UnixNano()) 45 b := make([]byte, 200) 46 for i := 0; i < 100000; i++ { 47 n, err := rand.Read(b[:rand.Int()%len(b)]) 48 if err != nil { 49 t.Fatal(err) 50 } 51 sm, err := json.Marshal(string(b[:n])) 52 if err != nil { 53 t.Fatal(err) 54 } 55 var su string 56 if err := json.Unmarshal([]byte(sm), &su); err != nil { 57 t.Fatal(err) 58 } 59 token := Get(`{"str":`+string(sm)+`}`, "str") 60 if token.Type != String || token.Str != su { 61 println("["+token.Raw+"]", "["+token.Str+"]", "["+su+"]", "["+string(sm)+"]") 62 t.Fatal("string mismatch") 63 } 64 } 65 } 66 func testEscapePath(t *testing.T, json, path, expect string) { 67 if Get(json, path).String() != expect { 68 t.Fatalf("expected '%v', got '%v'", expect, Get(json, path).String()) 69 } 70 } 71 72 func TestEscapePath(t *testing.T) { 73 json := `{ 74 "test":{ 75 "*":"valZ", 76 "*v":"val0", 77 "keyv*":"val1", 78 "key*v":"val2", 79 "keyv?":"val3", 80 "key?v":"val4", 81 "keyv.":"val5", 82 "key.v":"val6", 83 "keyk*":{"key?":"val7"} 84 } 85 }` 86 87 testEscapePath(t, json, "test.\\*", "valZ") 88 testEscapePath(t, json, "test.\\*v", "val0") 89 testEscapePath(t, json, "test.keyv\\*", "val1") 90 testEscapePath(t, json, "test.key\\*v", "val2") 91 testEscapePath(t, json, "test.keyv\\?", "val3") 92 testEscapePath(t, json, "test.key\\?v", "val4") 93 testEscapePath(t, json, "test.keyv\\.", "val5") 94 testEscapePath(t, json, "test.key\\.v", "val6") 95 testEscapePath(t, json, "test.keyk\\*.key\\?", "val7") 96 } 97 98 // this json block is poorly formed on purpose. 99 var basicJSON = `{"age":100, "name":{"here":"B\\\"R"}, 100 "noop":{"what is a wren?":"a bird"}, 101 "happy":true,"immortal":false, 102 "items":[1,2,3,{"tags":[1,2,3],"points":[[1,2],[3,4]]},4,5,6,7], 103 "arr":["1",2,"3",{"hello":"world"},"4",5], 104 "vals":[1,2,3,{"sadf":sdf"asdf"}],"name":{"first":"tom","last":null}, 105 "loggy":{ 106 "programmers": [ 107 { 108 "firstName": "Brett", 109 "lastName": "McLaughlin", 110 "email": "aaaa", 111 "tag": "good" 112 }, 113 { 114 "firstName": "Jason", 115 "lastName": "Hunter", 116 "email": "bbbb", 117 "tag": "bad" 118 }, 119 { 120 "firstName": "Elliotte", 121 "lastName": "Harold", 122 "email": "cccc", 123 "tag":, "good" 124 }, 125 { 126 "firstName": 1002.3, 127 "age": 101 128 } 129 ] 130 } 131 }` 132 var basicJSONB = []byte(basicJSON) 133 134 func TestByteSafety(t *testing.T) { 135 jsonb := []byte(`{"name":"Janet","age":38}`) 136 mtok := GetBytes(jsonb, "name") 137 if mtok.String() != "Janet" { 138 t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 139 } 140 mtok2 := GetBytes(jsonb, "age") 141 if mtok2.Raw != "38" { 142 t.Fatalf("expected %v, got %v", "Jason", mtok2.Raw) 143 } 144 jsonb[9] = 'T' 145 jsonb[12] = 'd' 146 jsonb[13] = 'y' 147 if mtok.String() != "Janet" { 148 t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 149 } 150 } 151 152 func get(json, path string) Result { 153 return GetBytes([]byte(basicJSONB), path) 154 } 155 156 func TestBasic(t *testing.T) { 157 var mtok Result 158 mtok = get(basicJSON, `loggy.programmers.#[tag="good"].firstName`) 159 if mtok.String() != "Brett" { 160 t.Fatalf("expected %v, got %v", "Brett", mtok.String()) 161 } 162 mtok = get(basicJSON, `loggy.programmers.#[tag="good"]#.firstName`) 163 if mtok.String() != `["Brett","Elliotte"]` { 164 t.Fatalf("expected %v, got %v", `["Brett","Elliotte"]`, mtok.String()) 165 } 166 167 mtok = get(basicJSON, `loggy.programmers`) 168 var count int 169 mtok.ForEach(func(key, value Result) bool { 170 if key.Exists() { 171 t.Fatalf("expected %v, got %v", false, key.Exists()) 172 } 173 count++ 174 if count == 3 { 175 return false 176 } 177 if count == 1 { 178 i := 0 179 value.ForEach(func(key, value Result) bool { 180 switch i { 181 case 0: 182 if key.String() != "firstName" || value.String() != "Brett" { 183 t.Fatalf("expected %v/%v got %v/%v", "firstName", "Brett", key.String(), value.String()) 184 } 185 case 1: 186 if key.String() != "lastName" || value.String() != "McLaughlin" { 187 t.Fatalf("expected %v/%v got %v/%v", "lastName", "McLaughlin", key.String(), value.String()) 188 } 189 case 2: 190 if key.String() != "email" || value.String() != "aaaa" { 191 t.Fatalf("expected %v/%v got %v/%v", "email", "aaaa", key.String(), value.String()) 192 } 193 } 194 i++ 195 return true 196 }) 197 } 198 return true 199 }) 200 if count != 3 { 201 t.Fatalf("expected %v, got %v", 3, count) 202 } 203 mtok = get(basicJSON, `loggy.programmers.#[age=101].firstName`) 204 if mtok.String() != "1002.3" { 205 t.Fatalf("expected %v, got %v", "1002.3", mtok.String()) 206 } 207 mtok = get(basicJSON, `loggy.programmers.#[firstName != "Brett"].firstName`) 208 if mtok.String() != "Jason" { 209 t.Fatalf("expected %v, got %v", "Jason", mtok.String()) 210 } 211 mtok = get(basicJSON, `loggy.programmers.#[firstName % "Bre*"].email`) 212 if mtok.String() != "aaaa" { 213 t.Fatalf("expected %v, got %v", "aaaa", mtok.String()) 214 } 215 mtok = get(basicJSON, `loggy.programmers.#[firstName == "Brett"].email`) 216 if mtok.String() != "aaaa" { 217 t.Fatalf("expected %v, got %v", "aaaa", mtok.String()) 218 } 219 mtok = get(basicJSON, "loggy") 220 if mtok.Type != JSON { 221 t.Fatalf("expected %v, got %v", JSON, mtok.Type) 222 } 223 if len(mtok.Map()) != 1 { 224 t.Fatalf("expected %v, got %v", 1, len(mtok.Map())) 225 } 226 programmers := mtok.Map()["programmers"] 227 if programmers.Array()[1].Map()["firstName"].Str != "Jason" { 228 t.Fatalf("expected %v, got %v", "Jason", mtok.Map()["programmers"].Array()[1].Map()["firstName"].Str) 229 } 230 231 if Parse(basicJSON).Get("loggy.programmers").Get("1").Get("firstName").Str != "Jason" { 232 t.Fatalf("expected %v, got %v", "Jason", Parse(basicJSON).Get("loggy.programmers").Get("1").Get("firstName").Str) 233 } 234 var token Result 235 if token = Parse("-102"); token.Num != -102 { 236 t.Fatal("expected %v, got %v", -102, token.Num) 237 } 238 if token = Parse("102"); token.Num != 102 { 239 t.Fatal("expected %v, got %v", 102, token.Num) 240 } 241 if token = Parse("102.2"); token.Num != 102.2 { 242 t.Fatal("expected %v, got %v", 102.2, token.Num) 243 } 244 if token = Parse(`"hello"`); token.Str != "hello" { 245 t.Fatal("expected %v, got %v", "hello", token.Str) 246 } 247 if token = Parse(`"\"he\nllo\""`); token.Str != "\"he\nllo\"" { 248 t.Fatal("expected %v, got %v", "\"he\nllo\"", token.Str) 249 } 250 mtok = get(basicJSON, "loggy.programmers.#.firstName") 251 if len(mtok.Array()) != 4 { 252 t.Fatalf("expected 4, got %v", len(mtok.Array())) 253 } 254 for i, ex := range []string{"Brett", "Jason", "Elliotte", "1002.3"} { 255 if mtok.Array()[i].String() != ex { 256 t.Fatalf("expected '%v', got '%v'", ex, mtok.Array()[i].String()) 257 } 258 } 259 mtok = get(basicJSON, "loggy.programmers.#.asd") 260 if mtok.Type != JSON { 261 t.Fatal("expected %v, got %v", JSON, mtok.Type) 262 } 263 if len(mtok.Array()) != 0 { 264 t.Fatalf("expected 0, got %v", len(mtok.Array())) 265 } 266 267 if get(basicJSON, "items.3.tags.#").Num != 3 { 268 t.Fatalf("expected 3, got %v", get(basicJSON, "items.3.tags.#").Num) 269 } 270 if get(basicJSON, "items.3.points.1.#").Num != 2 { 271 t.Fatalf("expected 2, got %v", get(basicJSON, "items.3.points.1.#").Num) 272 } 273 if get(basicJSON, "items.#").Num != 8 { 274 t.Fatalf("expected 6, got %v", get(basicJSON, "items.#").Num) 275 } 276 if get(basicJSON, "vals.#").Num != 4 { 277 t.Fatalf("expected 4, got %v", get(basicJSON, "vals.#").Num) 278 } 279 if !get(basicJSON, "name.last").Exists() { 280 t.Fatal("expected true, got false") 281 } 282 token = get(basicJSON, "name.here") 283 if token.String() != "B\\\"R" { 284 t.Fatal("expecting 'B\\\"R'", "got", token.String()) 285 } 286 token = get(basicJSON, "arr.#") 287 if token.String() != "6" { 288 t.Fatal("expecting '6'", "got", token.String()) 289 } 290 token = get(basicJSON, "arr.3.hello") 291 if token.String() != "world" { 292 t.Fatal("expecting 'world'", "got", token.String()) 293 } 294 _ = token.Value().(string) 295 token = get(basicJSON, "name.first") 296 if token.String() != "tom" { 297 t.Fatal("expecting 'tom'", "got", token.String()) 298 } 299 _ = token.Value().(string) 300 token = get(basicJSON, "name.last") 301 if token.String() != "null" { 302 t.Fatal("expecting 'null'", "got", token.String()) 303 } 304 if token.Value() != nil { 305 t.Fatal("should be nil") 306 } 307 token = get(basicJSON, "age") 308 if token.String() != "100" { 309 t.Fatal("expecting '100'", "got", token.String()) 310 } 311 _ = token.Value().(float64) 312 token = get(basicJSON, "happy") 313 if token.String() != "true" { 314 t.Fatal("expecting 'true'", "got", token.String()) 315 } 316 _ = token.Value().(bool) 317 token = get(basicJSON, "immortal") 318 if token.String() != "false" { 319 t.Fatal("expecting 'false'", "got", token.String()) 320 } 321 _ = token.Value().(bool) 322 token = get(basicJSON, "noop") 323 if token.String() != `{"what is a wren?":"a bird"}` { 324 t.Fatal("expecting '"+`{"what is a wren?":"a bird"}`+"'", "got", token.String()) 325 } 326 _ = token.Value().(map[string]interface{}) 327 328 if get(basicJSON, "").Value() != nil { 329 t.Fatal("should be nil") 330 } 331 332 get(basicJSON, "vals.hello") 333 334 mm := Parse(basicJSON).Value().(map[string]interface{}) 335 fn := mm["loggy"].(map[string]interface{})["programmers"].([]interface{})[1].(map[string]interface{})["firstName"].(string) 336 if fn != "Jason" { 337 t.Fatalf("expecting %v, got %v", "Jason", fn) 338 } 339 } 340 func TestUnicode(t *testing.T) { 341 var json = `{"key":0,"的情况下解":{"key":1,"的情况":2}}` 342 if Get(json, "的情况下解.key").Num != 1 { 343 t.Fatal("fail") 344 } 345 if Get(json, "的情况下解.的情况").Num != 2 { 346 t.Fatal("fail") 347 } 348 if Get(json, "的情况下解.的?况").Num != 2 { 349 t.Fatal("fail") 350 } 351 if Get(json, "的情况下解.的?*").Num != 2 { 352 t.Fatal("fail") 353 } 354 if Get(json, "的情况下解.*?况").Num != 2 { 355 t.Fatal("fail") 356 } 357 if Get(json, "的情?下解.*?况").Num != 2 { 358 t.Fatal("fail") 359 } 360 if Get(json, "的情下解.*?况").Num != 0 { 361 t.Fatal("fail") 362 } 363 } 364 365 func TestUnescape(t *testing.T) { 366 unescape(string([]byte{'\\', '\\', 0})) 367 unescape(string([]byte{'\\', '/', '\\', 'b', '\\', 'f'})) 368 } 369 func assert(t testing.TB, cond bool) { 370 if !cond { 371 t.Fatal("assert failed") 372 } 373 } 374 func TestLess(t *testing.T) { 375 assert(t, !Result{Type: Null}.Less(Result{Type: Null}, true)) 376 assert(t, Result{Type: Null}.Less(Result{Type: False}, true)) 377 assert(t, Result{Type: Null}.Less(Result{Type: True}, true)) 378 assert(t, Result{Type: Null}.Less(Result{Type: JSON}, true)) 379 assert(t, Result{Type: Null}.Less(Result{Type: Number}, true)) 380 assert(t, Result{Type: Null}.Less(Result{Type: String}, true)) 381 assert(t, !Result{Type: False}.Less(Result{Type: Null}, true)) 382 assert(t, Result{Type: False}.Less(Result{Type: True}, true)) 383 assert(t, Result{Type: String, Str: "abc"}.Less(Result{Type: String, Str: "bcd"}, true)) 384 assert(t, Result{Type: String, Str: "ABC"}.Less(Result{Type: String, Str: "abc"}, true)) 385 assert(t, !Result{Type: String, Str: "ABC"}.Less(Result{Type: String, Str: "abc"}, false)) 386 assert(t, Result{Type: Number, Num: 123}.Less(Result{Type: Number, Num: 456}, true)) 387 assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number, Num: 123}, true)) 388 assert(t, !Result{Type: Number, Num: 456}.Less(Result{Type: Number, Num: 456}, true)) 389 assert(t, stringLessInsensitive("abcde", "BBCDE")) 390 assert(t, stringLessInsensitive("abcde", "bBCDE")) 391 assert(t, stringLessInsensitive("Abcde", "BBCDE")) 392 assert(t, stringLessInsensitive("Abcde", "bBCDE")) 393 assert(t, !stringLessInsensitive("bbcde", "aBCDE")) 394 assert(t, !stringLessInsensitive("bbcde", "ABCDE")) 395 assert(t, !stringLessInsensitive("Bbcde", "aBCDE")) 396 assert(t, !stringLessInsensitive("Bbcde", "ABCDE")) 397 assert(t, !stringLessInsensitive("abcde", "ABCDE")) 398 assert(t, !stringLessInsensitive("Abcde", "ABCDE")) 399 assert(t, !stringLessInsensitive("abcde", "ABCDE")) 400 assert(t, !stringLessInsensitive("ABCDE", "ABCDE")) 401 assert(t, !stringLessInsensitive("abcde", "abcde")) 402 assert(t, !stringLessInsensitive("123abcde", "123Abcde")) 403 assert(t, !stringLessInsensitive("123Abcde", "123Abcde")) 404 assert(t, !stringLessInsensitive("123Abcde", "123abcde")) 405 assert(t, !stringLessInsensitive("123abcde", "123abcde")) 406 assert(t, !stringLessInsensitive("124abcde", "123abcde")) 407 assert(t, !stringLessInsensitive("124Abcde", "123Abcde")) 408 assert(t, !stringLessInsensitive("124Abcde", "123abcde")) 409 assert(t, !stringLessInsensitive("124abcde", "123abcde")) 410 assert(t, stringLessInsensitive("124abcde", "125abcde")) 411 assert(t, stringLessInsensitive("124Abcde", "125Abcde")) 412 assert(t, stringLessInsensitive("124Abcde", "125abcde")) 413 assert(t, stringLessInsensitive("124abcde", "125abcde")) 414 } 415 416 func TestIssue6(t *testing.T) { 417 data := `{ 418 "code": 0, 419 "msg": "", 420 "data": { 421 "sz002024": { 422 "qfqday": [ 423 [ 424 "2014-01-02", 425 "8.93", 426 "9.03", 427 "9.17", 428 "8.88", 429 "621143.00" 430 ], 431 [ 432 "2014-01-03", 433 "9.03", 434 "9.30", 435 "9.47", 436 "8.98", 437 "1624438.00" 438 ] 439 ] 440 } 441 } 442 }` 443 444 var num []string 445 for _, v := range Get(data, "data.sz002024.qfqday.0").Array() { 446 num = append(num, v.String()) 447 } 448 if fmt.Sprintf("%v", num) != "[2014-01-02 8.93 9.03 9.17 8.88 621143.00]" { 449 t.Fatalf("invalid result") 450 } 451 } 452 453 var exampleJSON = `{ 454 "widget": { 455 "debug": "on", 456 "window": { 457 "title": "Sample Konfabulator Widget", 458 "name": "main_window", 459 "width": 500, 460 "height": 500 461 }, 462 "image": { 463 "src": "Images/Sun.png", 464 "hOffset": 250, 465 "vOffset": 250, 466 "alignment": "center" 467 }, 468 "text": { 469 "data": "Click Here", 470 "size": 36, 471 "style": "bold", 472 "vOffset": 100, 473 "alignment": "center", 474 "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" 475 } 476 } 477 }` 478 479 func TestNewParse(t *testing.T) { 480 //fmt.Printf("%v\n", parse2(exampleJSON, "widget").String()) 481 } 482 483 func TestUnmarshalMap(t *testing.T) { 484 var m1 = Parse(exampleJSON).Value().(map[string]interface{}) 485 var m2 map[string]interface{} 486 if err := json.Unmarshal([]byte(exampleJSON), &m2); err != nil { 487 t.Fatal(err) 488 } 489 b1, err := json.Marshal(m1) 490 if err != nil { 491 t.Fatal(err) 492 } 493 b2, err := json.Marshal(m2) 494 if err != nil { 495 t.Fatal(err) 496 } 497 if bytes.Compare(b1, b2) != 0 { 498 t.Fatal("b1 != b2") 499 } 500 } 501 502 func TestSingleArrayValue(t *testing.T) { 503 var json = `{"key": "value","key2":[1,2,3,4,"A"]}` 504 var result = Get(json, "key") 505 var array = result.Array() 506 if len(array) != 1 { 507 t.Fatal("array is empty") 508 } 509 if array[0].String() != "value" { 510 t.Fatal("got %s, should be %s", array[0].String(), "value") 511 } 512 513 array = Get(json, "key2.#").Array() 514 if len(array) != 1 { 515 t.Fatal("got '%v', expected '%v'", len(array), 1) 516 } 517 518 array = Get(json, "key3").Array() 519 if len(array) != 0 { 520 t.Fatal("got '%v', expected '%v'", len(array), 0) 521 } 522 523 } 524 525 var manyJSON = ` { 526 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 527 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 528 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 529 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 530 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 531 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{ 532 "a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"hello":"world" 533 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 534 "position":{"type":"Point","coordinates":[-115.24,33.09]}, 535 "loves":["world peace"], 536 "name":{"last":"Anderson","first":"Nancy"}, 537 "age":31 538 "":{"a":"emptya","b":"emptyb"}, 539 "name.last":"Yellow", 540 "name.first":"Cat", 541 }` 542 543 func combine(results []Result) string { 544 return fmt.Sprintf("%v", results) 545 } 546 func TestManyBasic(t *testing.T) { 547 testWatchForFallback = true 548 defer func() { 549 testWatchForFallback = false 550 }() 551 testMany := func(shouldFallback bool, expect string, paths ...string) { 552 results := GetMany( 553 manyJSON, 554 paths..., 555 ) 556 if len(results) != len(paths) { 557 t.Fatalf("expected %v, got %v", len(paths), len(results)) 558 } 559 if fmt.Sprintf("%v", results) != expect { 560 t.Fatalf("expected %v, got %v", expect, results) 561 } 562 return 563 if testLastWasFallback != shouldFallback { 564 t.Fatalf("expected %v, got %v", shouldFallback, testLastWasFallback) 565 } 566 } 567 testMany(false, "[Point]", "position.type") 568 testMany(false, `[emptya ["world peace"] 31]`, ".a", "loves", "age") 569 testMany(false, `[["world peace"]]`, "loves") 570 testMany(false, `[{"last":"Anderson","first":"Nancy"} Nancy]`, "name", "name.first") 571 testMany(true, `[null]`, strings.Repeat("a.", 40)+"hello") 572 res := Get(manyJSON, strings.Repeat("a.", 48)+"a") 573 testMany(true, `[`+res.String()+`]`, strings.Repeat("a.", 48)+"a") 574 // these should fallback 575 testMany(true, `[Cat Nancy]`, "name\\.first", "name.first") 576 testMany(true, `[world]`, strings.Repeat("a.", 70)+"hello") 577 } 578 579 func TestRandomMany(t *testing.T) { 580 var lstr string 581 defer func() { 582 if v := recover(); v != nil { 583 println("'" + hex.EncodeToString([]byte(lstr)) + "'") 584 println("'" + lstr + "'") 585 panic(v) 586 } 587 }() 588 rand.Seed(time.Now().UnixNano()) 589 b := make([]byte, 512) 590 for i := 0; i < 50000; i++ { 591 n, err := rand.Read(b[:rand.Int()%len(b)]) 592 if err != nil { 593 t.Fatal(err) 594 } 595 lstr = string(b[:n]) 596 paths := make([]string, rand.Int()%64) 597 for i := range paths { 598 var b []byte 599 n := rand.Int() % 5 600 for j := 0; j < n; j++ { 601 if j > 0 { 602 b = append(b, '.') 603 } 604 nn := rand.Int() % 10 605 for k := 0; k < nn; k++ { 606 b = append(b, 'a'+byte(rand.Int()%26)) 607 } 608 } 609 paths[i] = string(b) 610 } 611 GetMany(lstr, paths...) 612 } 613 } 614 615 type BenchStruct struct { 616 Widget struct { 617 Window struct { 618 Name string `json:"name"` 619 } `json:"window"` 620 Image struct { 621 HOffset int `json:"hOffset"` 622 } `json:"image"` 623 Text struct { 624 OnMouseUp string `json:"onMouseUp"` 625 } `json:"text"` 626 } `json:"widget"` 627 } 628 629 var benchPaths = []string{ 630 "widget.window.name", 631 "widget.image.hOffset", 632 "widget.text.onMouseUp", 633 } 634 635 var benchManyPaths = []string{ 636 "widget.window.name", 637 "widget.image.hOffset", 638 "widget.text.onMouseUp", 639 "widget.window.title", 640 "widget.image.alignment", 641 "widget.text.style", 642 "widget.window.height", 643 "widget.image.src", 644 "widget.text.data", 645 "widget.text.size", 646 } 647 648 func BenchmarkGJSONGet(t *testing.B) { 649 t.ReportAllocs() 650 t.ResetTimer() 651 for i := 0; i < t.N; i++ { 652 for j := 0; j < len(benchPaths); j++ { 653 if Get(exampleJSON, benchPaths[j]).Type == Null { 654 t.Fatal("did not find the value") 655 } 656 } 657 } 658 t.N *= len(benchPaths) // because we are running against 3 paths 659 } 660 func BenchmarkGJSONGetMany4Paths(t *testing.B) { 661 benchmarkGJSONGetManyN(t, 4) 662 } 663 func BenchmarkGJSONGetMany8Paths(t *testing.B) { 664 benchmarkGJSONGetManyN(t, 8) 665 } 666 func BenchmarkGJSONGetMany16Paths(t *testing.B) { 667 benchmarkGJSONGetManyN(t, 16) 668 } 669 func BenchmarkGJSONGetMany32Paths(t *testing.B) { 670 benchmarkGJSONGetManyN(t, 32) 671 } 672 func BenchmarkGJSONGetMany64Paths(t *testing.B) { 673 benchmarkGJSONGetManyN(t, 64) 674 } 675 func BenchmarkGJSONGetMany128Paths(t *testing.B) { 676 benchmarkGJSONGetManyN(t, 128) 677 } 678 func BenchmarkGJSONGetMany256Paths(t *testing.B) { 679 benchmarkGJSONGetManyN(t, 256) 680 } 681 func BenchmarkGJSONGetMany512Paths(t *testing.B) { 682 benchmarkGJSONGetManyN(t, 512) 683 } 684 func benchmarkGJSONGetManyN(t *testing.B, n int) { 685 var paths []string 686 for len(paths) < n { 687 paths = append(paths, benchManyPaths...) 688 } 689 paths = paths[:n] 690 t.ReportAllocs() 691 t.ResetTimer() 692 for i := 0; i < t.N; i++ { 693 results := GetMany(exampleJSON, paths...) 694 if len(results) == 0 { 695 t.Fatal("did not find the value") 696 } 697 for j := 0; j < len(results); j++ { 698 if results[j].Type == Null { 699 t.Fatal("did not find the value") 700 } 701 } 702 } 703 t.N *= len(paths) // because we are running against 3 paths 704 } 705 706 func BenchmarkGJSONUnmarshalMap(t *testing.B) { 707 t.ReportAllocs() 708 t.ResetTimer() 709 for i := 0; i < t.N; i++ { 710 for j := 0; j < len(benchPaths); j++ { 711 parts := strings.Split(benchPaths[j], ".") 712 m, _ := Parse(exampleJSON).Value().(map[string]interface{}) 713 var v interface{} 714 for len(parts) > 0 { 715 part := parts[0] 716 if len(parts) > 1 { 717 m = m[part].(map[string]interface{}) 718 if m == nil { 719 t.Fatal("did not find the value") 720 } 721 } else { 722 v = m[part] 723 if v == nil { 724 t.Fatal("did not find the value") 725 } 726 } 727 parts = parts[1:] 728 } 729 } 730 } 731 t.N *= len(benchPaths) // because we are running against 3 paths 732 } 733 734 func BenchmarkJSONUnmarshalMap(t *testing.B) { 735 t.ReportAllocs() 736 t.ResetTimer() 737 for i := 0; i < t.N; i++ { 738 for j := 0; j < len(benchPaths); j++ { 739 parts := strings.Split(benchPaths[j], ".") 740 var m map[string]interface{} 741 if err := json.Unmarshal([]byte(exampleJSON), &m); err != nil { 742 t.Fatal(err) 743 } 744 var v interface{} 745 for len(parts) > 0 { 746 part := parts[0] 747 if len(parts) > 1 { 748 m = m[part].(map[string]interface{}) 749 if m == nil { 750 t.Fatal("did not find the value") 751 } 752 } else { 753 v = m[part] 754 if v == nil { 755 t.Fatal("did not find the value") 756 } 757 } 758 parts = parts[1:] 759 } 760 } 761 } 762 t.N *= len(benchPaths) // because we are running against 3 paths 763 } 764 765 func BenchmarkJSONUnmarshalStruct(t *testing.B) { 766 t.ReportAllocs() 767 t.ResetTimer() 768 for i := 0; i < t.N; i++ { 769 for j := 0; j < len(benchPaths); j++ { 770 var s BenchStruct 771 if err := json.Unmarshal([]byte(exampleJSON), &s); err != nil { 772 t.Fatal(err) 773 } 774 switch benchPaths[j] { 775 case "widget.window.name": 776 if s.Widget.Window.Name == "" { 777 t.Fatal("did not find the value") 778 } 779 case "widget.image.hOffset": 780 if s.Widget.Image.HOffset == 0 { 781 t.Fatal("did not find the value") 782 } 783 case "widget.text.onMouseUp": 784 if s.Widget.Text.OnMouseUp == "" { 785 t.Fatal("did not find the value") 786 } 787 } 788 } 789 } 790 t.N *= len(benchPaths) // because we are running against 3 paths 791 } 792 793 func BenchmarkJSONDecoder(t *testing.B) { 794 t.ReportAllocs() 795 t.ResetTimer() 796 for i := 0; i < t.N; i++ { 797 for j := 0; j < len(benchPaths); j++ { 798 dec := json.NewDecoder(bytes.NewBuffer([]byte(exampleJSON))) 799 var found bool 800 outer: 801 for { 802 tok, err := dec.Token() 803 if err != nil { 804 if err == io.EOF { 805 break 806 } 807 t.Fatal(err) 808 } 809 switch v := tok.(type) { 810 case string: 811 if found { 812 // break out once we find the value. 813 break outer 814 } 815 switch benchPaths[j] { 816 case "widget.window.name": 817 if v == "name" { 818 found = true 819 } 820 case "widget.image.hOffset": 821 if v == "hOffset" { 822 found = true 823 } 824 case "widget.text.onMouseUp": 825 if v == "onMouseUp" { 826 found = true 827 } 828 } 829 } 830 } 831 if !found { 832 t.Fatal("field not found") 833 } 834 } 835 } 836 t.N *= len(benchPaths) // because we are running against 3 paths 837 } 838 839 func BenchmarkFFJSONLexer(t *testing.B) { 840 t.ReportAllocs() 841 t.ResetTimer() 842 for i := 0; i < t.N; i++ { 843 for j := 0; j < len(benchPaths); j++ { 844 l := fflib.NewFFLexer([]byte(exampleJSON)) 845 var found bool 846 outer: 847 for { 848 t := l.Scan() 849 if t == fflib.FFTok_eof { 850 break 851 } 852 if t == fflib.FFTok_string { 853 b, _ := l.CaptureField(t) 854 v := string(b) 855 if found { 856 // break out once we find the value. 857 break outer 858 } 859 switch benchPaths[j] { 860 case "widget.window.name": 861 if v == "\"name\"" { 862 found = true 863 } 864 case "widget.image.hOffset": 865 if v == "\"hOffset\"" { 866 found = true 867 } 868 case "widget.text.onMouseUp": 869 if v == "\"onMouseUp\"" { 870 found = true 871 } 872 } 873 } 874 } 875 if !found { 876 t.Fatal("field not found") 877 } 878 } 879 } 880 t.N *= len(benchPaths) // because we are running against 3 paths 881 } 882 883 func BenchmarkEasyJSONLexer(t *testing.B) { 884 skipCC := func(l *jlexer.Lexer, n int) { 885 for i := 0; i < n; i++ { 886 l.Skip() 887 l.WantColon() 888 l.Skip() 889 l.WantComma() 890 } 891 } 892 skipGroup := func(l *jlexer.Lexer, n int) { 893 l.WantColon() 894 l.Delim('{') 895 skipCC(l, n) 896 l.Delim('}') 897 l.WantComma() 898 } 899 t.ReportAllocs() 900 t.ResetTimer() 901 for i := 0; i < t.N; i++ { 902 for j := 0; j < len(benchPaths); j++ { 903 l := &jlexer.Lexer{Data: []byte(exampleJSON)} 904 l.Delim('{') 905 if l.String() == "widget" { 906 l.WantColon() 907 l.Delim('{') 908 switch benchPaths[j] { 909 case "widget.window.name": 910 skipCC(l, 1) 911 if l.String() == "window" { 912 l.WantColon() 913 l.Delim('{') 914 skipCC(l, 1) 915 if l.String() == "name" { 916 l.WantColon() 917 if l.String() == "" { 918 t.Fatal("did not find the value") 919 } 920 } 921 } 922 case "widget.image.hOffset": 923 skipCC(l, 1) 924 if l.String() == "window" { 925 skipGroup(l, 4) 926 } 927 if l.String() == "image" { 928 l.WantColon() 929 l.Delim('{') 930 skipCC(l, 1) 931 if l.String() == "hOffset" { 932 l.WantColon() 933 if l.Int() == 0 { 934 t.Fatal("did not find the value") 935 } 936 } 937 } 938 case "widget.text.onMouseUp": 939 skipCC(l, 1) 940 if l.String() == "window" { 941 skipGroup(l, 4) 942 } 943 if l.String() == "image" { 944 skipGroup(l, 4) 945 } 946 if l.String() == "text" { 947 l.WantColon() 948 l.Delim('{') 949 skipCC(l, 5) 950 if l.String() == "onMouseUp" { 951 l.WantColon() 952 if l.String() == "" { 953 t.Fatal("did not find the value") 954 } 955 } 956 } 957 } 958 } 959 } 960 } 961 t.N *= len(benchPaths) // because we are running against 3 paths 962 } 963 964 func BenchmarkJSONParserGet(t *testing.B) { 965 data := []byte(exampleJSON) 966 keys := make([][]string, 0, len(benchPaths)) 967 for i := 0; i < len(benchPaths); i++ { 968 keys = append(keys, strings.Split(benchPaths[i], ".")) 969 } 970 t.ReportAllocs() 971 t.ResetTimer() 972 for i := 0; i < t.N; i++ { 973 for j, k := range keys { 974 if j == 1 { 975 // "widget.image.hOffset" is a number 976 v, _ := jsonparser.GetInt(data, k...) 977 if v == 0 { 978 t.Fatal("did not find the value") 979 } 980 } else { 981 // "widget.window.name", 982 // "widget.text.onMouseUp", 983 v, _ := jsonparser.GetString(data, k...) 984 if v == "" { 985 t.Fatal("did not find the value") 986 } 987 } 988 } 989 } 990 t.N *= len(benchPaths) // because we are running against 3 paths 991 } 992 993 var massiveJSON = func() string { 994 var buf bytes.Buffer 995 buf.WriteString("[") 996 for i := 0; i < 100; i++ { 997 if i > 0 { 998 buf.WriteByte(',') 999 } 1000 buf.WriteString(exampleJSON) 1001 } 1002 buf.WriteString("]") 1003 return buf.String() 1004 }() 1005 1006 func BenchmarkConvertNone(t *testing.B) { 1007 json := massiveJSON 1008 t.ReportAllocs() 1009 t.ResetTimer() 1010 for i := 0; i < t.N; i++ { 1011 Get(json, "50.widget.text.onMouseUp") 1012 } 1013 } 1014 func BenchmarkConvertGet(t *testing.B) { 1015 data := []byte(massiveJSON) 1016 t.ReportAllocs() 1017 t.ResetTimer() 1018 for i := 0; i < t.N; i++ { 1019 Get(string(data), "50.widget.text.onMouseUp") 1020 } 1021 } 1022 func BenchmarkConvertGetBytes(t *testing.B) { 1023 data := []byte(massiveJSON) 1024 t.ReportAllocs() 1025 t.ResetTimer() 1026 for i := 0; i < t.N; i++ { 1027 GetBytes(data, "50.widget.text.onMouseUp") 1028 } 1029 }