sjson/sjson_test.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))
}
}
}