jsone_decode_tests.erl 13 KB

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