123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668 |
- package jsonparser
- import (
- "bytes"
- "fmt"
- _ "fmt"
- "reflect"
- "testing"
- )
- // Set it to non-empty value if want to run only specific test
- var activeTest = ""
- func toArray(data []byte) (result [][]byte) {
- ArrayEach(data, func(value []byte, dataType ValueType, offset int, err error) {
- result = append(result, value)
- })
- return
- }
- func toStringArray(data []byte) (result []string) {
- ArrayEach(data, func(value []byte, dataType ValueType, offset int, err error) {
- result = append(result, string(value))
- })
- return
- }
- type GetTest struct {
- desc string
- json string
- path []string
- isErr bool
- isFound bool
- data interface{}
- }
- type SetTest struct {
- desc string
- json string
- setData string
- path []string
- isErr bool
- isFound bool
- data interface{}
- }
- type DeleteTest struct {
- desc string
- json string
- path []string
- data interface{}
- }
- var deleteTests = []DeleteTest{
- {
- desc: "Delete test key",
- json: `{"test":"input"}`,
- path: []string{"test"},
- data: `{}`,
- },
- {
- desc: "Delete object",
- json: `{"test":"input"}`,
- path: []string{},
- data: ``,
- },
- {
- desc: "Delete a nested object",
- json: `{"test":"input","new.field":{"key": "new object"}}`,
- path: []string{"new.field", "key"},
- data: `{"test":"input","new.field":{}}`,
- },
- {
- desc: "Deleting a key that doesn't exist should return the same object",
- json: `{"test":"input"}`,
- path: []string{"test2"},
- data: `{"test":"input"}`,
- },
- {
- desc: "Delete object in an array",
- json: `{"test":[{"key":"val-obj1"}]}`,
- path: []string{"test", "[0]"},
- data: `{"test":[]}`,
- },
- {
- desc: "Deleting a object in an array that doesn't exists should return the same object",
- json: `{"test":[{"key":"val-obj1"}]}`,
- path: []string{"test", "[1]"},
- data: `{"test":[{"key":"val-obj1"}]}`,
- },
- {
- desc: "Delete a complex object in a nested array",
- json: `{"test":[{"key":[{"innerKey":"innerKeyValue"}]}]}`,
- path: []string{"test", "[0]", "key", "[0]"},
- data: `{"test":[{"key":[]}]}`,
- },
- {
- desc: "Delete known key (simple type within nested array)",
- json: `{"test":[{"key":["innerKey"]}]}`,
- path: []string{"test", "[0]", "key", "[0]"},
- data: `{"test":[{"key":[]}]}`,
- },
- {
- desc: "Delete in empty json",
- json: `{}`,
- path: []string{},
- data: ``,
- },
- {
- desc: "Delete empty array",
- json: `[]`,
- path: []string{},
- data: ``,
- },
- {
- desc: "Deleting non json should return the same value",
- json: `1.323`,
- path: []string{"foo"},
- data: `1.323`,
- },
- {
- desc: "Delete known key (top level array)",
- json: `[{"key":"val-obj1"}]`,
- path: []string{"[0]"},
- data: `[]`,
- },
- { // This test deletes the key instead of returning a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: `malformed with trailing whitespace`,
- json: `{"a":1 `,
- path: []string{"a"},
- data: `{ `,
- },
- { // This test dels the key instead of returning a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: "malformed 'colon chain', delete b",
- json: `{"a":"b":"c"}`,
- path: []string{"b"},
- data: `{"a":}`,
- },
- {
- desc: "Delete object without inner array",
- json: `{"a": {"b": 1}, "b": 2}`,
- path: []string{"b"},
- data: `{"a": {"b": 1}}`,
- },
- {
- desc: "Delete object without inner array",
- json: `{"a": [{"b": 1}], "b": 2}`,
- path: []string{"b"},
- data: `{"a": [{"b": 1}]}`,
- },
- {
- desc: "Delete object without inner array",
- json: `{"a": {"c": {"b": 3}, "b": 1}, "b": 2}`,
- path: []string{"a", "b"},
- data: `{"a": {"c": {"b": 3}}, "b": 2}`,
- },
- {
- desc: "Delete object without inner array",
- json: `{"a": [{"c": {"b": 3}, "b": 1}], "b": 2}`,
- path: []string{"a", "[0]", "b"},
- data: `{"a": [{"c": {"b": 3}}], "b": 2}`,
- },
- {
- desc: "Remove trailing comma if last object is deleted",
- json: `{"a": "1", "b": "2"}`,
- path: []string{"b"},
- data: `{"a": "1"}`,
- },
- {
- desc: "Correctly delete first element with space-comma",
- json: `{"a": "1" ,"b": "2" }`,
- path: []string{"a"},
- data: `{"b": "2" }`,
- },
- {
- desc: "Correctly delete middle element with space-comma",
- json: `{"a": "1" ,"b": "2" , "c": 3}`,
- path: []string{"b"},
- data: `{"a": "1" , "c": 3}`,
- },
- }
- var setTests = []SetTest{
- {
- desc: "set unknown key (string)",
- json: `{"test":"input"}`,
- isFound: true,
- path: []string{"new.field"},
- setData: `"new value"`,
- data: `{"test":"input","new.field":"new value"}`,
- },
- {
- desc: "set known key (string)",
- json: `{"test":"input"}`,
- isFound: true,
- path: []string{"test"},
- setData: `"new value"`,
- data: `{"test":"new value"}`,
- },
- {
- desc: "set unknown key (object)",
- json: `{"test":"input"}`,
- isFound: true,
- path: []string{"new.field"},
- setData: `{"key": "new object"}`,
- data: `{"test":"input","new.field":{"key": "new object"}}`,
- },
- {
- desc: "set known key (object)",
- json: `{"test":"input"}`,
- isFound: true,
- path: []string{"test"},
- setData: `{"key": "new object"}`,
- data: `{"test":{"key": "new object"}}`,
- },
- {
- desc: "set known key (object within array)",
- json: `{"test":[{"key":"val-obj1"}]}`,
- isFound: true,
- path: []string{"test", "[0]"},
- setData: `{"key":"new object"}`,
- data: `{"test":[{"key":"new object"}]}`,
- },
- {
- desc: "set unknown key (replace object)",
- json: `{"test":[{"key":"val-obj1"}]}`,
- isFound: true,
- path: []string{"test", "newKey"},
- setData: `"new object"`,
- data: `{"test":{"newKey":"new object"}}`,
- },
- {
- desc: "set unknown key (complex object within nested array)",
- json: `{"test":[{"key":[{"innerKey":"innerKeyValue"}]}]}`,
- isFound: true,
- path: []string{"test", "[0]", "key", "[0]", "newInnerKey"},
- setData: `{"key":"new object"}`,
- data: `{"test":[{"key":[{"innerKey":"innerKeyValue","newInnerKey":{"key":"new object"}}]}]}`,
- },
- {
- desc: "set known key (complex object within nested array)",
- json: `{"test":[{"key":[{"innerKey":"innerKeyValue"}]}]}`,
- isFound: true,
- path: []string{"test", "[0]", "key", "[0]", "innerKey"},
- setData: `{"key":"new object"}`,
- data: `{"test":[{"key":[{"innerKey":{"key":"new object"}}]}]}`,
- },
- {
- desc: "set unknown key (object, partial subtree exists)",
- json: `{"test":{"input":"output"}}`,
- isFound: true,
- path: []string{"test", "new.field"},
- setData: `{"key":"new object"}`,
- data: `{"test":{"input":"output","new.field":{"key":"new object"}}}`,
- },
- {
- desc: "set unknown key (object, empty partial subtree exists)",
- json: `{"test":{}}`,
- isFound: true,
- path: []string{"test", "new.field"},
- setData: `{"key":"new object"}`,
- data: `{"test":{"new.field":{"key":"new object"}}}`,
- },
- {
- desc: "set unknown key (object, no subtree exists)",
- json: `{"test":"input"}`,
- isFound: true,
- path: []string{"new.field", "nested", "value"},
- setData: `{"key": "new object"}`,
- data: `{"test":"input","new.field":{"nested":{"value":{"key": "new object"}}}}`,
- },
- {
- desc: "set in empty json",
- json: `{}`,
- isFound: true,
- path: []string{"foo"},
- setData: `"null"`,
- data: `{"foo":"null"}`,
- },
- {
- desc: "set subtree in empty json",
- json: `{}`,
- isFound: true,
- path: []string{"foo", "bar"},
- setData: `"null"`,
- data: `{"foo":{"bar":"null"}}`,
- },
- {
- desc: "set in empty string - not found",
- json: ``,
- isFound: false,
- path: []string{"foo"},
- setData: `"null"`,
- data: ``,
- },
- {
- desc: "set in Number - not found",
- json: `1.323`,
- isFound: false,
- path: []string{"foo"},
- setData: `"null"`,
- data: `1.323`,
- },
- {
- desc: "set known key (top level array)",
- json: `[{"key":"val-obj1"}]`,
- isFound: true,
- path: []string{"[0]", "key"},
- setData: `"new object"`,
- data: `[{"key":"new object"}]`,
- },
- {
- desc: "set unknown key (trailing whitespace)",
- json: `{"key":"val-obj1"} `,
- isFound: true,
- path: []string{"alt-key"},
- setData: `"new object"`,
- data: `{"key":"val-obj1","alt-key":"new object"} `,
- },
- { // This test sets the key instead of returning a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: `malformed with trailing whitespace`,
- json: `{"a":1 `,
- path: []string{"a"},
- setData: `2`,
- isFound: true,
- data: `{"a":2 `,
- },
- { // This test sets the key instead of returning a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: "malformed 'colon chain', set second string",
- json: `{"a":"b":"c"}`,
- path: []string{"b"},
- setData: `"d"`,
- isFound: true,
- data: `{"a":"b":"d"}`,
- },
- {
- desc: "set indexed path to object on empty JSON",
- json: `{}`,
- path: []string{"top", "[0]", "middle", "[0]", "bottom"},
- setData: `"value"`,
- isFound: true,
- data: `{"top":[{"middle":[{"bottom":"value"}]}]}`,
- },
- {
- desc: "set indexed path on existing object with object",
- json: `{"top":[{"middle":[]}]}`,
- path: []string{"top", "[0]", "middle", "[0]", "bottom"},
- setData: `"value"`,
- isFound: true,
- data: `{"top":[{"middle":[{"bottom":"value"}]}]}`,
- },
- {
- desc: "set indexed path on existing object with value",
- json: `{"top":[{"middle":[]}]}`,
- path: []string{"top", "[0]", "middle", "[0]"},
- setData: `"value"`,
- isFound: true,
- data: `{"top":[{"middle":["value"]}]}`,
- },
- {
- desc: "set indexed path on empty object with value",
- json: `{}`,
- path: []string{"top", "[0]", "middle", "[0]"},
- setData: `"value"`,
- isFound: true,
- data: `{"top":[{"middle":["value"]}]}`,
- },
- {
- desc: "set indexed path on object with existing array",
- json: `{"top":["one", "two", "three"]}`,
- path: []string{"top", "[2]"},
- setData: `"value"`,
- isFound: true,
- data: `{"top":["one", "two", "value"]}`,
- },
- }
- var getTests = []GetTest{
- // Trivial tests
- {
- desc: "read string",
- json: `""`,
- isFound: true,
- data: ``,
- },
- {
- desc: "read number",
- json: `0`,
- isFound: true,
- data: `0`,
- },
- {
- desc: "read object",
- json: `{}`,
- isFound: true,
- data: `{}`,
- },
- {
- desc: "read array",
- json: `[]`,
- isFound: true,
- data: `[]`,
- },
- {
- desc: "read boolean",
- json: `true`,
- isFound: true,
- data: `true`,
- },
- // Found key tests
- {
- desc: "handling multiple nested keys with same name",
- json: `{"a":[{"b":1},{"b":2},3],"c":{"c":[1,2]}} }`,
- path: []string{"c", "c"},
- isFound: true,
- data: `[1,2]`,
- },
- {
- desc: "read basic key",
- json: `{"a":"b"}`,
- path: []string{"a"},
- isFound: true,
- data: `b`,
- },
- {
- desc: "read basic key with space",
- json: `{"a": "b"}`,
- path: []string{"a"},
- isFound: true,
- data: `b`,
- },
- {
- desc: "read composite key",
- json: `{"a": { "b":{"c":"d" }}}`,
- path: []string{"a", "b", "c"},
- isFound: true,
- data: `d`,
- },
- {
- desc: `read numberic value as string`,
- json: `{"a": "b", "c": 1}`,
- path: []string{"c"},
- isFound: true,
- data: `1`,
- },
- {
- desc: `handle multiple nested keys with same name`,
- json: `{"a":[{"b":1},{"b":2},3],"c":{"c":[1,2]}} }`,
- path: []string{"c", "c"},
- isFound: true,
- data: `[1,2]`,
- },
- {
- desc: `read string values with quotes`,
- json: `{"a": "string\"with\"quotes"}`,
- path: []string{"a"},
- isFound: true,
- data: `string\"with\"quotes`,
- },
- {
- desc: `read object`,
- json: `{"a": { "b":{"c":"d" }}}`,
- path: []string{"a", "b"},
- isFound: true,
- data: `{"c":"d" }`,
- },
- {
- desc: `empty path`,
- json: `{"c":"d" }`,
- path: []string{},
- isFound: true,
- data: `{"c":"d" }`,
- },
- {
- desc: `formatted JSON value`,
- json: "{\n \"a\": \"b\"\n}",
- path: []string{"a"},
- isFound: true,
- data: `b`,
- },
- {
- desc: `formatted JSON value 2`,
- json: "{\n \"a\":\n {\n\"b\":\n {\"c\":\"d\",\n\"e\": \"f\"}\n}\n}",
- path: []string{"a", "b"},
- isFound: true,
- data: "{\"c\":\"d\",\n\"e\": \"f\"}",
- },
- {
- desc: `whitespace`,
- json: " \n\r\t{ \n\r\t\"whitespace\" \n\r\t: \n\r\t333 \n\r\t} \n\r\t",
- path: []string{"whitespace"},
- isFound: true,
- data: "333",
- },
- {
- desc: `escaped backslash quote`,
- json: `{"a": "\\\""}`,
- path: []string{"a"},
- isFound: true,
- data: `\\\"`,
- },
- {
- desc: `unescaped backslash quote`,
- json: `{"a": "\\"}`,
- path: []string{"a"},
- isFound: true,
- data: `\\`,
- },
- {
- desc: `unicode in JSON`,
- json: `{"a": "15°C"}`,
- path: []string{"a"},
- isFound: true,
- data: `15°C`,
- },
- {
- desc: `no padding + nested`,
- json: `{"a":{"a":"1"},"b":2}`,
- path: []string{"b"},
- isFound: true,
- data: `2`,
- },
- {
- desc: `no padding + nested + array`,
- json: `{"a":{"b":[1,2]},"c":3}`,
- path: []string{"c"},
- isFound: true,
- data: `3`,
- },
- {
- desc: `empty key`,
- json: `{"":{"":{"":true}}}`,
- path: []string{"", "", ""},
- isFound: true,
- data: `true`,
- },
- // Escaped key tests
- {
- desc: `key with simple escape`,
- json: `{"a\\b":1}`,
- path: []string{"a\\b"},
- isFound: true,
- data: `1`,
- },
- {
- desc: `key and value with whitespace escapes`,
- json: `{"key\b\f\n\r\tkey":"value\b\f\n\r\tvalue"}`,
- path: []string{"key\b\f\n\r\tkey"},
- isFound: true,
- data: `value\b\f\n\r\tvalue`, // value is not unescaped since this is Get(), but the key should work correctly
- },
- {
- desc: `key with Unicode escape`,
- json: `{"a\u00B0b":1}`,
- path: []string{"a\u00B0b"},
- isFound: true,
- data: `1`,
- },
- {
- desc: `key with complex escape`,
- json: `{"a\uD83D\uDE03b":1}`,
- path: []string{"a\U0001F603b"},
- isFound: true,
- data: `1`,
- },
- { // This test returns a match instead of a parse error, as checking for the malformed JSON would reduce performance
- desc: `malformed with trailing whitespace`,
- json: `{"a":1 `,
- path: []string{"a"},
- isFound: true,
- data: `1`,
- },
- { // This test returns a match instead of a parse error, as checking for the malformed JSON would reduce performance
- desc: `malformed with wrong closing bracket`,
- json: `{"a":1]`,
- path: []string{"a"},
- isFound: true,
- data: `1`,
- },
- // Not found key tests
- {
- desc: `empty input`,
- json: ``,
- path: []string{"a"},
- isFound: false,
- },
- {
- desc: "non-existent key 1",
- json: `{"a":"b"}`,
- path: []string{"c"},
- isFound: false,
- },
- {
- desc: "non-existent key 2",
- json: `{"a":"b"}`,
- path: []string{"b"},
- isFound: false,
- },
- {
- desc: "non-existent key 3",
- json: `{"aa":"b"}`,
- path: []string{"a"},
- isFound: false,
- },
- {
- desc: "apply scope of parent when search for nested key",
- json: `{"a": { "b": 1}, "c": 2 }`,
- path: []string{"a", "b", "c"},
- isFound: false,
- },
- {
- desc: `apply scope to key level`,
- json: `{"a": { "b": 1}, "c": 2 }`,
- path: []string{"b"},
- isFound: false,
- },
- {
- desc: `handle escaped quote in key name in JSON`,
- json: `{"key\"key": 1}`,
- path: []string{"key"},
- isFound: false,
- },
- {
- desc: "handling multiple keys with different name",
- json: `{"a":{"a":1},"b":{"a":3,"c":[1,2]}}`,
- path: []string{"a", "c"},
- isFound: false,
- },
- {
- desc: "handling nested json",
- json: `{"a":{"b":{"c":1},"d":4}}`,
- path: []string{"a", "d"},
- isFound: true,
- data: `4`,
- },
- // Error/invalid tests
- {
- desc: `handle escaped quote in key name in JSON`,
- json: `{"key\"key": 1}`,
- path: []string{"key"},
- isFound: false,
- },
- {
- desc: `missing closing brace, but can still find key`,
- json: `{"a":"b"`,
- path: []string{"a"},
- isFound: true,
- data: `b`,
- },
- {
- desc: `missing value closing quote`,
- json: `{"a":"b`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `missing value closing curly brace`,
- json: `{"a": { "b": "c"`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `missing value closing square bracket`,
- json: `{"a": [1, 2, 3 }`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `missing value 1`,
- json: `{"a":`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `missing value 2`,
- json: `{"a": `,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `missing value 3`,
- json: `{"a":}`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `malformed array (no closing brace)`,
- json: `{"a":[, "b":123}`,
- path: []string{"b"},
- isFound: false,
- },
- { // Issue #81
- desc: `missing key in object in array`,
- json: `{"p":{"a":[{"u":"abc","t":"th"}]}}`,
- path: []string{"p", "a", "[0]", "x"},
- isFound: false,
- },
- { // Issue #81 counter test
- desc: `existing key in object in array`,
- json: `{"p":{"a":[{"u":"abc","t":"th"}]}}`,
- path: []string{"p", "a", "[0]", "u"},
- isFound: true,
- data: "abc",
- },
- { // This test returns not found instead of a parse error, as checking for the malformed JSON would reduce performance
- desc: "malformed key (followed by comma followed by colon)",
- json: `{"a",:1}`,
- path: []string{"a"},
- isFound: false,
- },
- { // This test returns a match instead of a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: "malformed 'colon chain', lookup first string",
- json: `{"a":"b":"c"}`,
- path: []string{"a"},
- isFound: true,
- data: "b",
- },
- { // This test returns a match instead of a parse error, as checking for the malformed JSON would reduce performance (this is not ideal)
- desc: "malformed 'colon chain', lookup second string",
- json: `{"a":"b":"c"}`,
- path: []string{"b"},
- isFound: true,
- data: "c",
- },
- // Array index paths
- {
- desc: "last key in path is index",
- json: `{"a":[{"b":1},{"b":"2"}, 3],"c":{"c":[1,2]}}`,
- path: []string{"a", "[1]"},
- isFound: true,
- data: `{"b":"2"}`,
- },
- {
- desc: "get string from array",
- json: `{"a":[{"b":1},"foo", 3],"c":{"c":[1,2]}}`,
- path: []string{"a", "[1]"},
- isFound: true,
- data: "foo",
- },
- {
- desc: "key in path is index",
- json: `{"a":[{"b":"1"},{"b":"2"},3],"c":{"c":[1,2]}}`,
- path: []string{"a", "[0]", "b"},
- isFound: true,
- data: `1`,
- },
- {
- desc: "last key in path is an index to value in array (formatted json)",
- json: `{
- "a": [
- {
- "b": 1
- },
- {"b":"2"},
- 3
- ],
- "c": {
- "c": [
- 1,
- 2
- ]
- }
- }`,
- path: []string{"a", "[1]"},
- isFound: true,
- data: `{"b":"2"}`,
- },
- {
- desc: "key in path is index (formatted json)",
- json: `{
- "a": [
- {"b": 1},
- {"b": "2"},
- 3
- ],
- "c": {
- "c": [
- 1,
- 2
- ]
- }
- }`,
- path: []string{"a", "[0]", "b"},
- isFound: true,
- data: `1`,
- },
- }
- var getIntTests = []GetTest{
- {
- desc: `read numeric value as number`,
- json: `{"a": "b", "c": 1}`,
- path: []string{"c"},
- isFound: true,
- data: int64(1),
- },
- {
- desc: `read numeric value as number in formatted JSON`,
- json: "{\"a\": \"b\", \"c\": 1 \n}",
- path: []string{"c"},
- isFound: true,
- data: int64(1),
- },
- }
- var getFloatTests = []GetTest{
- {
- desc: `read numeric value as number`,
- json: `{"a": "b", "c": 1.123}`,
- path: []string{"c"},
- isFound: true,
- data: float64(1.123),
- },
- {
- desc: `read numeric value as number in formatted JSON`,
- json: "{\"a\": \"b\", \"c\": 23.41323 \n}",
- path: []string{"c"},
- isFound: true,
- data: float64(23.41323),
- },
- }
- var getStringTests = []GetTest{
- {
- desc: `Translate Unicode symbols`,
- json: `{"c": "test"}`,
- path: []string{"c"},
- isFound: true,
- data: `test`,
- },
- {
- desc: `Translate Unicode symbols`,
- json: `{"c": "15\u00b0C"}`,
- path: []string{"c"},
- isFound: true,
- data: `15°C`,
- },
- {
- desc: `Translate supplementary Unicode symbols`,
- json: `{"c": "\uD83D\uDE03"}`, // Smiley face (UTF16 surrogate pair)
- path: []string{"c"},
- isFound: true,
- data: "\U0001F603", // Smiley face
- },
- {
- desc: `Translate escape symbols`,
- json: `{"c": "\\\""}`,
- path: []string{"c"},
- isFound: true,
- data: `\"`,
- },
- {
- desc: `key and value with whitespace escapes`,
- json: `{"key\b\f\n\r\tkey":"value\b\f\n\r\tvalue"}`,
- path: []string{"key\b\f\n\r\tkey"},
- isFound: true,
- data: "value\b\f\n\r\tvalue", // value is unescaped since this is GetString()
- },
- }
- var getBoolTests = []GetTest{
- {
- desc: `read boolean true as boolean`,
- json: `{"a": "b", "c": true}`,
- path: []string{"c"},
- isFound: true,
- data: true,
- },
- {
- desc: `boolean true in formatted JSON`,
- json: "{\"a\": \"b\", \"c\": true \n}",
- path: []string{"c"},
- isFound: true,
- data: true,
- },
- {
- desc: `read boolean false as boolean`,
- json: `{"a": "b", "c": false}`,
- path: []string{"c"},
- isFound: true,
- data: false,
- },
- {
- desc: `boolean true in formatted JSON`,
- json: "{\"a\": \"b\", \"c\": false \n}",
- path: []string{"c"},
- isFound: true,
- data: false,
- },
- {
- desc: `read fake boolean true`,
- json: `{"a": txyz}`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `read fake boolean false`,
- json: `{"a": fwxyz}`,
- path: []string{"a"},
- isErr: true,
- },
- {
- desc: `read boolean true with whitespace and another key`,
- json: "{\r\t\n \"a\"\r\t\n :\r\t\n true\r\t\n ,\r\t\n \"b\": 1}",
- path: []string{"a"},
- isFound: true,
- data: true,
- },
- }
- var getArrayTests = []GetTest{
- {
- desc: `read array of simple values`,
- json: `{"a": { "b":[1,2,3,4]}}`,
- path: []string{"a", "b"},
- isFound: true,
- data: []string{`1`, `2`, `3`, `4`},
- },
- {
- desc: `read array via empty path`,
- json: `[1,2,3,4]`,
- path: []string{},
- isFound: true,
- data: []string{`1`, `2`, `3`, `4`},
- },
- {
- desc: `read array of objects`,
- json: `{"a": { "b":[{"x":1},{"x":2},{"x":3},{"x":4}]}}`,
- path: []string{"a", "b"},
- isFound: true,
- data: []string{`{"x":1}`, `{"x":2}`, `{"x":3}`, `{"x":4}`},
- },
- {
- desc: `read nested array`,
- json: `{"a": [[[1]],[[2]]]}`,
- path: []string{"a"},
- isFound: true,
- data: []string{`[[1]]`, `[[2]]`},
- },
- }
- // checkFoundAndNoError checks the dataType and error return from Get*() against the test case expectations.
- // Returns true the test should proceed to checking the actual data returned from Get*(), or false if the test is finished.
- func getTestCheckFoundAndNoError(t *testing.T, testKind string, test GetTest, jtype ValueType, value interface{}, err error) bool {
- isFound := (err != KeyPathNotFoundError)
- isErr := (err != nil && err != KeyPathNotFoundError)
- if test.isErr != isErr {
- // If the call didn't match the error expectation, fail
- t.Errorf("%s test '%s' isErr mismatch: expected %t, obtained %t (err %v). Value: %v", testKind, test.desc, test.isErr, isErr, err, value)
- return false
- } else if isErr {
- // Else, if there was an error, don't fail and don't check isFound or the value
- return false
- } else if test.isFound != isFound {
- // Else, if the call didn't match the is-found expectation, fail
- t.Errorf("%s test '%s' isFound mismatch: expected %t, obtained %t", testKind, test.desc, test.isFound, isFound)
- return false
- } else if !isFound {
- // Else, if no value was found, don't fail and don't check the value
- return false
- } else {
- // Else, there was no error and a value was found, so check the value
- return true
- }
- }
- func runGetTests(t *testing.T, testKind string, tests []GetTest, runner func(GetTest) (interface{}, ValueType, error), resultChecker func(GetTest, interface{}) (bool, interface{})) {
- for _, test := range tests {
- if activeTest != "" && test.desc != activeTest {
- continue
- }
- fmt.Println("Running:", test.desc)
- value, dataType, err := runner(test)
- if getTestCheckFoundAndNoError(t, testKind, test, dataType, value, err) {
- if test.data == nil {
- t.Errorf("MALFORMED TEST: %v", test)
- continue
- }
- if ok, expected := resultChecker(test, value); !ok {
- if expectedBytes, ok := expected.([]byte); ok {
- expected = string(expectedBytes)
- }
- if valueBytes, ok := value.([]byte); ok {
- value = string(valueBytes)
- }
- t.Errorf("%s test '%s' expected to return value %v, but did returned %v instead", testKind, test.desc, expected, value)
- }
- }
- }
- }
- func setTestCheckFoundAndNoError(t *testing.T, testKind string, test SetTest, value interface{}, err error) bool {
- isFound := (err != KeyPathNotFoundError)
- isErr := (err != nil && err != KeyPathNotFoundError)
- if test.isErr != isErr {
- // If the call didn't match the error expectation, fail
- t.Errorf("%s test '%s' isErr mismatch: expected %t, obtained %t (err %v). Value: %v", testKind, test.desc, test.isErr, isErr, err, value)
- return false
- } else if isErr {
- // Else, if there was an error, don't fail and don't check isFound or the value
- return false
- } else if test.isFound != isFound {
- // Else, if the call didn't match the is-found expectation, fail
- t.Errorf("%s test '%s' isFound mismatch: expected %t, obtained %t", testKind, test.desc, test.isFound, isFound)
- return false
- } else if !isFound {
- // Else, if no value was found, don't fail and don't check the value
- return false
- } else {
- // Else, there was no error and a value was found, so check the value
- return true
- }
- }
- func runSetTests(t *testing.T, testKind string, tests []SetTest, runner func(SetTest) (interface{}, ValueType, error), resultChecker func(SetTest, interface{}) (bool, interface{})) {
- for _, test := range tests {
- if activeTest != "" && test.desc != activeTest {
- continue
- }
- fmt.Println("Running:", test.desc)
- value, _, err := runner(test)
- if setTestCheckFoundAndNoError(t, testKind, test, value, err) {
- if test.data == nil {
- t.Errorf("MALFORMED TEST: %v", test)
- continue
- }
- if string(value.([]byte)) != test.data {
- t.Errorf("Unexpected result on %s test '%s'", testKind, test.desc)
- t.Log("Got: ", string(value.([]byte)))
- t.Log("Expected:", test.data)
- t.Log("Error: ", err)
- }
- }
- }
- }
- func runDeleteTests(t *testing.T, testKind string, tests []DeleteTest, runner func(DeleteTest) interface{}, resultChecker func(DeleteTest, interface{}) (bool, interface{})) {
- for _, test := range tests {
- if activeTest != "" && test.desc != activeTest {
- continue
- }
- fmt.Println("Running:", test.desc)
- value := runner(test)
- if test.data == nil {
- t.Errorf("MALFORMED TEST: %v", test)
- continue
- }
- if ok, expected := resultChecker(test, value); !ok {
- if expectedBytes, ok := expected.([]byte); ok {
- expected = string(expectedBytes)
- }
- if valueBytes, ok := value.([]byte); ok {
- value = string(valueBytes)
- }
- t.Errorf("%s test '%s' expected to return value %v, but did returned %v instead", testKind, test.desc, expected, value)
- }
- }
- }
- func TestSet(t *testing.T) {
- runSetTests(t, "Set()", setTests,
- func(test SetTest) (value interface{}, dataType ValueType, err error) {
- value, err = Set([]byte(test.json), []byte(test.setData), test.path...)
- return
- },
- func(test SetTest, value interface{}) (bool, interface{}) {
- expected := []byte(test.data.(string))
- return bytes.Equal(expected, value.([]byte)), expected
- },
- )
- }
- func TestDelete(t *testing.T) {
- runDeleteTests(t, "Delete()", deleteTests,
- func(test DeleteTest) interface{} {
- return Delete([]byte(test.json), test.path...)
- },
- func(test DeleteTest, value interface{}) (bool, interface{}) {
- expected := []byte(test.data.(string))
- return bytes.Equal(expected, value.([]byte)), expected
- },
- )
- }
- func TestGet(t *testing.T) {
- runGetTests(t, "Get()", getTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, dataType, _, err = Get([]byte(test.json), test.path...)
- return
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := []byte(test.data.(string))
- return bytes.Equal(expected, value.([]byte)), expected
- },
- )
- }
- func TestGetString(t *testing.T) {
- runGetTests(t, "GetString()", getStringTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, err = GetString([]byte(test.json), test.path...)
- return value, String, err
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := test.data.(string)
- return expected == value.(string), expected
- },
- )
- }
- func TestGetInt(t *testing.T) {
- runGetTests(t, "GetInt()", getIntTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, err = GetInt([]byte(test.json), test.path...)
- return value, Number, err
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := test.data.(int64)
- return expected == value.(int64), expected
- },
- )
- }
- func TestGetFloat(t *testing.T) {
- runGetTests(t, "GetFloat()", getFloatTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, err = GetFloat([]byte(test.json), test.path...)
- return value, Number, err
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := test.data.(float64)
- return expected == value.(float64), expected
- },
- )
- }
- func TestGetBoolean(t *testing.T) {
- runGetTests(t, "GetBoolean()", getBoolTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, err = GetBoolean([]byte(test.json), test.path...)
- return value, Boolean, err
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := test.data.(bool)
- return expected == value.(bool), expected
- },
- )
- }
- func TestGetSlice(t *testing.T) {
- runGetTests(t, "Get()-for-arrays", getArrayTests,
- func(test GetTest) (value interface{}, dataType ValueType, err error) {
- value, dataType, _, err = Get([]byte(test.json), test.path...)
- return
- },
- func(test GetTest, value interface{}) (bool, interface{}) {
- expected := test.data.([]string)
- return reflect.DeepEqual(expected, toStringArray(value.([]byte))), expected
- },
- )
- }
- func TestArrayEach(t *testing.T) {
- mock := []byte(`{"a": { "b":[{"x": 1} ,{"x":2},{ "x":3}, {"x":4} ]}}`)
- count := 0
- ArrayEach(mock, func(value []byte, dataType ValueType, offset int, err error) {
- count++
- switch count {
- case 1:
- if string(value) != `{"x": 1}` {
- t.Errorf("Wrong first item: %s", string(value))
- }
- case 2:
- if string(value) != `{"x":2}` {
- t.Errorf("Wrong second item: %s", string(value))
- }
- case 3:
- if string(value) != `{ "x":3}` {
- t.Errorf("Wrong third item: %s", string(value))
- }
- case 4:
- if string(value) != `{"x":4}` {
- t.Errorf("Wrong forth item: %s", string(value))
- }
- default:
- t.Errorf("Should process only 4 items")
- }
- }, "a", "b")
- }
- func TestArrayEachEmpty(t *testing.T) {
- funcError := func([]byte, ValueType, int, error) { t.Errorf("Run func not allow") }
- type args struct {
- data []byte
- cb func(value []byte, dataType ValueType, offset int, err error)
- keys []string
- }
- tests := []struct {
- name string
- args args
- wantOffset int
- wantErr bool
- }{
- {"Empty array", args{[]byte("[]"), funcError, []string{}}, 1, false},
- {"Empty array with space", args{[]byte("[ ]"), funcError, []string{}}, 2, false},
- {"Empty array with \n", args{[]byte("[\n]"), funcError, []string{}}, 2, false},
- {"Empty field array", args{[]byte("{\"data\": []}"), funcError, []string{"data"}}, 10, false},
- {"Empty field array with space", args{[]byte("{\"data\": [ ]}"), funcError, []string{"data"}}, 11, false},
- {"Empty field array with \n", args{[]byte("{\"data\": [\n]}"), funcError, []string{"data"}}, 11, false},
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- gotOffset, err := ArrayEach(tt.args.data, tt.args.cb, tt.args.keys...)
- if (err != nil) != tt.wantErr {
- t.Errorf("ArrayEach() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if gotOffset != tt.wantOffset {
- t.Errorf("ArrayEach() = %v, want %v", gotOffset, tt.wantOffset)
- }
- })
- }
- }
- type keyValueEntry struct {
- key string
- value string
- valueType ValueType
- }
- func (kv keyValueEntry) String() string {
- return fmt.Sprintf("[%s: %s (%s)]", kv.key, kv.value, kv.valueType)
- }
- type ObjectEachTest struct {
- desc string
- json string
- isErr bool
- entries []keyValueEntry
- }
- var objectEachTests = []ObjectEachTest{
- {
- desc: "empty object",
- json: `{}`,
- entries: []keyValueEntry{},
- },
- {
- desc: "single key-value object",
- json: `{"key": "value"}`,
- entries: []keyValueEntry{
- {"key", "value", String},
- },
- },
- {
- desc: "multiple key-value object with many value types",
- json: `{
- "key1": null,
- "key2": true,
- "key3": 1.23,
- "key4": "string value",
- "key5": [1,2,3],
- "key6": {"a":"b"}
- }`,
- entries: []keyValueEntry{
- {"key1", "null", Null},
- {"key2", "true", Boolean},
- {"key3", "1.23", Number},
- {"key4", "string value", String},
- {"key5", "[1,2,3]", Array},
- {"key6", `{"a":"b"}`, Object},
- },
- },
- {
- desc: "escaped key",
- json: `{"key\"\\\/\b\f\n\r\t\u00B0": "value"}`,
- entries: []keyValueEntry{
- {"key\"\\/\b\f\n\r\t\u00B0", "value", String},
- },
- },
- // Error cases
- {
- desc: "no object present",
- json: ` \t\n\r`,
- isErr: true,
- },
- {
- desc: "unmatched braces 1",
- json: `{`,
- isErr: true,
- },
- {
- desc: "unmatched braces 2",
- json: `}`,
- isErr: true,
- },
- {
- desc: "unmatched braces 3",
- json: `}{}`,
- isErr: true,
- },
- {
- desc: "bad key (number)",
- json: `{123: "value"}`,
- isErr: true,
- },
- {
- desc: "bad key (unclosed quote)",
- json: `{"key: 123}`,
- isErr: true,
- },
- {
- desc: "bad value (no value)",
- json: `{"key":}`,
- isErr: true,
- },
- {
- desc: "bad value (bogus value)",
- json: `{"key": notavalue}`,
- isErr: true,
- },
- {
- desc: "bad entry (missing colon)",
- json: `{"key" "value"}`,
- isErr: true,
- },
- {
- desc: "bad entry (no trailing comma)",
- json: `{"key": "value" "key2": "value2"}`,
- isErr: true,
- },
- {
- desc: "bad entry (two commas)",
- json: `{"key": "value",, "key2": "value2"}`,
- isErr: true,
- },
- }
- func TestObjectEach(t *testing.T) {
- for _, test := range objectEachTests {
- if activeTest != "" && test.desc != activeTest {
- continue
- }
- // Execute ObjectEach and capture all of the entries visited, in order
- var entries []keyValueEntry
- err := ObjectEach([]byte(test.json), func(key, value []byte, valueType ValueType, off int) error {
- entries = append(entries, keyValueEntry{
- key: string(key),
- value: string(value),
- valueType: valueType,
- })
- return nil
- })
- // Check the correctness of the result
- isErr := (err != nil)
- if test.isErr != isErr {
- // If the call didn't match the error expectation, fail
- t.Errorf("ObjectEach test '%s' isErr mismatch: expected %t, obtained %t (err %v)", test.desc, test.isErr, isErr, err)
- } else if isErr {
- // Else, if there was an expected error, don't fail and don't check anything further
- } else if len(test.entries) != len(entries) {
- t.Errorf("ObjectEach test '%s' mismatch in number of key-value entries: expected %d, obtained %d (entries found: %s)", test.desc, len(test.entries), len(entries), entries)
- } else {
- for i, entry := range entries {
- expectedEntry := test.entries[i]
- if expectedEntry.key != entry.key {
- t.Errorf("ObjectEach test '%s' key mismatch at entry %d: expected %s, obtained %s", test.desc, i, expectedEntry.key, entry.key)
- break
- } else if expectedEntry.value != entry.value {
- t.Errorf("ObjectEach test '%s' value mismatch at entry %d: expected %s, obtained %s", test.desc, i, expectedEntry.value, entry.value)
- break
- } else if expectedEntry.valueType != entry.valueType {
- t.Errorf("ObjectEach test '%s' value type mismatch at entry %d: expected %s, obtained %s", test.desc, i, expectedEntry.valueType, entry.valueType)
- break
- } else {
- // Success for this entry
- }
- }
- }
- }
- }
- var testJson = []byte(`{"name": "Name", "order": "Order", "sum": 100, "len": 12, "isPaid": true, "nested": {"a":"test", "b":2, "nested3":{"a":"test3","b":4}, "c": "unknown"}, "nested2": {"a":"test2", "b":3}, "arr": [{"a":"zxc", "b": 1}, {"a":"123", "b":2}], "arrInt": [1,2,3,4], "intPtr": 10}`)
- func TestEachKey(t *testing.T) {
- paths := [][]string{
- {"name"},
- {"order"},
- {"nested", "a"},
- {"nested", "b"},
- {"nested2", "a"},
- {"nested", "nested3", "b"},
- {"arr", "[1]", "b"},
- {"arrInt", "[3]"},
- {"arrInt", "[5]"}, // Should not find last key
- }
- keysFound := 0
- EachKey(testJson, func(idx int, value []byte, vt ValueType, err error) {
- keysFound++
- switch idx {
- case 0:
- if string(value) != "Name" {
- t.Error("Should find 1 key", string(value))
- }
- case 1:
- if string(value) != "Order" {
- t.Errorf("Should find 2 key")
- }
- case 2:
- if string(value) != "test" {
- t.Errorf("Should find 3 key")
- }
- case 3:
- if string(value) != "2" {
- t.Errorf("Should find 4 key")
- }
- case 4:
- if string(value) != "test2" {
- t.Error("Should find 5 key", string(value))
- }
- case 5:
- if string(value) != "4" {
- t.Errorf("Should find 6 key")
- }
- case 6:
- if string(value) != "2" {
- t.Errorf("Should find 7 key")
- }
- case 7:
- if string(value) != "4" {
- t.Error("Should find 8 key", string(value))
- }
- default:
- t.Errorf("Should found only 8 keys")
- }
- }, paths...)
- if keysFound != 8 {
- t.Errorf("Should find 8 keys: %d", keysFound)
- }
- }
- type ParseTest struct {
- in string
- intype ValueType
- out interface{}
- isErr bool
- }
- var parseBoolTests = []ParseTest{
- {
- in: "true",
- intype: Boolean,
- out: true,
- },
- {
- in: "false",
- intype: Boolean,
- out: false,
- },
- {
- in: "foo",
- intype: Boolean,
- isErr: true,
- },
- {
- in: "trux",
- intype: Boolean,
- isErr: true,
- },
- {
- in: "truex",
- intype: Boolean,
- isErr: true,
- },
- {
- in: "",
- intype: Boolean,
- isErr: true,
- },
- }
- var parseFloatTest = []ParseTest{
- {
- in: "0",
- intype: Number,
- out: float64(0),
- },
- {
- in: "0.0",
- intype: Number,
- out: float64(0.0),
- },
- {
- in: "1",
- intype: Number,
- out: float64(1),
- },
- {
- in: "1.234",
- intype: Number,
- out: float64(1.234),
- },
- {
- in: "1.234e5",
- intype: Number,
- out: float64(1.234e5),
- },
- {
- in: "-1.234e5",
- intype: Number,
- out: float64(-1.234e5),
- },
- {
- in: "+1.234e5", // Note: + sign not allowed under RFC7159, but our parser accepts it since it uses strconv.ParseFloat
- intype: Number,
- out: float64(1.234e5),
- },
- {
- in: "1.2.3",
- intype: Number,
- isErr: true,
- },
- {
- in: "1..1",
- intype: Number,
- isErr: true,
- },
- {
- in: "1a",
- intype: Number,
- isErr: true,
- },
- {
- in: "",
- intype: Number,
- isErr: true,
- },
- }
- // parseTestCheckNoError checks the error return from Parse*() against the test case expectations.
- // Returns true the test should proceed to checking the actual data returned from Parse*(), or false if the test is finished.
- func parseTestCheckNoError(t *testing.T, testKind string, test ParseTest, value interface{}, err error) bool {
- if isErr := (err != nil); test.isErr != isErr {
- // If the call didn't match the error expectation, fail
- t.Errorf("%s test '%s' isErr mismatch: expected %t, obtained %t (err %v). Obtained value: %v", testKind, test.in, test.isErr, isErr, err, value)
- return false
- } else if isErr {
- // Else, if there was an error, don't fail and don't check isFound or the value
- return false
- } else {
- // Else, there was no error and a value was found, so check the value
- return true
- }
- }
- func runParseTests(t *testing.T, testKind string, tests []ParseTest, runner func(ParseTest) (interface{}, error), resultChecker func(ParseTest, interface{}) (bool, interface{})) {
- for _, test := range tests {
- value, err := runner(test)
- if parseTestCheckNoError(t, testKind, test, value, err) {
- if test.out == nil {
- t.Errorf("MALFORMED TEST: %v", test)
- continue
- }
- if ok, expected := resultChecker(test, value); !ok {
- if expectedBytes, ok := expected.([]byte); ok {
- expected = string(expectedBytes)
- }
- if valueBytes, ok := value.([]byte); ok {
- value = string(valueBytes)
- }
- t.Errorf("%s test '%s' expected to return value %v, but did returned %v instead", testKind, test.in, expected, value)
- }
- }
- }
- }
- func TestParseBoolean(t *testing.T) {
- runParseTests(t, "ParseBoolean()", parseBoolTests,
- func(test ParseTest) (value interface{}, err error) {
- return ParseBoolean([]byte(test.in))
- },
- func(test ParseTest, obtained interface{}) (bool, interface{}) {
- expected := test.out.(bool)
- return obtained.(bool) == expected, expected
- },
- )
- }
- func TestParseFloat(t *testing.T) {
- runParseTests(t, "ParseFloat()", parseFloatTest,
- func(test ParseTest) (value interface{}, err error) {
- return ParseFloat([]byte(test.in))
- },
- func(test ParseTest, obtained interface{}) (bool, interface{}) {
- expected := test.out.(float64)
- return obtained.(float64) == expected, expected
- },
- )
- }
- var parseStringTest = []ParseTest{
- {
- in: `\uFF11`,
- intype: String,
- out: "\uFF11",
- },
- {
- in: `\uFFFF`,
- intype: String,
- out: "\uFFFF",
- },
- {
- in: `\uDF00`,
- intype: String,
- isErr: true,
- },
- }
- func TestParseString(t *testing.T) {
- runParseTests(t, "ParseString()", parseStringTest,
- func(test ParseTest) (value interface{}, err error) {
- return ParseString([]byte(test.in))
- },
- func(test ParseTest, obtained interface{}) (bool, interface{}) {
- expected := test.out.(string)
- return obtained.(string) == expected, expected
- },
- )
- }
|