jsone_decode_tests.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. %% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
  2. %% coding: latin-1
  3. -module(jsone_decode_tests).
  4. -include_lib("eunit/include/eunit.hrl").
  5. -ifdef('NO_MAP_TYPE').
  6. -define(MAP_OBJECT_TYPE, tuple).
  7. -define(OBJ0, {[]}).
  8. -define(OBJ1(K, V), {[{K, V}]}).
  9. -define(OBJ2(K1, V1, K2, V2), {[{K1, V1}, {K2, V2}]}).
  10. -define(OBJ2_DUP_KEY(K1, V1, K2, V2), ?OBJ2(K1, V1, K2, V2)).
  11. -else.
  12. -define(MAP_OBJECT_TYPE, map).
  13. -define(OBJ0, #{}).
  14. -define(OBJ1(K, V), #{K => V}).
  15. -define(OBJ2(K1, V1, K2, V2), #{K1 => V1, K2 => V2}).
  16. -define(OBJ2_DUP_KEY(K1, V1, _K2, _V2), #{K1 => V1}). % the first (leftmost) value is used
  17. -define(OBJ2_DUP_KEY_LAST(_K1, _V1, K2, V2), #{K2 => V2}). % the last value is used
  18. -endif.
  19. decode_test_() ->
  20. [
  21. %% Symbols
  22. {"false",
  23. fun () ->
  24. ?assertEqual({ok, false, <<"">>}, jsone_decode:decode(<<"false">>))
  25. end},
  26. {"true",
  27. fun () ->
  28. ?assertEqual({ok, true, <<"">>}, jsone_decode:decode(<<"true">>))
  29. end},
  30. {"null",
  31. fun () ->
  32. ?assertEqual({ok, null, <<"">>}, jsone_decode:decode(<<"null">>))
  33. end},
  34. %% Numbers: Integer
  35. {"positive integer",
  36. fun () ->
  37. ?assertEqual({ok, 1, <<"">>}, jsone_decode:decode(<<"1">>))
  38. end},
  39. {"zero",
  40. fun () ->
  41. ?assertEqual({ok, 0, <<"">>}, jsone_decode:decode(<<"0">>))
  42. end},
  43. {"negative integer",
  44. fun () ->
  45. ?assertEqual({ok, -1, <<"">>}, jsone_decode:decode(<<"-1">>))
  46. end},
  47. {"large integer (no limit on size)",
  48. fun () ->
  49. ?assertEqual({ok, 111111111111111111111111111111111111111111111111111111111111111111111111111111, <<"">>},
  50. jsone_decode:decode(<<"111111111111111111111111111111111111111111111111111111111111111111111111111111">>))
  51. end},
  52. {"integer with leading zero (interpreted as zero and remaining binary)",
  53. fun () ->
  54. ?assertEqual({ok, 0, <<"0">>}, jsone_decode:decode(<<"00">>)),
  55. ?assertEqual({ok, 0, <<"1">>}, jsone_decode:decode(<<"01">>)),
  56. ?assertEqual({ok, 0, <<"1">>}, jsone_decode:decode(<<"-01">>))
  57. end},
  58. {"integer can't begin with an explicit plus sign",
  59. fun () ->
  60. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"+1">>))
  61. end},
  62. %% Numbers: Floats
  63. {"float: decimal notation",
  64. fun () ->
  65. ?assertEqual({ok, 1.23, <<"">>}, jsone_decode:decode(<<"1.23">>))
  66. end},
  67. {"float: exponential notation",
  68. fun () ->
  69. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345e-3">>)), % lower case 'e'
  70. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345E-3">>)), % upper case 'E'
  71. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"12345.0e-3">>)),
  72. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E2">>)),
  73. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345e+2">>)), % exponent part can begin with plus sign
  74. ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E+2">>)),
  75. ?assertEqual({ok, -12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>)),
  76. ?assertEqual({ok, 123.0, <<"">>}, jsone_decode:decode(<<"1.23000000000000000000e+02">>))
  77. end},
  78. {"float: invalid format",
  79. fun () ->
  80. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<".123">>)), % omitted integer part
  81. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.">>)), % omitted fraction part: EOS
  82. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.e+3">>)), % omitted fraction part: with exponent part
  83. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e">>)), % imcomplete fraction part
  84. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e-">>)), % imcomplete fraction part
  85. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1ee-1">>)), % duplicated 'e'
  86. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e--1">>)), % duplicated sign
  87. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"1e999">>)), % exponent out of range
  88. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e999">>)), % exponent out of range
  89. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"100000000000000000000000000000000000e300">>)), % product out of range
  90. ?assertEqual({ok, 0.1, <<".2">>}, jsone_decode:decode(<<"0.1.2">>)) % duplicated '.': interpreted as individual tokens
  91. end},
  92. %% Strings
  93. {"simple string",
  94. fun () ->
  95. ?assertEqual({ok, <<"abc">>, <<"">>}, jsone_decode:decode(<<"\"abc\"">>))
  96. end},
  97. {"string: escaped characters",
  98. fun () ->
  99. Input = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
  100. Expected = <<"\"\/\\\b\f\n\r\t">>,
  101. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
  102. end},
  103. {"string: escaped Unicode characters",
  104. fun () ->
  105. %% japanese
  106. Input1 = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304A\"">>,
  107. Expected1 = <<"あいうえお">>, % assumed that the encoding of this file is UTF-8
  108. ?assertEqual({ok, Expected1, <<"">>}, jsone_decode:decode(Input1)),
  109. %% ascii
  110. Input2 = <<"\"\\u0061\\u0062\\u0063\"">>,
  111. Expected2 = <<"abc">>,
  112. ?assertEqual({ok, Expected2, <<"">>}, jsone_decode:decode(Input2)),
  113. %% other multi-byte characters
  114. Input3 = <<"\"\\u06DD\\u06DE\\u10AE\\u10AF\"">>,
  115. Expected3 = <<"۝۞ႮႯ">>,
  116. ?assertEqual({ok, Expected3, <<"">>}, jsone_decode:decode(Input3)),
  117. %% mixture of ascii and japanese characters
  118. Input4 = <<"\"a\\u30421\\u3044bb\\u304622\\u3048ccc\\u304A333\"">>,
  119. Expected4 = <<"aあ1いbbう22えcccお333">>, % assumed that the encoding of this file is UTF-8
  120. ?assertEqual({ok, Expected4, <<"">>}, jsone_decode:decode(Input4))
  121. end},
  122. {"string: surrogate pairs",
  123. fun () ->
  124. Input = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
  125. Expected = <<"𢁉𢂚𢃼">>,
  126. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
  127. end},
  128. {"string: control characters",
  129. fun () ->
  130. Ctrls = lists:seq(0, 16#1f),
  131. lists:foreach(
  132. fun (C) ->
  133. %% Control characters are unacceptable
  134. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<$", C, $">>))
  135. end,
  136. Ctrls),
  137. lists:foreach(
  138. fun (C) ->
  139. %% `allow_ctrl_chars' option allows strings which contain unescaped control characters
  140. ?assertEqual({ok, <<C>>, <<"">>}, jsone_decode:decode(<<$", C, $">>, [{allow_ctrl_chars, true}]))
  141. end,
  142. Ctrls)
  143. end},
  144. {"string: invalid escape characters",
  145. fun () ->
  146. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\z\"">>)), % '\z' is undefined
  147. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\uab\"">>)), % too few hex characters
  148. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\"">>)), % high(first) surrogate only
  149. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\udc49\"">>)), % low(second) surrogate only
  150. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\\u0061\"">>)), % missing low(second) surrogate
  151. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\udf0u\"">>)),
  152. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\ud848\\udf0u\"">>)),
  153. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"\"\\u-3351\"">>))
  154. end},
  155. %% Arrays
  156. {"simple array",
  157. fun () ->
  158. Input = <<"[1,2,\"abc\",null]">>,
  159. Expected = [1, 2, <<"abc">>, null],
  160. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
  161. end},
  162. {"array: contains whitespaces",
  163. fun () ->
  164. Input = <<"[ 1,\t2, \n \"abc\",\r null]">>,
  165. Expected = [1, 2, <<"abc">>, null],
  166. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
  167. end},
  168. {"empty array",
  169. fun () ->
  170. ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[]">>)),
  171. ?assertEqual({ok, [], <<"">>}, jsone_decode:decode(<<"[ \t\r\n]">>))
  172. end},
  173. {"array: trailing comma is disallowed",
  174. fun () ->
  175. Input = <<"[1, 2, \"abc\", null, ]">>,
  176. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  177. end},
  178. {"array: missing comma",
  179. fun () ->
  180. Input = <<"[1 2, \"abc\", null]">>, % a missing comma between '1' and '2'
  181. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  182. end},
  183. {"array: missing closing bracket",
  184. fun () ->
  185. Input = <<"[1, 2, \"abc\", null">>,
  186. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  187. end},
  188. %% Objects
  189. {"simple object",
  190. fun () ->
  191. Input = <<"{\"1\":2,\"key\":\"value\"}">>,
  192. Expected = ?OBJ2(<<"1">>, 2, <<"key">>, <<"value">>),
  193. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input)), % `map' is the default format
  194. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input, [{object_format, ?MAP_OBJECT_TYPE}]))
  195. end},
  196. {"simple object: tuple or proplist",
  197. fun () ->
  198. Input = <<"{\"1\":2,\"key\":\"value\"}">>,
  199. Expected = {[{<<"1">>, 2},{<<"key">>, <<"value">>}]},
  200. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input, [{object_format, tuple}])),
  201. ?assertEqual({ok, element(1, Expected), <<"">>}, jsone_decode:decode(Input, [{object_format, proplist}]))
  202. end},
  203. {"object: contains whitespaces",
  204. fun () ->
  205. Input = <<"{ \"1\" :\t 2,\n\r\"key\" : \n \"value\"}">>,
  206. Expected = ?OBJ2(<<"1">>, 2, <<"key">>, <<"value">>),
  207. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
  208. end},
  209. {"empty object",
  210. fun () ->
  211. ?assertEqual({ok, ?OBJ0, <<"">>}, jsone_decode:decode(<<"{}">>)),
  212. ?assertEqual({ok, ?OBJ0, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>)),
  213. ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{}">>, [{object_format, tuple}])),
  214. ?assertEqual({ok, [{}], <<"">>}, jsone_decode:decode(<<"{}">>, [{object_format, proplist}]))
  215. end},
  216. {"empty object: map",
  217. fun () ->
  218. ?assertEqual({ok, ?OBJ0, <<"">>}, jsone_decode:decode(<<"{}">>, [{object_format, ?MAP_OBJECT_TYPE}]))
  219. end},
  220. {"duplicated members: map",
  221. fun () ->
  222. Input = <<"{\"1\":\"first\",\"1\":\"second\"}">>,
  223. Expected = ?OBJ2_DUP_KEY(<<"1">>, <<"first">>, <<"1">>, <<"second">>),
  224. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input, [{object_format, ?MAP_OBJECT_TYPE}]))
  225. end},
  226. {"duplicated members last: map",
  227. fun () ->
  228. Input = <<"{\"1\":\"first\",\"1\":\"second\"}">>,
  229. Expected = ?OBJ2_DUP_KEY_LAST(<<"1">>, <<"first">>, <<"1">>, <<"second">>),
  230. ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input,
  231. [{object_format, ?MAP_OBJECT_TYPE},
  232. {duplicate_map_keys, last}]))
  233. end},
  234. {"object: trailing comma is disallowed",
  235. fun () ->
  236. Input = <<"{\"1\":2, \"key\":\"value\", }">>,
  237. io:format("~p\n", [catch jsone_decode:decode(Input)]),
  238. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input, [{object_format, tuple}]))
  239. end},
  240. {"object: missing comma",
  241. fun () ->
  242. Input = <<"{\"1\":2 \"key\":\"value\"}">>,
  243. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  244. end},
  245. {"object: missing field key",
  246. fun () ->
  247. Input = <<"{:2, \"key\":\"value\"}">>,
  248. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  249. end},
  250. {"object: non string key",
  251. fun () ->
  252. Input = <<"{1:2, \"key\":\"value\"}">>,
  253. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  254. end},
  255. {"object: missing field value",
  256. fun () ->
  257. Input = <<"{\"1\", \"key\":\"value\"}">>,
  258. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  259. end},
  260. {"object: missing closing brace",
  261. fun () ->
  262. Input = <<"{\"1\":2 \"key\":\"value\"">>,
  263. ?assertMatch({error, {badarg, _}}, jsone_decode:decode(Input))
  264. end},
  265. {"atom keys",
  266. fun () ->
  267. KeyOpt = fun(Keys) -> [{keys, Keys}, {object_format, proplist}]
  268. end,
  269. Input = <<"{\"foo\":\"ok\"}">>,
  270. ?assertEqual([{<<"foo">>, <<"ok">>}], jsone:decode(Input, KeyOpt(binary))),
  271. ?assertEqual([{foo, <<"ok">>}], jsone:decode(Input, KeyOpt(atom))),
  272. ?assertEqual([{foo, <<"ok">>}], jsone:decode(Input, KeyOpt(existing_atom))),
  273. ?assertError(badarg, jsone:decode(<<"{\"@#$%^!\":\"ok\"}">>, KeyOpt(existing_atom))),
  274. ?assertEqual([{foo, <<"ok">>}], jsone:decode(Input, KeyOpt(attempt_atom))),
  275. ?assertEqual([{<<"@#$%^!">>, <<"ok">>}], jsone:decode(<<"{\"@#$%^!\":\"ok\"}">>, KeyOpt(attempt_atom))),
  276. Value = integer_to_binary(1234),
  277. % do not make atom in test code
  278. [{Atom, <<"ok">>}] = jsone:decode(<<"{\"", Value/binary, "\":\"ok\"}">>, KeyOpt(atom)),
  279. ?assertEqual(Value, atom_to_binary(Atom, latin1))
  280. end},
  281. %% Others
  282. {"compound data",
  283. fun () ->
  284. Input = <<" [true, {\"1\" : 2, \"array\":[[[[1]]], {\"ab\":\"cd\"}, false]}, null] ">>,
  285. Expected = [true, ?OBJ2(<<"1">>, 2, <<"array">>, [[[[1]]], ?OBJ1(<<"ab">>, <<"cd">>), false]), null],
  286. ?assertEqual({ok, Expected, <<" ">>}, jsone_decode:decode(Input))
  287. end},
  288. {"undefined_as_null option",
  289. fun() ->
  290. ?assertEqual({ok, undefined, <<>>}, jsone_decode:decode(<<"null">>,[undefined_as_null])), % OK
  291. ?assertEqual({ok, null, <<>>}, jsone_decode:decode(<<"null">>,[])) % OK
  292. end},
  293. {"Invalid UTF-8 characters",
  294. fun () ->
  295. Input = <<123,34,105,100,34,58,34,190,72,94,90,253,121,94,71,73,68,91,122,211,253,32,94,86,67,163,253,230,34,125>>,
  296. ?assertMatch({ok, _, _}, jsone:try_decode(Input)),
  297. ?assertMatch({error, {badarg, _}}, jsone:try_decode(Input, [reject_invalid_utf8]))
  298. end}
  299. ].