jsone_decode_tests.erl 12 KB

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