123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- %% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
- %% coding: latin-1
- -module(jsone_encode_tests).
- -include_lib("eunit/include/eunit.hrl").
- -ifdef('NO_MAP_TYPE').
- -define(OBJ0, {[]}).
- -define(OBJ1(K, V), {[{K, V}]}).
- -define(OBJ2(K1, V1, K2, V2), {[{K1, V1}, {K2, V2}]}).
- -define(OBJECT_FROM_LIST(List), List).
- -else.
- -define(OBJ0, #{}).
- -define(OBJ1(K, V), #{K => V}).
- -define(OBJ2(K1, V1, K2, V2), #{K1 => V1, K2 => V2}).
- -define(OBJECT_FROM_LIST(List), maps:from_list(List)).
- -endif.
- encode_test_() ->
- [
- %% Symbols
- {"false", fun () -> ?assertEqual({ok, <<"false">>}, jsone_encode:encode(false))end},
- {"true", fun () -> ?assertEqual({ok, <<"true">>}, jsone_encode:encode(true))end},
- {"null", fun () -> ?assertEqual({ok, <<"null">>}, jsone_encode:encode(null))end},
- %% Numbers: Inline json term
- {"json",
- fun () ->
- ?assertEqual({ok, <<"{\"foo\":[1,2,3],\"bar\":\"", 195, 169, "ok\"}">>},
- jsone_encode:encode(?OBJ2(foo,
- {{json, ["[" | [$1, ",2", <<",3]">>]]}},
- <<"bar">>,
- {{json_utf8, [$", 233, "ok", $"]}}))),
- ?assertEqual({ok, <<"{\"foo\":[1,2,3],\"bar\":\"", 233, "ok\"}">>},
- jsone_encode:encode(?OBJ2(foo,
- {{json, ["[" | [$1, ",2", <<",3]">>]]}},
- <<"bar">>,
- {{json, [$", 233, "ok", $"]}}))),
- ?assertEqual({ok, <<"{\"json\":\"[1,2,3]\"}">>}, jsone_encode:encode([{json, <<"[1,2,3]">>}])),
- ?assertEqual({ok, <<"[[1,2,3]]">>}, jsone_encode:encode([{{json, <<"[1,2,3]">>}}]))
- end},
- %% Numbers: Integer
- {"zero", fun () -> ?assertEqual({ok, <<"0">>}, jsone_encode:encode(0))end},
- {"positive integer", fun () -> ?assertEqual({ok, <<"1">>}, jsone_encode:encode(1))end},
- {"negative integer", fun () -> ?assertEqual({ok, <<"-1">>}, jsone_encode:encode(-1))end},
- {"large number",
- fun () ->
- ?assertEqual({ok, <<"11111111111111111111111111111111111111111111111111111111111111111111111">>},
- jsone_encode:encode(11111111111111111111111111111111111111111111111111111111111111111111111))
- end},
- %% Numbers: Float",
- {"float",
- fun () ->
- Input = 1.234,
- ?assertMatch({ok, _}, jsone_encode:encode(Input)),
- ?assertEqual(Input, binary_to_float(element(2, jsone_encode:encode(Input))))
- end},
- {"float_format option",
- fun () ->
- Input = 1.23,
- ?assertEqual({ok, <<"1.22999999999999998224e+00">>}, jsone_encode:encode(Input)),
- ?assertEqual({ok, <<"1.2300e+00">>}, jsone_encode:encode(Input, [{float_format, [{scientific, 4}]}])),
- ?assertEqual({ok, <<"1.2e+00">>}, jsone_encode:encode(Input, [{float_format, [{scientific, 1}]}])),
- ?assertEqual({ok, <<"1.2300">>}, jsone_encode:encode(Input, [{float_format, [{decimals, 4}]}])),
- ?assertEqual({ok, <<"1.23">>}, jsone_encode:encode(Input, [{float_format, [{decimals, 4}, compact]}]))
- end},
- %% Strings
- {"simple string", fun () -> ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(<<"abc">>))end},
- {"atom is regarded as string", fun () -> ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(abc))end},
- {"string: contains escaped characters",
- fun () ->
- Input = <<"\"\/\\\b\f\n\r\t">>,
- Expected = list_to_binary([$", [[$\\, C] || C <- [$", $/, $\\, $b, $f, $n, $r, $t]], $"]),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input, [native_utf8]))
- end},
- {"string: contains forward slashes",
- fun () ->
- Input = <<"1/2">>,
- ?assertEqual({ok, <<"\"1\\/2\"">>}, jsone_encode:encode(Input)),
- ?assertEqual({ok, <<"\"1/2\"">>}, jsone_encode:encode(Input, [native_forward_slash]))
- end},
- {"string: contains control characters",
- fun () ->
- Ctrls = lists:seq(16#00, 16#1F) -- [$\b, $\f, $\n, $\r, $\t],
- Input = list_to_binary(Ctrls),
- Expected = list_to_binary([$", [io_lib:format("\\u00~2.16.0b", [C]) || C <- Ctrls], $"]),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input, [native_utf8]))
- end},
- {"string: contains multi-byte (UTF-8 encoded) characters",
- fun () ->
- %% japanese
- Input1 = <<"あいうえお">>, % assumed that the encoding of this file is UTF-8
- Expected1 = <<"\"\\u3042\\u3044\\u3046\\u3048\\u304a\"">>,
- ?assertEqual({ok, Expected1}, jsone_encode:encode(Input1)),
- Expected12 = <<$", Input1/binary, $">>,
- ?assertEqual({ok, Expected12}, jsone_encode:encode(Input1, [native_utf8])),
- %% other multi-byte characters
- Input2 = <<"۞ႮႯ">>,
- Expected2 = <<"\"\\u06dd\\u06de\\u10ae\\u10af\"">>,
- ?assertEqual({ok, Expected2}, jsone_encode:encode(Input2)),
- Expected22 = <<$", Input2/binary, $">>,
- ?assertEqual({ok, Expected22}, jsone_encode:encode(Input2, [native_utf8]))
- end},
- {"string: contains surrogate pairs",
- fun () ->
- Input = <<"𢁉𢂚𢃼">>,
- Expected = <<"\"\\ud848\\udc49\\ud848\\udc9a\\ud848\\udcfc\"">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
- end},
- %% Strings variant: Datetimes
- {"datetime: iso8601: utc",
- fun () ->
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>}, jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}})),
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, iso8601}])),
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, {iso8601, utc}}]))
- end},
- {"datetime: iso8601: local",
- fun () ->
- {ok, Json} = jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, {iso8601, local}}]),
- UTC = {{1970, 1, 2}, {0, 0, 0}},
- Local = calendar:universal_time_to_local_time({{1970, 1, 2}, {0, 0, 0}}),
- case UTC =:= Local of
- false ->
- ?assertMatch(<<"\"2015-06-25T14:57:25", _:6/binary, "\"">>, Json);
- true ->
- ?assertMatch(<<"\"2015-06-25T14:57:25Z\"">>, Json)
- end
- end},
- {"datetime: iso8601: timezone",
- fun () ->
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25Z\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, {iso8601, 0}}])),
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25+00:01\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, {iso8601, 60}}])),
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25-00:01\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25}}, [{datetime_format, {iso8601, -60}}]))
- end},
- {"datetime as head of array",
- ?_assertEqual({ok, <<"[\"2015-06-25T14:57:25Z\"]">>}, jsone_encode:encode([{{2015, 6, 25}, {14, 57, 25}}]))},
- {"datetime: iso8601: with fractions of seconds",
- fun () ->
- ?assertEqual({ok, <<"\"2015-06-25T14:57:25.325Z\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 25.3245}})),
- ?assertEqual({ok, <<"\"2015-06-25T14:57:05.320Z\"">>},
- jsone_encode:encode({{2015, 6, 25}, {14, 57, 5.32}}))
- end},
- %% Arrays
- {"simple array",
- fun () ->
- Input = [1, 2, 3],
- Expected = <<"[1,2,3]">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
- end},
- {"empty array",
- fun () ->
- Input = [],
- Expected = <<"[]">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
- end},
- %% Objects
- {"simple object",
- fun () ->
- Input1 = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
- Input2 = [{<<"key">>, <<"value">>}, {<<"1">>, 2}],
- Expected = <<"{\"key\":\"value\",\"1\":2}">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
- end},
- {"empty object",
- fun () ->
- Input1 = {[]},
- Input2 = [{}],
- Expected = <<"{}">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
- end},
- {"simple object: map",
- fun () ->
- Input = ?OBJ2(<<"1">>, 2, <<"key">>, <<"value">>),
- Expected = <<"{\"1\":2,\"key\":\"value\"}">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
- end},
- {"empty object: map",
- fun () ->
- Input = ?OBJ0,
- Expected = <<"{}">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
- end},
- {"atom key is allowed",
- fun () ->
- Expected = <<"{\"key\":2}">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode({[{key, 2}]}))
- end},
- {"object_key_type option",
- fun () ->
- %% key: atom
- ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, string}])), % OK
- ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, scalar}])), % OK
- ?assertEqual({ok, <<"{\"a\":2}">>}, jsone_encode:encode(?OBJ1(a, 2), [{object_key_type, value}])), % OK
- %% key: number
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, string}])), % NG
- ?assertEqual({ok, <<"{\"1\":2}">>}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, scalar}])), % OK
- ?assertEqual({ok, <<"{\"1\":2}">>}, jsone_encode:encode(?OBJ1(1, 2), [{object_key_type, value}])), % OK
- %% key: datetime
- ?assertMatch({error, {badarg, _}},
- jsone_encode:encode(?OBJ1({{2000, 1, 1}, {0, 0, 0}}, 2), [{object_key_type, string}])), % NG
- ?assertEqual({ok, <<"{\"2000-01-01T00:00:00Z\":2}">>},
- jsone_encode:encode(?OBJ1({{2000, 1, 1}, {0, 0, 0}}, 2), [{object_key_type, scalar}])), % OK
- ?assertEqual({ok, <<"{\"2000-01-01T00:00:00Z\":2}">>},
- jsone_encode:encode(?OBJ1({{2000, 1, 1}, {0, 0, 0}}, 2), [{object_key_type, value}])), % OK
- %% key: array
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, string}])), % NG
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, scalar}])), % NG
- ?assertEqual({ok, <<"{\"[1]\":2}">>}, jsone_encode:encode(?OBJ1([1], 2), [{object_key_type, value}])), % OK
- %% key: object
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, string}])), % NG
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, scalar}])), % NG
- ?assertEqual({ok, <<"{\"{}\":2}">>}, jsone_encode:encode(?OBJ1(?OBJ0, 2), [{object_key_type, value}])) % OK
- end},
- {"non binary object member key is disallowed",
- fun () ->
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{1, 2}]})),
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{"1", 2}]}))
- end},
- {"undefined_as_null option",
- fun () ->
- ?assertEqual({ok, <<"null">>}, jsone_encode:encode(undefined, [undefined_as_null])), % OK
- ?assertEqual({ok, <<"\"undefined\"">>}, jsone_encode:encode(undefined, [])) % OK
- end},
- {"skip_undefined option",
- fun () ->
- Object = #{<<"1">> => undefined, <<"2">> => 3, <<"3">> => undefined},
- ?assertEqual({ok, <<"{\"1\":null,\"2\":3,\"3\":null}">>},
- jsone_encode:encode(Object, [undefined_as_null])),
- ?assertEqual({ok, <<"{\"2\":3}">>}, jsone_encode:encode(Object, [skip_undefined]))
- end},
- %% Pretty Print
- {"space",
- fun () ->
- ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{space, 1}])),
- ?assertEqual({ok, <<"[1, 2, 3]">>}, jsone_encode:encode([1, 2, 3], [{space, 1}])),
- ?assertEqual({ok, <<"[1, 2, 3]">>}, jsone_encode:encode([1, 2, 3], [{space, 2}])),
- ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{space, 1}])),
- ?assertEqual({ok, <<"{\"a\": 1, \"b\": 2}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{space, 1}])),
- ?assertEqual({ok, <<"{\"a\": 1, \"b\": 2}">>}, jsone_encode:encode(?OBJ2(a, 1, b, 2), [{space, 2}]))
- end},
- {"indent",
- fun () ->
- ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{indent, 1}])),
- ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1, 2, 3], [{indent, 1}])),
- ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1, 2, 3], [{indent, 2}])),
- ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{indent, 1}])),
- ?assertEqual({ok, <<"{\n \"a\":1,\n \"b\":2\n}">>},
- jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 1}])),
- ?assertEqual({ok, <<"{\n \"a\":1,\n \"b\":2\n}">>},
- jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 2}]))
- end},
- {"indent+space",
- fun () ->
- ?assertEqual({ok, <<"[]">>}, jsone_encode:encode([], [{indent, 1}, {space, 1}])),
- ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>}, jsone_encode:encode([1, 2, 3], [{indent, 1}, {space, 1}])),
- ?assertEqual({ok, <<"[\n 1,\n 2,\n 3\n]">>},
- jsone_encode:encode([1, 2, 3], [{indent, 2}, {space, 2}])),
- ?assertEqual({ok, <<"{}">>}, jsone_encode:encode(?OBJ0, [{indent, 1}, {space, 1}])),
- ?assertEqual({ok, <<"{\n \"a\": 1,\n \"b\": 2\n}">>},
- jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 1}, {space, 1}])),
- ?assertEqual({ok, <<"{\n \"a\": 1,\n \"b\": 2\n}">>},
- jsone_encode:encode(?OBJ2(a, 1, b, 2), [{indent, 2}, {space, 2}]))
- end},
- %% `map_unknown_value` option
- {"`map_unknown_value` option",
- fun () ->
- Input = [{1, 2, 3, 4}],
- MapFun =
- fun ({_, _, _, _} = Ip4) ->
- {ok, list_to_binary(inet:ntoa(Ip4))};
- (_) ->
- error
- end,
- Expected = <<"[\"1.2.3.4\"]">>,
- ?assertEqual(Expected, jsone:encode(Input, [{map_unknown_value, MapFun}]))
- end},
- {"`map_unknown_value` option with singleton tuple",
- fun () ->
- Input = [{foo}],
- MapFun = fun (Value) -> {ok, unicode:characters_to_binary(io_lib:format("~p~n", [Value]))}end,
- Expected = <<"[\"{foo}\\n\"]">>,
- ?assertEqual(Expected, jsone:encode(Input, [{map_unknown_value, MapFun}]))
- end},
- {"IP address",
- fun () ->
- Input = #{ip => {127, 0, 0, 1}},
- Expected = <<"{\"ip\":\"127.0.0.1\"}">>,
- ?assertEqual(Expected, jsone:encode(Input, [{map_unknown_value, fun jsone:ip_address_to_json_string/1}])),
- %% Without `map_unknown_value' option.
- ?assertMatch({error, _}, jsone:try_encode(Input, [{map_unknown_value, undefined}]))
- end},
- %% Others
- {"compound data",
- fun () ->
- Input =
- [true, {[{<<"1">>, 2}, {<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, [], ?OBJ0, false]}]}, null],
- Expected = <<"[true,{\"1\":2,\"array\":[[[[1]]],{\"ab\":\"cd\"},[],{},false]},null]">>,
- ?assertEqual({ok, Expected}, jsone_encode:encode(Input)),
- 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]">>,
- ?assertEqual({ok, PpExpected}, jsone_encode:encode(Input, [{indent, 1}, {space, 1}]))
- end},
- {"invalid value",
- fun () ->
- Pid = self(),
- PidString = list_to_binary(io_lib:format("~p", [Pid])),
- ?assertEqual({ok, <<$", PidString/binary, $">>}, jsone_encode:encode(Pid)),
- ?assertMatch({error, {badarg, _}}, jsone_encode:encode(Pid, [{map_unknown_value, undefined}]))
- end},
- {"wrong option", fun () -> ?assertError(badarg, jsone_encode:encode(1, [{no_such_option, hoge}]))end},
- {"canonical_form",
- fun () ->
- Obj1 = ?OBJECT_FROM_LIST([{<<"key", (integer_to_binary(I))/binary>>, I} || I <- lists:seq(1000, 0, -1)]),
- Obj2 = ?OBJECT_FROM_LIST([{<<"key", (integer_to_binary(I))/binary>>, I} || I <- lists:seq(0, 1000, 1)]),
- ?assertEqual(jsone_encode:encode(Obj1, [canonical_form]), jsone_encode:encode(Obj2, [canonical_form]))
- end}].
|