123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- package jsonpointer
- import (
- "compress/gzip"
- "io/ioutil"
- "math/rand"
- "os"
- "reflect"
- "strings"
- "testing"
- "testing/quick"
- "github.com/dustin/gojson"
- )
- var ptests = []struct {
- path string
- exp interface{}
- }{
- {"/foo", []interface{}{"bar", "baz"}},
- {"/foo/0", "bar"},
- {"/", 0.0},
- {"/a~1b", 1.0},
- {"/c%d", 2.0},
- {"/e^f", 3.0},
- {"/g|h", 4.0},
- {"/i\\j", 5.0},
- {"/k\"l", 6.0},
- {"/ ", 7.0},
- {"/m~0n", 8.0},
- {"/g~1n~1r", "has slash, will travel"},
- {"/g/n/r", "where's tito?"},
- }
- func TestFindDecode(t *testing.T) {
- in := []byte(objSrc)
- var fl float64
- if err := FindDecode(in, "/g|h", &fl); err != nil {
- t.Errorf("Failed to decode /g|h: %v", err)
- }
- if fl != 4.0 {
- t.Errorf("Expected 4.0 at /g|h, got %v", fl)
- }
- fl = 0
- if err := FindDecode(in, "/z", &fl); err == nil {
- t.Errorf("Expected failure to decode /z: got %v", fl)
- }
- if err := FindDecode([]byte(`{"a": 1.x35}`), "/a", &fl); err == nil {
- t.Errorf("Expected failure, got %v", fl)
- }
- }
- func TestListPointers(t *testing.T) {
- got, err := ListPointers(nil)
- if err == nil {
- t.Errorf("Expected error on nil input, got %v", got)
- }
- got, err = ListPointers([]byte(`{"x": {"y"}}`))
- if err == nil {
- t.Errorf("Expected error on broken input, got %v", got)
- }
- got, err = ListPointers([]byte(objSrc))
- if err != nil {
- t.Fatalf("Error getting list of pointers: %v", err)
- }
- exp := []string{"", "/foo", "/foo/0", "/foo/1", "/", "/a~1b",
- "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n",
- "/g~1n~1r", "/g", "/g/n", "/g/n/r",
- }
- if !reflect.DeepEqual(exp, got) {
- t.Fatalf("Expected\n%#v\ngot\n%#v", exp, got)
- }
- }
- func TestPointerRoot(t *testing.T) {
- got, err := Find([]byte(objSrc), "")
- if err != nil {
- t.Fatalf("Error finding root: %v", err)
- }
- if !reflect.DeepEqual([]byte(objSrc), got) {
- t.Fatalf("Error finding root, found\n%s\n, wanted\n%s",
- got, objSrc)
- }
- }
- func TestPointerManyRoot(t *testing.T) {
- got, err := FindMany([]byte(objSrc), []string{""})
- if err != nil {
- t.Fatalf("Error finding root: %v", err)
- }
- if !reflect.DeepEqual([]byte(objSrc), got[""]) {
- t.Fatalf("Error finding root, found\n%s\n, wanted\n%s",
- got, objSrc)
- }
- }
- func TestPointerManyBroken(t *testing.T) {
- got, err := FindMany([]byte(`{"a": {"b": "something}}`), []string{"/a/b"})
- if err == nil {
- t.Errorf("Expected error parsing broken JSON, got %v", got)
- }
- }
- func TestPointerMissing(t *testing.T) {
- got, err := Find([]byte(objSrc), "/missing")
- if err != nil {
- t.Fatalf("Error finding missing item: %v", err)
- }
- if got != nil {
- t.Fatalf("Expected nil looking for /missing, got %v",
- got)
- }
- }
- func TestManyPointers(t *testing.T) {
- pointers := []string{}
- exp := map[string]interface{}{}
- for _, test := range ptests {
- pointers = append(pointers, test.path)
- exp[test.path] = test.exp
- }
- rv, err := FindMany([]byte(objSrc), pointers)
- if err != nil {
- t.Fatalf("Error finding many: %v", err)
- }
- got := map[string]interface{}{}
- for k, v := range rv {
- var val interface{}
- err = json.Unmarshal(v, &val)
- if err != nil {
- t.Fatalf("Error unmarshaling %s: %v", v, err)
- }
- got[k] = val
- }
- if !reflect.DeepEqual(got, exp) {
- for k, v := range exp {
- if !reflect.DeepEqual(got[k], v) {
- t.Errorf("At %v, expected %#v, got %#v", k, v, got[k])
- }
- }
- t.Fail()
- }
- }
- func TestManyPointersMissing(t *testing.T) {
- got, err := FindMany([]byte(objSrc), []string{"/missing"})
- if err != nil {
- t.Fatalf("Error finding missing item: %v", err)
- }
- if len(got) != 0 {
- t.Fatalf("Expected empty looking for many /missing, got %v",
- got)
- }
- }
- var badDocs = [][]byte{
- []byte{}, []byte(" "), nil,
- []byte{'{'}, []byte{'['},
- []byte{'}'}, []byte{']'},
- }
- func TestManyPointersBadDoc(t *testing.T) {
- for _, b := range badDocs {
- got, _ := FindMany(b, []string{"/broken"})
- if len(got) > 0 {
- t.Errorf("Expected failure on %v, got %v", b, got)
- }
- }
- }
- func TestPointersBadDoc(t *testing.T) {
- for _, b := range badDocs {
- got, _ := Find(b, "/broken")
- if len(got) > 0 {
- t.Errorf("Expected failure on %s, got %v", b, got)
- }
- }
- }
- func TestPointer(t *testing.T) {
- for _, test := range ptests {
- got, err := Find([]byte(objSrc), test.path)
- var val interface{}
- if err == nil {
- err = json.Unmarshal([]byte(got), &val)
- }
- if err != nil {
- t.Errorf("Got an error on key %v: %v", test.path, err)
- } else if !reflect.DeepEqual(val, test.exp) {
- t.Errorf("On %#v, expected %+v (%T), got %+v (%T)",
- test.path, test.exp, test.exp, val, val)
- } else {
- t.Logf("Success - got %s for %#v", got, test.path)
- }
- }
- }
- func TestPointerCoder(t *testing.T) {
- tests := map[string][]string{
- "/": []string{""},
- "/a": []string{"a"},
- "/a~1b": []string{"a/b"},
- "/m~0n": []string{"m~n"},
- "/ ": []string{" "},
- "/g~1n~1r": []string{"g/n/r"},
- "/g/n/r": []string{"g", "n", "r"},
- }
- for k, v := range tests {
- parsed := parsePointer(k)
- encoded := encodePointer(v)
- if k != encoded {
- t.Errorf("Expected to encode %#v as %#v, got %#v",
- v, k, encoded)
- }
- if !arreq(v, parsed) {
- t.Errorf("Expected to decode %#v as %#v, got %#v",
- k, v, parsed)
- }
- }
- }
- func TestCBugg406(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/pools.json")
- if err != nil {
- t.Fatalf("Error reading pools data: %v", err)
- }
- found, err := Find(data, "/implementationVersion")
- if err != nil {
- t.Fatalf("Failed to find thing: %v", err)
- }
- exp := ` "2.0.0-1976-rel-enterprise"`
- if string(found) != exp {
- t.Fatalf("Expected %q, got %q", exp, found)
- }
- }
- func BenchmarkEncodePointer(b *testing.B) {
- aPath := []string{"a", "ab", "a~0b", "a~1b", "a~0~1~0~1b"}
- for i := 0; i < b.N; i++ {
- encodePointer(aPath)
- }
- }
- func BenchmarkAll(b *testing.B) {
- obj := []byte(objSrc)
- for i := 0; i < b.N; i++ {
- for _, test := range tests {
- Find(obj, test.path)
- }
- }
- }
- func BenchmarkManyPointer(b *testing.B) {
- pointers := []string{}
- for _, test := range ptests {
- pointers = append(pointers, test.path)
- }
- obj := []byte(objSrc)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- FindMany(obj, pointers)
- }
- }
- func TestMustParseInt(t *testing.T) {
- tests := map[string]bool{
- "": true,
- "0": false,
- "13": false,
- }
- for in, out := range tests {
- var panicked bool
- func() {
- defer func() {
- panicked = recover() != nil
- }()
- mustParseInt(in)
- if panicked != out {
- t.Logf("Expected panicked=%v", panicked)
- }
- }()
- }
- }
- func TestFindBrokenJSON(t *testing.T) {
- x, err := Find([]byte(`{]`), "/foo/x")
- if err == nil {
- t.Errorf("Expected error, got %q", x)
- }
- }
- func TestGrokLiteral(t *testing.T) {
- brokenStr := "---broken---"
- tests := []struct {
- in []byte
- exp string
- }{
- {[]byte(`"simple"`), "simple"},
- {[]byte(`"has\nnewline"`), "has\nnewline"},
- {[]byte(`"broken`), brokenStr},
- }
- for _, test := range tests {
- var got string
- func() {
- defer func() {
- if e := recover(); e != nil {
- got = brokenStr
- }
- }()
- got = grokLiteral(test.in)
- }()
- if test.exp != got {
- t.Errorf("Expected %q for %s, got %q",
- test.exp, test.in, got)
- }
- }
- }
- func TestSerieslySample(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/serieslysample.json")
- if err != nil {
- t.Fatalf("Error opening sample file: %v", err)
- }
- tests := []struct {
- pointer string
- exp string
- }{
- {"/kind", "Listing"},
- {"/data/children/0/data/id", "w568e"},
- {"/data/children/0/data/name", "t3_w568e"},
- }
- for _, test := range tests {
- var found string
- err := FindDecode(data, test.pointer, &found)
- if err != nil {
- t.Errorf("Error on %v: %v", test.pointer, err)
- }
- if found != test.exp {
- t.Errorf("Expected %q, got %q", test.exp, found)
- }
- }
- }
- func TestSerieslySampleMany(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/serieslysample.json")
- if err != nil {
- t.Fatalf("Error opening sample file: %v", err)
- }
- keys := []string{"/kind", "/data/children/0/data/id", "/data/children/0/data/name"}
- exp := []string{` "Listing"`, ` "w568e"`, ` "t3_w568e"`}
- found, err := FindMany(data, keys)
- if err != nil {
- t.Fatalf("Error in FindMany: %v", err)
- }
- for i, k := range keys {
- if string(found[k]) != exp[i] {
- t.Errorf("Expected %q on %q, got %q", exp[i], k, found[k])
- }
- }
- }
- func TestSerieslySampleList(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/serieslysample.json")
- if err != nil {
- t.Fatalf("Error opening sample file: %v", err)
- }
- pointers, err := ListPointers(data)
- if err != nil {
- t.Fatalf("Error listing pointers: %v", err)
- }
- exp := 932
- if len(pointers) != exp {
- t.Fatalf("Expected %v pointers, got %v", exp, len(pointers))
- }
- }
- func Test357ListPointers(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/357.json")
- if err != nil {
- t.Fatalf("Error beer-sample brewery 357 data: %v", err)
- }
- exp := []string{"", "/name", "/city", "/state", "/code",
- "/country", "/phone", "/website", "/type", "/updated",
- "/description",
- "/address", "/address/0", "/address2",
- "/address2/0", "/address3", "/address3/0"}
- got, err := ListPointers(data)
- if err != nil {
- t.Fatalf("error listing pointers: %v", err)
- }
- if !reflect.DeepEqual(exp, got) {
- t.Fatalf("Expected\n%#v\ngot\n%#v", exp, got)
- }
- }
- func TestEscape(t *testing.T) {
- tests := []string{
- "/", "~1", "~0", "/~1", "/~1/",
- }
- for _, test := range tests {
- esc := string(escape(test, nil))
- got := unescape(esc)
- if got != test {
- t.Errorf("unescape(escape(%q) [%q]) = %q", test, esc, got)
- }
- }
- tf := func(s chars) bool {
- uns := unescape(string(s))
- got := string(escape(uns, nil))
- return got == string(s)
- }
- quick.Check(tf, nil)
- }
- func TestUnescape(t *testing.T) {
- tests := []struct {
- in, exp string
- }{
- {"", ""},
- {"/", "/"},
- {"/thing", "/thing"},
- {"~0", "~"},
- {"~1", "/"},
- {"~2", "~2"},
- {"~", "~"},
- {"thing~", "thing~"},
- }
- for _, test := range tests {
- got := string(unescape(test.in))
- if got != test.exp {
- t.Errorf("on %q, got %q, wanted %q", test.in, got, test.exp)
- }
- }
- }
- var codeJSON []byte
- func init() {
- f, err := os.Open("testdata/code.json.gz")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- gz, err := gzip.NewReader(f)
- if err != nil {
- panic(err)
- }
- data, err := ioutil.ReadAll(gz)
- if err != nil {
- panic(err)
- }
- codeJSON = data
- }
- func BenchmarkLarge3Key(b *testing.B) {
- keys := []string{
- "/tree/kids/0/kids/0/name",
- "/tree/kids/0/name",
- "/tree/kids/0/kids/0/kids/0/kids/0/kids/0/name",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- found, err := FindMany(codeJSON, keys)
- if err != nil || len(found) != 3 {
- b.Fatalf("Didn't find all the things from %v/%v",
- found, err)
- }
- }
- }
- func BenchmarkLargeShallow(b *testing.B) {
- keys := []string{
- "/tree/kids/0/kids/0/kids/1/kids/1/kids/3/name",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- found, err := FindMany(codeJSON, keys)
- if err != nil || len(found) != 1 {
- b.Fatalf("Didn't find all the things: %v/%v",
- found, err)
- }
- }
- }
- func BenchmarkLargeMissing(b *testing.B) {
- keys := []string{
- "/this/does/not/exist",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- found, err := FindMany(codeJSON, keys)
- if err != nil || len(found) != 0 {
- b.Fatalf("Didn't find all the things: %v/%v",
- found, err)
- }
- }
- }
- func BenchmarkLargeIdentity(b *testing.B) {
- keys := []string{
- "",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- found, err := FindMany(codeJSON, keys)
- if err != nil || len(found) != 1 {
- b.Fatalf("Didn't find all the things: %v/%v",
- found, err)
- }
- }
- }
- func BenchmarkLargeBest(b *testing.B) {
- keys := []string{
- "/tree/name",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- found, err := FindMany(codeJSON, keys)
- if err != nil || len(found) != 1 {
- b.Fatalf("Didn't find all the things: %v/%v",
- found, err)
- }
- }
- }
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01233456789/~."
- type chars string
- func (c chars) Generate(rand *rand.Rand, _ int) reflect.Value {
- size := rand.Intn(128)
- var o []byte
- for i := 0; i < size; i++ {
- o = append(o, alphabet[rand.Intn(len(alphabet))])
- }
- s := chars(escape(string(o), nil))
- return reflect.ValueOf(s)
- }
- // unescape unescapes a tilde escaped string.
- //
- // It's dumb looking, but it benches faster than strings.NewReplacer
- func oldunescape(s string) string {
- return strings.Replace(strings.Replace(s, "~1", "/", -1), "~0", "~", -1)
- }
- func TestNewEscaper(t *testing.T) {
- of := func(in chars) string {
- return oldunescape(string(in))
- }
- nf := func(in chars) string {
- return unescape(string(in))
- }
- if err := quick.CheckEqual(of, nf, nil); err != nil {
- t.Errorf("quickcheck failure: %v", err)
- }
- }
- func BenchmarkLargeMap(b *testing.B) {
- keys := []string{
- "/tree/kids/0/kids/0/kids/0/kids/0/kids/0/name",
- }
- b.SetBytes(int64(len(codeJSON)))
- for i := 0; i < b.N; i++ {
- m := map[string]interface{}{}
- err := json.Unmarshal(codeJSON, &m)
- if err != nil {
- b.Fatalf("Error parsing JSON: %v", err)
- }
- Get(m, keys[0])
- }
- }
- const (
- tildeTestKey = "/name~0contained"
- slashTestKey = "/name~1contained"
- twoTestKey = "/name~1cont~0ned"
- )
- func testDoubleReplacer(s string) string {
- return unescape(s)
- }
- func BenchmarkReplacerSlash(b *testing.B) {
- r := strings.NewReplacer("~1", "/", "~0", "~")
- for i := 0; i < b.N; i++ {
- r.Replace(slashTestKey)
- }
- }
- func BenchmarkReplacerTilde(b *testing.B) {
- r := strings.NewReplacer("~1", "/", "~0", "~")
- for i := 0; i < b.N; i++ {
- r.Replace(tildeTestKey)
- }
- }
- func BenchmarkDblReplacerSlash(b *testing.B) {
- for i := 0; i < b.N; i++ {
- testDoubleReplacer(slashTestKey)
- }
- }
- func BenchmarkDblReplacerTilde(b *testing.B) {
- for i := 0; i < b.N; i++ {
- testDoubleReplacer(tildeTestKey)
- }
- }
- func BenchmarkDblReplacerTwo(b *testing.B) {
- for i := 0; i < b.N; i++ {
- testDoubleReplacer(twoTestKey)
- }
- }
|