mirror of https://github.com/tidwall/sjson.git
364 lines
9.8 KiB
Go
364 lines
9.8 KiB
Go
package sjson
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"math/rand"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestInvalidPaths(t *testing.T) {
|
|
var err error
|
|
_, err = SetRaw(`{"hello":"world"}`, "", `"planet"`)
|
|
if err == nil || err.Error() != "path cannot be empty" {
|
|
t.Fatalf("expecting '%v', got '%v'", "path cannot be empty", err)
|
|
}
|
|
_, err = SetRaw("", "name.last.#", "")
|
|
if err == nil || err.Error() != "array access character not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.last.\\1#", "")
|
|
if err == nil || err.Error() != "array access character not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "array access character not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las?t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.la\\s?t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las*t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
_, err = SetRaw("", "name.las\\a*t", "")
|
|
if err == nil || err.Error() != "wildcard characters not allowed in path" {
|
|
t.Fatalf("expecting '%v', got '%v'", "wildcard characters not allowed in path", err)
|
|
}
|
|
}
|
|
|
|
const (
|
|
setRaw = 1
|
|
setBool = 2
|
|
setInt = 3
|
|
setFloat = 4
|
|
setString = 5
|
|
setDelete = 6
|
|
)
|
|
|
|
func testRaw(t *testing.T, kind int, expect, json, path string, value interface{}) {
|
|
var json2 string
|
|
var err error
|
|
switch kind {
|
|
default:
|
|
json2, err = Set(json, path, value)
|
|
case setRaw:
|
|
json2, err = SetRaw(json, path, value.(string))
|
|
case setDelete:
|
|
json2, err = Delete(json, path)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if json2 != expect {
|
|
t.Fatalf("expected '%v', got '%v'", expect, json2)
|
|
}
|
|
|
|
var json3 []byte
|
|
switch kind {
|
|
default:
|
|
json3, err = SetBytes([]byte(json), path, value)
|
|
case setRaw:
|
|
json3, err = SetRawBytes([]byte(json), path, []byte(value.(string)))
|
|
case setDelete:
|
|
json3, err = DeleteBytes([]byte(json), path)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
} else if string(json3) != expect {
|
|
t.Fatalf("expected '%v', got '%v'", expect, string(json3))
|
|
}
|
|
}
|
|
func TestBasic(t *testing.T) {
|
|
testRaw(t, setRaw, `[{"hiw":"planet","hi":"world"}]`, `[{"hi":"world"}]`, "0.hiw", `"planet"`)
|
|
testRaw(t, setRaw, `[true]`, ``, "0", `true`)
|
|
testRaw(t, setRaw, `[null,true]`, ``, "1", `true`)
|
|
testRaw(t, setRaw, `[1,null,true]`, `[1]`, "2", `true`)
|
|
testRaw(t, setRaw, `[1,true,false]`, `[1,null,false]`, "1", `true`)
|
|
testRaw(t, setRaw,
|
|
`[1,{"hello":"when","this":[0,null,2]},false]`,
|
|
`[1,{"hello":"when","this":[0,1,2]},false]`,
|
|
"1.this.1", `null`)
|
|
testRaw(t, setRaw,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
|
|
`{"a":1,"b":{"hello":"when","this":[0,1,2]},"c":false}`,
|
|
"b.this.1", `null`)
|
|
testRaw(t, setRaw,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2,null,4]},"c":false}`,
|
|
`{"a":1,"b":{"hello":"when","this":[0,null,2]},"c":false}`,
|
|
"b.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"b":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
"b.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`[null,{"this":[null,null,null,null,4]}]`,
|
|
``,
|
|
"1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"1":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
":1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{":1":{"this":[null,null,null,null,4]}}`,
|
|
``,
|
|
"\\:1.this.4", `4`)
|
|
testRaw(t, setRaw,
|
|
`{":\1":{"this":[null,null,null,null,{".HI":4}]}}`,
|
|
``,
|
|
"\\:\\\\1.this.4.\\.HI", `4`)
|
|
testRaw(t, setRaw,
|
|
`{"b":{"this":{"😇":""}}}`,
|
|
``,
|
|
"b.this.😇", `""`)
|
|
testRaw(t, setRaw,
|
|
`[ 1,2 ,3]`,
|
|
` [ 1,2 ] `,
|
|
"-1", `3`)
|
|
testRaw(t, setInt, `[1234]`, ``, `0`, int64(1234))
|
|
testRaw(t, setFloat, `[1234.5]`, ``, `0`, float64(1234.5))
|
|
testRaw(t, setString, `["1234.5"]`, ``, `0`, "1234.5")
|
|
testRaw(t, setBool, `[true]`, ``, `0`, true)
|
|
testRaw(t, setBool, `[null]`, ``, `0`, nil)
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
testRaw(t, setDelete, `[456]`, `[123,456]`, `0`, nil)
|
|
testRaw(t, setDelete, `[123,789]`, `[123,456,789]`, `1`, nil)
|
|
testRaw(t, setDelete, `[123,456]`, `[123,456,789]`, `-1`, nil)
|
|
testRaw(t, setDelete, `{"a":[123,456]}`, `{"a":[123,456,789]}`, `a.-1`, nil)
|
|
testRaw(t, setDelete, `{"and":"another"}`, `{"this":"that","and":"another"}`, `this`, nil)
|
|
testRaw(t, setDelete, `{"this":"that"}`, `{"this":"that","and":"another"}`, `and`, nil)
|
|
testRaw(t, setDelete, `{}`, `{"and":"another"}`, `and`, nil)
|
|
testRaw(t, setDelete, `{"1":"2"}`, `{"1":"2"}`, `3`, nil)
|
|
}
|
|
|
|
// TestRandomData is a fuzzing test that throws random data at SetRaw
|
|
// function looking for panics.
|
|
func TestRandomData(t *testing.T) {
|
|
var lstr string
|
|
defer func() {
|
|
if v := recover(); v != nil {
|
|
println("'" + hex.EncodeToString([]byte(lstr)) + "'")
|
|
println("'" + lstr + "'")
|
|
panic(v)
|
|
}
|
|
}()
|
|
rand.Seed(time.Now().UnixNano())
|
|
b := make([]byte, 200)
|
|
for i := 0; i < 2000000; i++ {
|
|
n, err := rand.Read(b[:rand.Int()%len(b)])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lstr = string(b[:n])
|
|
SetRaw(lstr, "zzzz.zzzz.zzzz", "123")
|
|
}
|
|
}
|
|
|
|
var json = `
|
|
{
|
|
"sha": "d25341478381063d1c76e81b3a52e0592a7c997f",
|
|
"commit": {
|
|
"author": {
|
|
"name": "Tom Tom Anderson",
|
|
"email": "tomtom@anderson.edu",
|
|
"date": "2013-06-22T16:30:59Z"
|
|
},
|
|
"committer": {
|
|
"name": "Tom Tom Anderson",
|
|
"email": "jeffditto@anderson.edu",
|
|
"date": "2013-06-22T16:30:59Z"
|
|
},
|
|
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
|
"tree": {
|
|
"sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
|
|
"url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
|
|
},
|
|
"url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
|
"comment_count": 0
|
|
}
|
|
}
|
|
`
|
|
var path = "commit.committer.email"
|
|
var value = "tomtom@anderson.com"
|
|
var rawValue = `"tomtom@anderson.com"`
|
|
var rawValueBytes = []byte(rawValue)
|
|
var expect = strings.Replace(json, "jeffditto@anderson.edu", "tomtom@anderson.com", 1)
|
|
var jsonBytes = []byte(json)
|
|
var jsonBytes2 = []byte(json)
|
|
var expectBytes = []byte(expect)
|
|
var opts = &Options{Optimistic: true}
|
|
var optsInPlace = &Options{Optimistic: true, ReplaceInPlace: true}
|
|
|
|
func BenchmarkSet(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := Set(json, path, value)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRaw(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetRaw(json, path, rawValue)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetBytes(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetBytes(jsonBytes, path, value)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRawBytes(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetRawBytes(jsonBytes, path, rawValueBytes)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetOptimistic(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetOptions(json, path, value, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetInPlace(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetOptions(json, path, value, optsInPlace)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRawOptimistic(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetRawOptions(json, path, rawValue, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRawInPlace(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetRawOptions(json, path, rawValue, optsInPlace)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if res != expect {
|
|
t.Fatal("expected '%v', got '%v'", expect, res)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetBytesOptimistic(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetBytesOptions(jsonBytes, path, value, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetBytesInPlace(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
copy(jsonBytes2, jsonBytes)
|
|
res, err := SetBytesOptions(jsonBytes2, path, value, optsInPlace)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRawBytesOptimistic(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
res, err := SetRawBytesOptions(jsonBytes, path, rawValueBytes, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSetRawBytesInPlace(t *testing.B) {
|
|
t.ReportAllocs()
|
|
for i := 0; i < t.N; i++ {
|
|
copy(jsonBytes2, jsonBytes)
|
|
res, err := SetRawBytesOptions(jsonBytes2, path, rawValueBytes, optsInPlace)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Compare(res, expectBytes) != 0 {
|
|
t.Fatal("expected '%v', got '%v'", string(expectBytes), string(res))
|
|
}
|
|
}
|
|
}
|