more tests plus associated bug fixes, some tests still broken (in the sense they are not being run, not that they fail)
This commit is contained in:
parent
80d42c4e49
commit
843b3cdf24
3 changed files with 117 additions and 27 deletions
|
@ -127,7 +127,7 @@ collect({event, end_json, _Next}, [[Acc]], _Opts) ->
|
||||||
%% the head of the accumulator and deal with it when we receive it's paired value
|
%% the head of the accumulator and deal with it when we receive it's paired value
|
||||||
collect({event, {key, _} = PreKey, Next}, [Current|_] = Acc, Opts) ->
|
collect({event, {key, _} = PreKey, Next}, [Current|_] = Acc, Opts) ->
|
||||||
Key = event(PreKey, Opts),
|
Key = event(PreKey, Opts),
|
||||||
case key_repeats(Key, Current) of
|
case decode_key_repeats(Key, Current) of
|
||||||
true -> erlang:error(badarg)
|
true -> erlang:error(badarg)
|
||||||
; false -> collect(Next(), [Key] ++ Acc, Opts)
|
; false -> collect(Next(), [Key] ++ Acc, Opts)
|
||||||
end;
|
end;
|
||||||
|
@ -141,6 +141,14 @@ collect({event, Event, Next}, [Current|Rest], Opts) when is_list(Current) ->
|
||||||
collect({event, Event, Next}, [Key, Current|Rest], Opts) ->
|
collect({event, Event, Next}, [Key, Current|Rest], Opts) ->
|
||||||
collect(Next(), [[{Key, event(Event, Opts)}] ++ Current] ++ Rest, Opts);
|
collect(Next(), [[{Key, event(Event, Opts)}] ++ Current] ++ Rest, Opts);
|
||||||
|
|
||||||
|
%% if our first returned event is {incomplete, ...} try to force end and return the
|
||||||
|
%% Event if one is returned
|
||||||
|
collect({incomplete, More}, [[]], Opts) ->
|
||||||
|
case More(end_stream) of
|
||||||
|
{event, Event, _Next} -> event(Event, Opts)
|
||||||
|
; _ -> erlang:error(badarg)
|
||||||
|
end;
|
||||||
|
|
||||||
%% any other event is an error
|
%% any other event is an error
|
||||||
collect(_, _, _) -> erlang:error(badarg).
|
collect(_, _, _) -> erlang:error(badarg).
|
||||||
|
|
||||||
|
@ -172,6 +180,12 @@ event({float, Float}, _Opts) ->
|
||||||
event({literal, Literal}, _Opts) ->
|
event({literal, Literal}, _Opts) ->
|
||||||
Literal.
|
Literal.
|
||||||
|
|
||||||
|
|
||||||
|
decode_key_repeats(Key, [{Key, _Value}|_Rest]) -> true;
|
||||||
|
decode_key_repeats(Key, [_|Rest]) -> decode_key_repeats(Key, Rest);
|
||||||
|
decode_key_repeats(_Key, []) -> false.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%% convert eep0018 representation to jsx events. note special casing for the empty object
|
%% convert eep0018 representation to jsx events. note special casing for the empty object
|
||||||
|
|
||||||
|
@ -188,7 +202,7 @@ term_to_events(Term) ->
|
||||||
proplist_to_events([{Key, Term}|Rest], Acc) ->
|
proplist_to_events([{Key, Term}|Rest], Acc) ->
|
||||||
Event = term_to_event(Term),
|
Event = term_to_event(Term),
|
||||||
EncodedKey = key_to_event(Key),
|
EncodedKey = key_to_event(Key),
|
||||||
case key_repeats(EncodedKey, Acc) of
|
case encode_key_repeats(EncodedKey, Acc) of
|
||||||
false -> proplist_to_events(Rest, Event ++ EncodedKey ++ Acc)
|
false -> proplist_to_events(Rest, Event ++ EncodedKey ++ Acc)
|
||||||
; true -> erlang:error(badarg)
|
; true -> erlang:error(badarg)
|
||||||
end;
|
end;
|
||||||
|
@ -223,6 +237,16 @@ key_to_event(Key) when is_atom(Key) ->
|
||||||
key_to_event(Key) when is_binary(Key) ->
|
key_to_event(Key) when is_binary(Key) ->
|
||||||
[{key, json_escape(Key)}].
|
[{key, json_escape(Key)}].
|
||||||
|
|
||||||
|
|
||||||
|
encode_key_repeats([Key], SoFar) -> encode_key_repeats(Key, SoFar, 0).
|
||||||
|
|
||||||
|
encode_key_repeats(Key, [Key|_], 0) -> true;
|
||||||
|
encode_key_repeats(Key, [end_object|Rest], Level) -> encode_key_repeats(Key, Rest, Level + 1);
|
||||||
|
encode_key_repeats(Key, [start_object|_], 0) -> false;
|
||||||
|
encode_key_repeats(Key, [start_object|Rest], Level) -> encode_key_repeats(Key, Rest, Level - 1);
|
||||||
|
encode_key_repeats(Key, [_|Rest], Level) -> encode_key_repeats(Key, Rest, Level);
|
||||||
|
encode_key_repeats(_, [], 0) -> false.
|
||||||
|
|
||||||
|
|
||||||
%% conversion of floats to 'nice' decimal output. erlang's float implementation is almost
|
%% conversion of floats to 'nice' decimal output. erlang's float implementation is almost
|
||||||
%% but not quite ieee 754. it converts negative zero to plain zero silently, and throws
|
%% but not quite ieee 754. it converts negative zero to plain zero silently, and throws
|
||||||
|
@ -393,17 +417,49 @@ to_hex(10) -> $a;
|
||||||
to_hex(X) -> X + $0.
|
to_hex(X) -> X + $0.
|
||||||
|
|
||||||
|
|
||||||
%% common functions
|
|
||||||
|
|
||||||
key_repeats([{key, Key}], [{key, Key}|_Rest]) -> true;
|
|
||||||
key_repeats(Key, [{Key, _Value}|_Rest]) -> true;
|
|
||||||
key_repeats(Key, [_|Rest]) -> key_repeats(Key, Rest);
|
|
||||||
key_repeats(_Key, []) -> false.
|
|
||||||
|
|
||||||
|
|
||||||
%% eunit tests
|
%% eunit tests
|
||||||
-ifdef(test).
|
-ifdef(test).
|
||||||
|
|
||||||
|
decode_test_() ->
|
||||||
|
[
|
||||||
|
{"empty object", ?_assert(json_to_term(<<"{}">>, []) =:= [{}])},
|
||||||
|
{"empty array", ?_assert(json_to_term(<<"[]">>, []) =:= [])},
|
||||||
|
{"simple object", ?_assert(json_to_term(<<"{\"a\": true, \"b\": true, \"c\": true}">>, [{label, atom}]) =:= [{a, true}, {b, true}, {c, true}])},
|
||||||
|
{"simple array", ?_assert(json_to_term(<<"[true,true,true]">>, []) =:= [true, true, true])},
|
||||||
|
{"nested structures", ?_assert(json_to_term(<<"{\"list\":[{\"list\":[{}, {}],\"object\":{}}, []],\"object\":{}}">>, [{label, atom}]) =:= [{list, [[{list, [[{}], [{}]]}, {object, [{}]}],[]]}, {object, [{}]}])},
|
||||||
|
{"numbers", ?_assert(json_to_term(<<"[-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0]">>, []) =:= [-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0])},
|
||||||
|
{"numbers (all floats)", ?_assert(json_to_term(<<"[-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0]">>, [{float, true}]) =:= [-10000000000.0, -1.0, 0.0, 0.0, 1.0, 10000000000.0, 1000000000.0])},
|
||||||
|
{"strings", ?_assert(json_to_term(<<"[\"a string\"]">>, []) =:= [<<"a string">>])},
|
||||||
|
{"literals", ?_assert(json_to_term(<<"[true,false,null]">>, []) =:= [true,false,null])},
|
||||||
|
{"naked true", ?_assert(json_to_term(<<"true">>, [{strict, false}]) =:= true)},
|
||||||
|
{"naked short number", ?_assert(json_to_term(<<"1">>, [{strict, false}]) =:= 1)},
|
||||||
|
{"float", ?_assert(json_to_term(<<"1.0">>, [{strict, false}]) =:= 1.0)},
|
||||||
|
{"naked string", ?_assert(json_to_term(<<"\"hello world\"">>, [{strict, false}]) =:= <<"hello world">>)},
|
||||||
|
{"comments", ?_assert(json_to_term(<<"[ /* a comment in an empty array */ ]">>, [{comments, true}]) =:= [])}
|
||||||
|
].
|
||||||
|
|
||||||
|
encode_test_() ->
|
||||||
|
[
|
||||||
|
{"empty object", ?_assert(term_to_json([{}], []) =:= <<"{}">>)},
|
||||||
|
{"empty array", ?_assert(term_to_json([], []) =:= <<"[]">>)},
|
||||||
|
{"simple object", ?_assert(term_to_json([{a, true}, {b, true}, {c, true}], []) =:= <<"{\"a\":true,\"b\":true,\"c\":true}">>)},
|
||||||
|
{"simple array", ?_assert(term_to_json([true, true, true], []) =:= <<"[true,true,true]">>)},
|
||||||
|
{"nested structures", ?_assert(term_to_json([{list, [[{list, [[{}], [{}]]}, {object, [{}]}],[]]}, {object, [{}]}], []) =:= <<"{\"list\":[{\"list\":[{},{}],\"object\":{}},[]],\"object\":{}}">>)},
|
||||||
|
{"numbers", ?_assert(term_to_json([-10000000000.0, -1, 0.0, 0, 1, 10000000000, 1000000000.0], []) =:= <<"[-1.0e10,-1,0.0,0,1,10000000000,1.0e9]">>)},
|
||||||
|
{"strings", ?_assert(term_to_json([<<"a string">>], []) =:= <<"[\"a string\"]">>)},
|
||||||
|
{"literals", ?_assert(term_to_json([true,false,null], []) =:= <<"[true,false,null]">>)},
|
||||||
|
{"naked true", ?_assert(term_to_json(true, [{strict, false}]) =:= <<"true">>)},
|
||||||
|
{"naked number", ?_assert(term_to_json(1, [{strict, false}]) =:= <<"1">>)},
|
||||||
|
{"float", ?_assert(term_to_json(1.0, [{strict, false}]) =:= <<"1.0">>)},
|
||||||
|
{"naked string", ?_assert(term_to_json(<<"hello world">>, [{strict, false}]) =:= <<"\"hello world\"">>)}
|
||||||
|
].
|
||||||
|
|
||||||
|
repeated_keys_test_() ->
|
||||||
|
[
|
||||||
|
{"encode", ?_assertError(badarg, term_to_json([{k, true}, {k, false}], []))},
|
||||||
|
{"decode", ?_assertError(badarg, json_to_term(<<"{\"k\": true, \"k\": false}">>, []))}
|
||||||
|
].
|
||||||
|
|
||||||
escape_test_() ->
|
escape_test_() ->
|
||||||
[
|
[
|
||||||
{"json string escaping", ?_assert(json_escape(<<"\"\\\b\f\n\r\t">>) =:= <<"\\\"\\\\\\b\\f\\n\\r\\t">>)},
|
{"json string escaping", ?_assert(json_escape(<<"\"\\\b\f\n\r\t">>) =:= <<"\\\"\\\\\\b\\f\\n\\r\\t">>)},
|
||||||
|
@ -427,15 +483,5 @@ nice_decimal_test_() ->
|
||||||
{"min denormalized float", ?_assert(float_to_decimal(math:pow(2, -1074)) =:= "5.0e-324")},
|
{"min denormalized float", ?_assert(float_to_decimal(math:pow(2, -1074)) =:= "5.0e-324")},
|
||||||
{"max denormalized float", ?_assert(float_to_decimal((1 - math:pow(2, -52)) * math:pow(2, -1022)) =:= "2.225073858507201e-308")}
|
{"max denormalized float", ?_assert(float_to_decimal((1 - math:pow(2, -52)) * math:pow(2, -1022)) =:= "2.225073858507201e-308")}
|
||||||
].
|
].
|
||||||
|
|
||||||
key_repeats_test_() ->
|
|
||||||
[
|
|
||||||
{"encoded key repeat", ?_assert(key_repeats([{key, <<"key">>}], [{key, <<>>}, {key, <<"notkey">>}, {key, <<"key">>}, {key, <<"trailing key">>}]) =:= true)},
|
|
||||||
{"encoded key no repeat", ?_assert(key_repeats([{key, <<"key">>}], [{key, <<>>}, {key, <<"notkey">>}, {key, <<"trailing key">>}]) =:= false)},
|
|
||||||
{"decoded key (atom) repeat", ?_assert(key_repeats(key, [{notkey, true}, {key, true}, {trailing_key, true}]) =:= true)},
|
|
||||||
{"decoded key (binary) repeat", ?_assert(key_repeats(<<"key">>, [{<<"notkey">>, true}, {<<"key">>, true}, {<<"trailing key">>, true}]) =:= true)},
|
|
||||||
{"decoded key (atom) no repeat", ?_assert(key_repeats(key, [{notkey, true}, {definitely_not_key, true}, {trailing_key, true}]) =:= false)},
|
|
||||||
{"decoded key (binary) no repeat", ?_assert(key_repeats(<<"key">>, [{<<"notkey">>, true}, {<<"definitely not key">>, true}, {<<"trailing key">>, true}]) =:= false)}
|
|
||||||
].
|
|
||||||
|
|
||||||
-endif.
|
-endif.
|
|
@ -81,6 +81,8 @@ parse_opts([space|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{space = 1});
|
parse_opts(Rest, Opts#opts{space = 1});
|
||||||
parse_opts([{output_encoding, Val}|Rest], Opts) ->
|
parse_opts([{output_encoding, Val}|Rest], Opts) ->
|
||||||
parse_opts(Rest, Opts#opts{output_encoding = Val});
|
parse_opts(Rest, Opts#opts{output_encoding = Val});
|
||||||
|
parse_opts([_|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts);
|
||||||
parse_opts([], Opts) ->
|
parse_opts([], Opts) ->
|
||||||
Opts.
|
Opts.
|
||||||
|
|
||||||
|
|
|
@ -42,20 +42,24 @@ test() -> erlang:error(notest).
|
||||||
-else.
|
-else.
|
||||||
|
|
||||||
jsx_decoder_test_() ->
|
jsx_decoder_test_() ->
|
||||||
jsx_decoder_gen(load_tests("./test/cases"), [utf8, utf16, {utf16, little}, utf32, {utf32, little}]).
|
jsx_decoder_gen(load_tests("./test/cases"), [utf8, utf16, {utf16, little}, utf32, {utf32, little}], fun decode/2).
|
||||||
|
|
||||||
jsx_decoder_gen([_Test|Rest], []) ->
|
jsx_incremental_test_() ->
|
||||||
jsx_decoder_gen(Rest, [utf8, utf16, {utf16, little}, utf32, {utf32, little}]);
|
jsx_decoder_gen(load_tests("./test/cases"), [utf8, utf16, {utf16, little}, utf32, {utf32, little}], fun incremental_decode/2).
|
||||||
jsx_decoder_gen([], _) ->
|
|
||||||
|
|
||||||
|
jsx_decoder_gen([_Test|Rest], [], F) ->
|
||||||
|
jsx_decoder_gen(Rest, [utf8, utf16, {utf16, little}, utf32, {utf32, little}], F);
|
||||||
|
jsx_decoder_gen([], _, _) ->
|
||||||
[];
|
[];
|
||||||
jsx_decoder_gen([Test|_] = Tests, [Encoding|Encodings]) ->
|
jsx_decoder_gen([Test|_] = Tests, [Encoding|Encodings], F) ->
|
||||||
Name = lists:flatten(proplists:get_value(name, Test) ++ " :: " ++ io_lib:format("~p", [Encoding])),
|
Name = lists:flatten(proplists:get_value(name, Test) ++ " :: " ++ io_lib:format("~p", [Encoding])),
|
||||||
JSON = unicode:characters_to_binary(proplists:get_value(json, Test), unicode, Encoding),
|
JSON = unicode:characters_to_binary(proplists:get_value(json, Test), unicode, Encoding),
|
||||||
JSX = proplists:get_value(jsx, Test),
|
JSX = proplists:get_value(jsx, Test),
|
||||||
Flags = proplists:get_value(jsx_flags, Test, []),
|
Flags = proplists:get_value(jsx_flags, Test, []),
|
||||||
{generator,
|
{generator,
|
||||||
fun() ->
|
fun() ->
|
||||||
[{Name, ?_assert(decode(JSON, Flags) =:= JSX)} | jsx_decoder_gen(Tests, Encodings)]
|
[{Name, ?_assert(F(JSON, Flags) =:= JSX)} | jsx_decoder_gen(Tests, Encodings, F)]
|
||||||
end
|
end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
@ -120,5 +124,43 @@ incremental_decode_loop({event, end_json, _Next}, _Rest, Acc) ->
|
||||||
lists:reverse([end_json] ++ Acc);
|
lists:reverse([end_json] ++ Acc);
|
||||||
incremental_decode_loop({event, Event, Next}, Rest, Acc) ->
|
incremental_decode_loop({event, Event, Next}, Rest, Acc) ->
|
||||||
incremental_decode_loop(Next(), Rest, [Event] ++ Acc).
|
incremental_decode_loop(Next(), Rest, [Event] ++ Acc).
|
||||||
|
|
||||||
|
|
||||||
|
multi_decode_test_() ->
|
||||||
|
[
|
||||||
|
{"multiple values in a single stream", ?_assert(multi_decode(multi_json_body(), []) =:= multi_test_result())}
|
||||||
|
].
|
||||||
|
|
||||||
|
|
||||||
|
multi_decode(JSON, Flags) ->
|
||||||
|
P = jsx:parser(Flags ++ [{multi_term, true}]),
|
||||||
|
multi_decode_loop(P(JSON), [[]]).
|
||||||
|
|
||||||
|
multi_decode_loop({incomplete, _Next}, [[]|Acc]) ->
|
||||||
|
lists:reverse(Acc);
|
||||||
|
multi_decode_loop({event, end_json, Next}, [S|Acc]) ->
|
||||||
|
multi_decode_loop(Next(), [[]|[lists:reverse(S)] ++ Acc]);
|
||||||
|
multi_decode_loop({event, E, Next}, [S|Acc]) ->
|
||||||
|
multi_decode_loop(Next(), [[E] ++ S] ++ Acc).
|
||||||
|
|
||||||
|
|
||||||
|
multi_json_body() ->
|
||||||
|
<<"0 1 -1 1e1 0.7 0.7e-1 true false null {} [] [1, 2, 3] \"hope this works\"">>.
|
||||||
|
|
||||||
|
multi_test_result() ->
|
||||||
|
[ [{integer, "0"}],
|
||||||
|
[{integer, "1"}],
|
||||||
|
[{integer, "-1"}],
|
||||||
|
[{float, "1.0e1"}],
|
||||||
|
[{float, "0.7"}],
|
||||||
|
[{float, "0.7e-1"}],
|
||||||
|
[{literal, true}],
|
||||||
|
[{literal, false}],
|
||||||
|
[{literal, null}],
|
||||||
|
[start_object, end_object],
|
||||||
|
[start_array, end_array],
|
||||||
|
[start_array, {integer, "1"}, {integer, "2"}, {integer, "3"}, end_array],
|
||||||
|
[{string, "hope this works"}]
|
||||||
|
].
|
||||||
|
|
||||||
-endif.
|
-endif.
|
Loading…
Add table
Add a link
Reference in a new issue