json_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package json
  5. import (
  6. "math"
  7. "strings"
  8. "testing"
  9. "unicode/utf8"
  10. "github.com/google/go-cmp/cmp"
  11. "github.com/google/go-cmp/cmp/cmpopts"
  12. )
  13. func Test(t *testing.T) {
  14. const space = " \n\r\t"
  15. var V = ValueOf
  16. type Arr = []Value
  17. type Obj = [][2]Value
  18. tests := []struct {
  19. in string
  20. wantVal Value
  21. wantOut string
  22. wantOutIndent string
  23. wantErr string
  24. }{{
  25. in: ``,
  26. wantErr: `unexpected EOF`,
  27. }, {
  28. in: space,
  29. wantErr: `unexpected EOF`,
  30. }, {
  31. in: space + `null` + space,
  32. wantVal: V(nil),
  33. wantOut: `null`,
  34. wantOutIndent: `null`,
  35. }, {
  36. in: space + `true` + space,
  37. wantVal: V(true),
  38. wantOut: `true`,
  39. wantOutIndent: `true`,
  40. }, {
  41. in: space + `false` + space,
  42. wantVal: V(false),
  43. wantOut: `false`,
  44. wantOutIndent: `false`,
  45. }, {
  46. in: space + `0` + space,
  47. wantVal: V(0.0),
  48. wantOut: `0`,
  49. wantOutIndent: `0`,
  50. }, {
  51. in: space + `"hello"` + space,
  52. wantVal: V("hello"),
  53. wantOut: `"hello"`,
  54. wantOutIndent: `"hello"`,
  55. }, {
  56. in: space + `[]` + space,
  57. wantVal: V(Arr{}),
  58. wantOut: `[]`,
  59. wantOutIndent: `[]`,
  60. }, {
  61. in: space + `{}` + space,
  62. wantVal: V(Obj{}),
  63. wantOut: `{}`,
  64. wantOutIndent: `{}`,
  65. }, {
  66. in: `null#invalid`,
  67. wantErr: `8 bytes of unconsumed input`,
  68. }, {
  69. in: `0#invalid`,
  70. wantErr: `8 bytes of unconsumed input`,
  71. }, {
  72. in: `"hello"#invalid`,
  73. wantErr: `8 bytes of unconsumed input`,
  74. }, {
  75. in: `[]#invalid`,
  76. wantErr: `8 bytes of unconsumed input`,
  77. }, {
  78. in: `{}#invalid`,
  79. wantErr: `8 bytes of unconsumed input`,
  80. }, {
  81. in: `[truee,true]`,
  82. wantErr: `invalid "truee" as literal`,
  83. }, {
  84. in: `[falsee,false]`,
  85. wantErr: `invalid "falsee" as literal`,
  86. }, {
  87. in: `[`,
  88. wantErr: `unexpected EOF`,
  89. }, {
  90. in: `[{}]`,
  91. wantVal: V(Arr{V(Obj{})}),
  92. wantOut: "[{}]",
  93. wantOutIndent: "[\n\t{}\n]",
  94. }, {
  95. in: `[{]}`,
  96. wantErr: `invalid character ']' at start of string`,
  97. }, {
  98. in: `[,]`,
  99. wantErr: `invalid "," as value`,
  100. }, {
  101. in: `{,}`,
  102. wantErr: `invalid character ',' at start of string`,
  103. }, {
  104. in: `{"key""val"}`,
  105. wantErr: `invalid character '"', expected ':' in object`,
  106. }, {
  107. in: `["elem0""elem1"]`,
  108. wantErr: `invalid character '"', expected ']' at end of array`,
  109. }, {
  110. in: `{"hello"`,
  111. wantErr: `unexpected EOF`,
  112. }, {
  113. in: `{"hello"}`,
  114. wantErr: `invalid character '}', expected ':' in object`,
  115. }, {
  116. in: `{"hello":`,
  117. wantErr: `unexpected EOF`,
  118. }, {
  119. in: `{"hello":}`,
  120. wantErr: `invalid "}" as value`,
  121. }, {
  122. in: `{"hello":"goodbye"`,
  123. wantErr: `unexpected EOF`,
  124. }, {
  125. in: `{"hello":"goodbye"]`,
  126. wantErr: `invalid character ']', expected '}' at end of object`,
  127. }, {
  128. in: `{"hello":"goodbye"}`,
  129. wantVal: V(Obj{{V("hello"), V("goodbye")}}),
  130. wantOut: `{"hello":"goodbye"}`,
  131. wantOutIndent: "{\n\t\"hello\": \"goodbye\"\n}",
  132. }, {
  133. in: `{"hello":"goodbye",}`,
  134. wantErr: `invalid character '}' at start of string`,
  135. }, {
  136. in: `{"k":"v1","k":"v2"}`,
  137. wantVal: V(Obj{
  138. {V("k"), V("v1")}, {V("k"), V("v2")},
  139. }),
  140. wantOut: `{"k":"v1","k":"v2"}`,
  141. wantOutIndent: "{\n\t\"k\": \"v1\",\n\t\"k\": \"v2\"\n}",
  142. }, {
  143. in: `{"k":{"k":{"k":"v"}}}`,
  144. wantVal: V(Obj{
  145. {V("k"), V(Obj{
  146. {V("k"), V(Obj{
  147. {V("k"), V("v")},
  148. })},
  149. })},
  150. }),
  151. wantOut: `{"k":{"k":{"k":"v"}}}`,
  152. wantOutIndent: "{\n\t\"k\": {\n\t\t\"k\": {\n\t\t\t\"k\": \"v\"\n\t\t}\n\t}\n}",
  153. }, {
  154. in: `{"k":{"k":{"k":"v1","k":"v2"}}}`,
  155. wantVal: V(Obj{
  156. {V("k"), V(Obj{
  157. {V("k"), V(Obj{
  158. {V("k"), V("v1")},
  159. {V("k"), V("v2")},
  160. })},
  161. })},
  162. }),
  163. wantOut: `{"k":{"k":{"k":"v1","k":"v2"}}}`,
  164. wantOutIndent: "{\n\t\"k\": {\n\t\t\"k\": {\n\t\t\t\"k\": \"v1\",\n\t\t\t\"k\": \"v2\"\n\t\t}\n\t}\n}",
  165. }, {
  166. in: " x",
  167. wantErr: `syntax error (line 1:3)`,
  168. }, {
  169. in: `["💩"x`,
  170. wantErr: `syntax error (line 1:5)`,
  171. }, {
  172. in: "\n\n[\"🔥🔥🔥\"x",
  173. wantErr: `syntax error (line 3:7)`,
  174. }, {
  175. in: `["👍🏻👍🏿"x`,
  176. wantErr: `syntax error (line 1:8)`, // multi-rune emojis; could be column:6
  177. }, {
  178. in: "\"\x00\"",
  179. wantErr: `invalid character '\x00' in string`,
  180. }, {
  181. in: "\"\xff\"",
  182. wantErr: `invalid UTF-8 detected`,
  183. wantVal: V(string("\xff")),
  184. }, {
  185. in: `"` + string(utf8.RuneError) + `"`,
  186. wantVal: V(string(utf8.RuneError)),
  187. wantOut: `"` + string(utf8.RuneError) + `"`,
  188. }, {
  189. in: `"\uFFFD"`,
  190. wantVal: V(string(utf8.RuneError)),
  191. wantOut: `"` + string(utf8.RuneError) + `"`,
  192. }, {
  193. in: `"\x"`,
  194. wantErr: `invalid escape code "\\x" in string`,
  195. }, {
  196. in: `"\uXXXX"`,
  197. wantErr: `invalid escape code "\\uXXXX" in string`,
  198. }, {
  199. in: `"\uDEAD"`, // unmatched surrogate pair
  200. wantErr: `unexpected EOF`,
  201. }, {
  202. in: `"\uDEAD\uBEEF"`, // invalid surrogate half
  203. wantErr: `invalid escape code "\\uBEEF" in string`,
  204. }, {
  205. in: `"\uD800\udead"`, // valid surrogate pair
  206. wantVal: V("𐊭"),
  207. wantOut: `"𐊭"`,
  208. }, {
  209. in: `"\u0000\"\\\/\b\f\n\r\t"`,
  210. wantVal: V("\u0000\"\\/\b\f\n\r\t"),
  211. wantOut: `"\u0000\"\\/\b\f\n\r\t"`,
  212. }, {
  213. in: `-`,
  214. wantErr: `invalid "-" as number`,
  215. }, {
  216. in: `-0`,
  217. wantVal: V(math.Copysign(0, -1)),
  218. wantOut: `-0`,
  219. }, {
  220. in: `+0`,
  221. wantErr: `invalid "+0" as value`,
  222. }, {
  223. in: `-+`,
  224. wantErr: `invalid "-+" as number`,
  225. }, {
  226. in: `0.`,
  227. wantErr: `invalid "0." as number`,
  228. }, {
  229. in: `.1`,
  230. wantErr: `invalid ".1" as value`,
  231. }, {
  232. in: `0.e1`,
  233. wantErr: `invalid "0.e1" as number`,
  234. }, {
  235. in: `0.0`,
  236. wantVal: V(0.0),
  237. wantOut: "0",
  238. }, {
  239. in: `01`,
  240. wantErr: `invalid "01" as number`,
  241. }, {
  242. in: `0e`,
  243. wantErr: `invalid "0e" as number`,
  244. }, {
  245. in: `0e0`,
  246. wantVal: V(0.0),
  247. wantOut: "0",
  248. }, {
  249. in: `0E0`,
  250. wantVal: V(0.0),
  251. wantOut: "0",
  252. }, {
  253. in: `0Ee`,
  254. wantErr: `invalid "0Ee" as number`,
  255. }, {
  256. in: `-1.0E+1`,
  257. wantVal: V(-10.0),
  258. wantOut: "-10",
  259. }, {
  260. in: `
  261. {
  262. "firstName" : "John",
  263. "lastName" : "Smith" ,
  264. "isAlive" : true,
  265. "age" : 27,
  266. "address" : {
  267. "streetAddress" : "21 2nd Street" ,
  268. "city" : "New York" ,
  269. "state" : "NY" ,
  270. "postalCode" : "10021-3100"
  271. },
  272. "phoneNumbers" : [
  273. {
  274. "type" : "home" ,
  275. "number" : "212 555-1234"
  276. } ,
  277. {
  278. "type" : "office" ,
  279. "number" : "646 555-4567"
  280. } ,
  281. {
  282. "type" : "mobile" ,
  283. "number" : "123 456-7890"
  284. }
  285. ],
  286. "children" : [] ,
  287. "spouse" : null
  288. }
  289. `,
  290. wantVal: V(Obj{
  291. {V("firstName"), V("John")},
  292. {V("lastName"), V("Smith")},
  293. {V("isAlive"), V(true)},
  294. {V("age"), V(27.0)},
  295. {V("address"), V(Obj{
  296. {V("streetAddress"), V("21 2nd Street")},
  297. {V("city"), V("New York")},
  298. {V("state"), V("NY")},
  299. {V("postalCode"), V("10021-3100")},
  300. })},
  301. {V("phoneNumbers"), V(Arr{
  302. V(Obj{
  303. {V("type"), V("home")},
  304. {V("number"), V("212 555-1234")},
  305. }),
  306. V(Obj{
  307. {V("type"), V("office")},
  308. {V("number"), V("646 555-4567")},
  309. }),
  310. V(Obj{
  311. {V("type"), V("mobile")},
  312. {V("number"), V("123 456-7890")},
  313. }),
  314. })},
  315. {V("children"), V(Arr{})},
  316. {V("spouse"), V(nil)},
  317. }),
  318. wantOut: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
  319. wantOutIndent: `{
  320. "firstName": "John",
  321. "lastName": "Smith",
  322. "isAlive": true,
  323. "age": 27,
  324. "address": {
  325. "streetAddress": "21 2nd Street",
  326. "city": "New York",
  327. "state": "NY",
  328. "postalCode": "10021-3100"
  329. },
  330. "phoneNumbers": [
  331. {
  332. "type": "home",
  333. "number": "212 555-1234"
  334. },
  335. {
  336. "type": "office",
  337. "number": "646 555-4567"
  338. },
  339. {
  340. "type": "mobile",
  341. "number": "123 456-7890"
  342. }
  343. ],
  344. "children": [],
  345. "spouse": null
  346. }`,
  347. }}
  348. opts := cmp.Options{
  349. cmpopts.EquateEmpty(),
  350. cmp.Transformer("", func(v Value) interface{} {
  351. switch v.typ {
  352. case 0:
  353. return nil // special case so Value{} == Value{}
  354. case Null:
  355. return nil
  356. case Bool:
  357. return v.Bool()
  358. case Number:
  359. return v.Number()
  360. case String:
  361. return v.String()
  362. case Array:
  363. return v.Array()
  364. case Object:
  365. return v.Object()
  366. default:
  367. panic("invalid type")
  368. }
  369. }),
  370. }
  371. for _, tt := range tests {
  372. t.Run("", func(t *testing.T) {
  373. if tt.in != "" || tt.wantVal.Type() != 0 || tt.wantErr != "" {
  374. gotVal, err := Unmarshal([]byte(tt.in))
  375. if err == nil {
  376. if tt.wantErr != "" {
  377. t.Errorf("Unmarshal(): got nil error, want %v", tt.wantErr)
  378. }
  379. } else {
  380. if tt.wantErr == "" {
  381. t.Errorf("Unmarshal(): got %v, want nil error", err)
  382. } else if !strings.Contains(err.Error(), tt.wantErr) {
  383. t.Errorf("Unmarshal(): got %v, want %v", err, tt.wantErr)
  384. }
  385. }
  386. if diff := cmp.Diff(gotVal, tt.wantVal, opts); diff != "" {
  387. t.Errorf("Unmarshal(): output mismatch (-got +want):\n%s", diff)
  388. }
  389. }
  390. if tt.wantOut != "" {
  391. gotOut, err := Marshal(tt.wantVal, "")
  392. if err != nil {
  393. t.Errorf("Marshal(): got %v, want nil error", err)
  394. }
  395. if string(gotOut) != tt.wantOut {
  396. t.Errorf("Marshal():\ngot: %s\nwant: %s", gotOut, tt.wantOut)
  397. }
  398. }
  399. if tt.wantOutIndent != "" {
  400. gotOut, err := Marshal(tt.wantVal, "\t")
  401. if err != nil {
  402. t.Errorf("Marshal(Indent): got %v, want nil error", err)
  403. }
  404. if string(gotOut) != tt.wantOutIndent {
  405. t.Errorf("Marshal(Indent):\ngot: %s\nwant: %s", gotOut, tt.wantOutIndent)
  406. }
  407. }
  408. })
  409. }
  410. }