jsone_encode_tests.erl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. %% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
  2. %% coding: latin-1
  3. -module(jsone_encode_tests).
  4. -include_lib("eunit/include/eunit.hrl").
  5. -ifdef('NO_MAP_TYPE').
  6. -define(OBJ0, {[]}).
  7. -define(OBJ1(K, V), {[{K, V}]}).
  8. -define(OBJ2(K1, V1, K2, V2), {[{K1, V1}, {K2, V2}]}).
  9. -define(OBJECT_FROM_LIST(List), List).
  10. -else.
  11. -define(OBJ0, #{}).
  12. -define(OBJ1(K, V), #{K => V}).
  13. -define(OBJ2(K1, V1, K2, V2), #{K1 => V1, K2 => V2}).
  14. -define(OBJECT_FROM_LIST(List), maps:from_list(List)).
  15. -endif.
  16. encode_test_() ->
  17. [
  18. %% Symbols
  19. {"false",
  20. fun () ->
  21. ?assertEqual({ok, <<"false">>}, jsone_encode:encode(false))
  22. end},
  23. {"true",
  24. fun () ->
  25. ?assertEqual({ok, <<"true">>}, jsone_encode:encode(true))
  26. end},
  27. {"null",
  28. fun () ->
  29. ?assertEqual({ok, <<"null">>}, jsone_encode:encode(null))
  30. end},
  31. %% Numbers: Inline json term
  32. {"json",
  33. fun () ->
  34. ?assertEqual(
  35. {ok, <<"{\"foo\":[1,2,3],\"bar\":\"",195,169,"ok\"}">>},
  36. jsone_encode:encode(
  37. ?OBJ2(foo, {{json, ["["|[$1, ",2",<<",3]">>]]}},
  38. <<"bar">>, {{json_utf8, [$", 233, "ok", $"]}}))),
  39. ?assertEqual(
  40. {ok, <<"{\"foo\":[1,2,3],\"bar\":\"",233,"ok\"}">>},
  41. jsone_encode:encode(
  42. ?OBJ2(foo, {{json, ["["|[$1, ",2",<<",3]">>]]}},
  43. <<"bar">>, {{json, [$", 233, "ok", $"]}}))),
  44. ?assertEqual(
  45. {ok, <<"{\"json\":\"[1,2,3]\"}">>},
  46. jsone_encode:encode([{json, <<"[1,2,3]">>}])),
  47. ?assertEqual(
  48. {ok, <<"[[1,2,3]]">>},
  49. jsone_encode:encode([{{json, <<"[1,2,3]">>}}]))
  50. end},
  51. %% Numbers: Integer
  52. {"zero",
  53. fun () ->
  54. ?assertEqual({ok, <<"0">>}, jsone_encode:encode(0))
  55. end},
  56. {"positive integer",
  57. fun () ->
  58. ?assertEqual({ok, <<"1">>}, jsone_encode:encode(1))
  59. end},
  60. {"negative integer",
  61. fun () ->
  62. ?assertEqual({ok, <<"-1">>}, jsone_encode:encode(-1))
  63. end},
  64. {"large number",
  65. fun () ->
  66. ?assertEqual({ok, <<"11111111111111111111111111111111111111111111111111111111111111111111111">>},
  67. jsone_encode:encode(11111111111111111111111111111111111111111111111111111111111111111111111))
  68. end},
  69. %% Numbers: Float",
  70. {"float",
  71. fun () ->
  72. Input = 1.234,
  73. ?assertMatch({ok, _}, jsone_encode:encode(Input)),
  74. ?assertEqual(Input, binary_to_float(element(2, jsone_encode:encode(Input))))
  75. end},
  76. {"float_format option",
  77. fun () ->
  78. Input = 1.23,
  79. ?assertEqual({ok, <<"1.22999999999999998224e+00">>}, jsone_encode:encode(Input)),
  80. ?assertEqual({ok, <<"1.2300e+00">>}, jsone_encode:encode(Input, [{float_format, [{scientific, 4}]}])),
  81. ?assertEqual({ok, <<"1.2e+00">>}, jsone_encode:encode(Input, [{float_format, [{scientific, 1}]}])),
  82. ?assertEqual({ok, <<"1.2300">>}, jsone_encode:encode(Input, [{float_format, [{decimals, 4}]}])),
  83. ?assertEqual({ok, <<"1.23">>}, jsone_encode:encode(Input, [{float_format, [{decimals, 4}, compact]}]))
  84. end},
  85. %% Strings
  86. {"simple string",
  87. fun () ->
  88. ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(<<"abc">>))
  89. end},
  90. {"atom is regarded as string",
  91. fun () ->
  92. ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(abc))
  93. end},
  94. {"string: contains escaped characters",
  95. fun () ->
  96. Input = <<"\"\/\\\b\f\n\r\t">>,
  97. Expected = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
  98. ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
  99. ?assertEqual({ok, Expected}, jsone_encode:encode(Input, [native_utf8]))
  100. end},
  101. {"string: contains forward slashes",
  102. fun () ->
  103. Input = <<"1/2">>,
  104. ?assertEqual({ok, <<"\"1\\/2\"">>}, jsone_encode:encode(Input)),
  105. ?assertEqual({ok, <<"\"1/2\"">>}, jsone_encode:encode(Input, [native_forward_slash]))
  106. end},
  107. {"string: contains control characters",
  108. fun () ->
  109. Ctrls = lists:seq(16#00, 16#1F) -- [$\b, $\f, $\n, $\r, $\t],
  110. Input = list_to_binary(Ctrls),
  111. Expected = list_to_binary([$", [io_lib:format("\\u00~2.16.0b", [C]) || C <- Ctrls], $"]),
  112. ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
  113. ?assertEqual({ok, Expected}, jsone_encode:encode(Input, [native_utf8]))
  114. end},
  115. {"string: contains multi-byte (UTF-8 encoded) characters",
  116. fun () ->
  117. %% japanese
  118. Input1 = <<"あいうえお">>, % assumed that the encoding of this file is UTF-8
  119. Expected1 = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304a\"">>,
  120. ?assertEqual({ok, Expected1}, jsone_encode:encode(Input1)),
  121. Expected12 = <<$", Input1/binary, $">>,
  122. ?assertEqual({ok, Expected12}, jsone_encode:encode(Input1, [native_utf8])),
  123. %% other multi-byte characters
  124. Input2 = <<"۝۞ႮႯ">>,
  125. Expected2 = <<"\"\\u06dd\\u06de\\u10ae\\u10af\"">>,
  126. ?assertEqual({ok, Expected2}, jsone_encode:encode(Input2)),
  127. Expected22 = <<$", Input2/binary, $">>,
  128. ?assertEqual({ok, Expected22}, jsone_encode:encode(Input2, [native_utf8]))
  129. end},
  130. {"string: containts surrogate pairs",
  131. fun () ->
  132. Input = <<"𢁉𢂚𢃼">>,
  133. Expected = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
  134. ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
  135. end},
  136. %% Strings variant: Datetimes
  137. {"datetime: iso8601: utc",
  138. fun () ->
  139. ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}})),
  140. ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, iso8601}])),
  141. ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, {iso8601, utc}}]))
  142. end},
  143. {"datetime: iso8601: local",
  144. fun () ->
  145. {ok, Json} = jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, {iso8601, local}}]),
  146. UTC = {{1970, 1, 2}, {0,0,0}},
  147. Local = calendar:universal_time_to_local_time({{1970, 1, 2}, {0,0,0}}),
  148. case UTC =:= Local of
  149. false -> ?assertMatch(<<"\"2015-06-25T14:57:25",_:6/binary,"\"">>, Json);
  150. true -> ?assertMatch(<<"\"2015-06-25T14:57:25Z\"">>, Json)
  151. end
  152. end},
  153. {"datetime: iso8601: timezone",
  154. fun () ->
  155. ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, {iso8601, 0}}])),
  156. ?assertEqual({ok, <<"\"2015-06-25T14:57:25+00:01\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, {iso8601, 60}}])),
  157. ?assertEqual({ok, <<"\"2015-06-25T14:57:25-00:01\"">>}, jsone_encode:encode({{2015,6,25},{14,57,25}}, [{datetime_format, {iso8601, -60}}]))
  158. end},
  159. {"datetime as head of array",
  160. ?_assertEqual({ok, <<"[\"2015-06-25T14:57:25Z\"]">>},
  161. jsone_encode:encode([{{2015,6,25},{14,57,25}}]))},
  162. {"datetime: iso8601: with fractions of seconds",
  163. fun () ->
  164. ?assertEqual({ok, <<"\"2015-06-25T14:57:25.325Z\"">>},
  165. jsone_encode:encode({{2015,6,25},{14,57,25.3245}})),
  166. ?assertEqual({ok, <<"\"2015-06-25T14:57:05.320Z\"">>},
  167. jsone_encode:encode({{2015,6,25},{14,57,5.32}}))
  168. end},
  169. %% Arrays
  170. {"simple array",
  171. fun () ->
  172. Input = [1, 2, 3],
  173. Expected = <<"[1,2,3]">>,
  174. ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
  175. end},
  176. {"empty array",
  177. fun () ->
  178. Input = [],
  179. Expected = <<"[]">>,
  180. ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
  181. end},
  182. %% Objects
  183. {"simple object",
  184. fun () ->
  185. Input1 = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
  186. Input2 = [{<<"key">>, <<"value">>}, {<<"1">>, 2}],
  187. Expected = <<"{\"key\":\"value\",\"1\":2}">>,
  188. ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
  189. ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
  190. end},
  191. {"empty object",
  192. fun () ->
  193. Input1 = {[]},
  194. Input2 = [{}],
  195. Expected = <<"{}">>,
  196. ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
  197. ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
  198. end},
  199. {"simple object: map",
  200. fun () ->
  201. Input = ?OBJ2(<<"1">>, 2, <<"key">>, <<"value">>),
  202. Expected = <<"{\"1\":2,\"key\":\"value\"}">>,
  203. ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
  204. end},
  205. {"empty object: map",
  206. fun () ->
  207. Input = ?OBJ0,
  208. Expected = <<"{}">>,
  209. ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
  210. end},
  211. {"atom key is allowed",
  212. fun () ->
  213. Expected = <<"{\"key\":2}">>,
  214. ?assertEqual({ok, Expected}, jsone_encode:encode({[{key, 2}]}))
  215. end},
  216. {"object_key_type option",
  217. fun () ->
  218. %% key: atom
  219. ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, string}])), % OK
  220. ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, scalar}])), % OK
  221. ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, value}])), % OK
  222. %% key: number
  223. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, string}])), % NG
  224. ?assertEqual({ok, <<"{\"1\":2}">>}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, scalar}])), % OK
  225. ?assertEqual({ok, <<"{\"1\":2}">>}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, value}])), % OK
  226. %% key: datetime
  227. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1({{2000,1,1}, {0,0,0}}, 2), [{object_key_type, string}])), % NG
  228. ?assertEqual({ok, <<"{\"2000-01-01T00:00:00Z\":2}">>}, jsone_encode:encode(?OBJ1({{2000,1,1}, {0,0,0}}, 2), [{object_key_type, scalar}])), % OK
  229. ?assertEqual({ok, <<"{\"2000-01-01T00:00:00Z\":2}">>}, jsone_encode:encode(?OBJ1({{2000,1,1}, {0,0,0}}, 2), [{object_key_type, value}])), % OK
  230. %% key: array
  231. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, string}])), % NG
  232. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, scalar}])), % NG
  233. ?assertEqual({ok, <<"{\"[1]\":2}">>}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, value}])), % OK
  234. %% key: object
  235. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, string}])), % NG
  236. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, scalar}])), % NG
  237. ?assertEqual({ok, <<"{\"{}\":2}">>}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, value}])) % OK
  238. end},
  239. {"non binary object member key is disallowed",
  240. fun () ->
  241. ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{1, 2}]})),
  242. ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{"1", 2}]}))
  243. end},
  244. {"undefined_as_null option",
  245. fun() ->
  246. ?assertEqual({ok,<<"null">>}, jsone_encode:encode(undefined,[undefined_as_null])), % OK
  247. ?assertEqual({ok,<<"\"undefined\"">>}, jsone_encode:encode(undefined,[])) % OK
  248. end},
  249. {"skip_undefined option",
  250. fun() ->
  251. Object = #{<<"1">> => undefined, <<"2">> => 3, <<"3">> => undefined},
  252. ?assertEqual({ok,<<"{\"1\":null,\"2\":3,\"3\":null}">>}, jsone_encode:encode(Object,[undefined_as_null])),
  253. ?assertEqual({ok,<<"{\"2\":3}">>}, jsone_encode:encode(Object,[skip_undefined]))
  254. end},
  255. %% Pretty Print
  256. {"space",
  257. fun () ->
  258. ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{space, 1}])),
  259. ?assertEqual({ok, <<"[1, 2, 3]">>}, jsone_encode:encode([1,2,3], [{space, 1}])),
  260. ?assertEqual({ok, <<"[1, 2, 3]">>}, jsone_encode:encode([1,2,3], [{space, 2}])),
  261. ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{space, 1}])),
  262. ?assertEqual({ok, <<"{\"a\": 1, \"b\": 2}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{space, 1}])),
  263. ?assertEqual({ok, <<"{\"a\": 1, \"b\": 2}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{space, 2}]))
  264. end},
  265. {"indent",
  266. fun () ->
  267. ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{indent, 1}])),
  268. ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1,2,3], [{indent, 1}])),
  269. ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1,2,3], [{indent, 2}])),
  270. ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{indent, 1}])),
  271. ?assertEqual({ok, <<"{\n \"a\":1,\n \"b\":2\n}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 1}])),
  272. ?assertEqual({ok, <<"{\n \"a\":1,\n \"b\":2\n}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 2}]))
  273. end},
  274. {"indent+space",
  275. fun () ->
  276. ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{indent, 1}, {space, 1}])),
  277. ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1,2,3], [{indent, 1}, {space, 1}])),
  278. ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1,2,3], [{indent, 2}, {space, 2}])),
  279. ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{indent, 1}, {space, 1}])),
  280. ?assertEqual({ok, <<"{\n \"a\": 1,\n \"b\": 2\n}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 1}, {space, 1}])),
  281. ?assertEqual({ok, <<"{\n \"a\": 1,\n \"b\": 2\n}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 2}, {space, 2}]))
  282. end},
  283. %% `map_unknown_value` option
  284. {"`map_unknown_value` option",
  285. fun () ->
  286. Input = [{1,2,3,4}],
  287. MapFun = fun ({_,_,_,_} = Ip4) -> {ok, list_to_binary(inet:ntoa(Ip4))};
  288. (_) -> error
  289. end,
  290. Expected = <<"[\"1.2.3.4\"]">>,
  291. ?assertEqual(Expected, jsone:encode(Input, [{map_unknown_value, MapFun}]))
  292. end},
  293. {"`map_unknown_value` option with singleton tuple",
  294. fun () ->
  295. Input = [{foo}],
  296. MapFun = fun (Value) -> {ok, unicode:characters_to_binary(io_lib:format("~p~n", [Value]))}
  297. end,
  298. Expected = <<"[\"{foo}\\n\"]">>,
  299. ?assertEqual(Expected, jsone:encode(Input, [{map_unknown_value, MapFun}]))
  300. end},
  301. %% Others
  302. {"compound data",
  303. fun () ->
  304. Input = [true, {[{<<"1">>, 2}, {<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, [], ?OBJ0, false]}]}, null],
  305. Expected = <<"[true,{\"1\":2,\"array\":[[[[1]]],{\"ab\":\"cd\"},[],{},false]},null]">>,
  306. ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
  307. PpExpected = <<"[\n true,\n {\n \"1\": 2,\n \"array\": [\n [\n [\n [\n 1\n ]\n ]\n ],\n {\n \"ab\": \"cd\"\n },\n [],\n {},\n false\n ]\n },\n null\n]">>,
  308. ?assertEqual({ok, PpExpected}, jsone_encode:encode(Input, [{indent, 1}, {space, 1}]))
  309. end},
  310. {"invalid value",
  311. fun () ->
  312. ?assertMatch({error, {badarg, _}}, jsone_encode:encode(self()))
  313. end},
  314. {"wrong option",
  315. fun () ->
  316. ?assertError(badarg, jsone_encode:encode(1, [{no_such_option, hoge}]))
  317. end},
  318. {"canonical_form",
  319. fun () ->
  320. Obj1 = ?OBJECT_FROM_LIST( [{<<"key", (integer_to_binary(I))/binary >>, I} || I <- lists:seq(1000, 0, -1)] ),
  321. Obj2 = ?OBJECT_FROM_LIST( [{<<"key", (integer_to_binary(I))/binary >>, I} || I <- lists:seq(0, 1000, 1)] ),
  322. ?assertEqual(jsone_encode:encode(Obj1, [canonical_form]), jsone_encode:encode(Obj2, [canonical_form]))
  323. end}
  324. ].