blob: 23aa38d7651f46e94f8c785e13178aa12ef4cd9d [file] [log] [blame]
% This file is part of Jiffy released under the MIT license.
% See the LICENSE file for more information.
-module(jiffy_11_proper_tests).
-ifdef(JIFFY_DEV).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("jiffy_util.hrl").
opts() ->
[
{max_size, 15},
{numtests, 1000}
].
run(Name) ->
{msg("~s", [Name]), [
{timeout, 300, ?_assert(proper:quickcheck(?MODULE:Name(), opts()))}
]}.
proper_encode_decode_test_() ->
[
run(prop_enc_dec),
run(prop_enc_dec_pretty),
run(prop_dec_trailer),
run(prop_enc_no_crash),
run(prop_dec_no_crash_bin),
run(prop_dec_no_crash_any)
].
prop_enc_dec() ->
?FORALL(Data, json(),
begin
%io:format(standard_error, "Data: ~p~n", [Data]),
Data == jiffy:decode(jiffy:encode(Data))
end
).
prop_dec_trailer() ->
?FORALL({T1, T2}, {json(), json()},
begin
B1 = jiffy:encode(T1),
B2 = jiffy:encode(T2),
Combiners = [
<<" ">>,
<<"\r\t">>,
<<"\n \t">>,
<<" ">>
],
lists:foreach(fun(Comb) ->
Bin = <<B1/binary, Comb/binary, B2/binary>>,
{has_trailer, T1, Rest} = jiffy:decode(Bin, [return_trailer]),
T2 = jiffy:decode(Rest)
end, Combiners),
true
end
).
-ifndef(JIFFY_NO_MAPS).
to_map_ejson({Props}) ->
NewProps = [{K, to_map_ejson(V)} || {K, V} <- Props],
maps:from_list(NewProps);
to_map_ejson(Vals) when is_list(Vals) ->
[to_map_ejson(V) || V <- Vals];
to_map_ejson(Val) ->
Val.
prop_map_enc_dec() ->
?FORALL(Data, json(),
begin
MapData = to_map_ejson(Data),
MapData == jiffy:decode(jiffy:encode(MapData), [return_maps])
end
).
-endif.
prop_enc_dec_pretty() ->
?FORALL(Data, json(),
begin
Data == jiffy:decode(jiffy:encode(Data, [pretty]))
end
).
prop_enc_no_crash() ->
?FORALL(Data, any(), begin catch jiffy:encode(Data), true end).
prop_dec_no_crash_bin() ->
?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end).
prop_dec_no_crash_any() ->
?FORALL(Data, any(), begin catch jiffy:decode(Data), true end).
% JSON Generation
json_null() ->
null.
json_boolean() ->
oneof([true, false]).
json_number() ->
oneof([integer(), float()]).
json_string() ->
escaped_utf8_bin().
json_list(S) when S =< 0 ->
[];
json_list(S) ->
?LETSHRINK(
[ListSize],
[integer(0, S)],
vector(ListSize, json_text(S - ListSize))
).
json_object(S) when S =< 0 ->
{[]};
json_object(S) ->
?LETSHRINK(
[ObjectSize],
[integer(0, S)],
{vector(ObjectSize, {json_string(), json_text(S - ObjectSize)})}
).
json_value() ->
oneof([
json_null(),
json_boolean(),
json_string(),
json_number()
]).
json_text(S) when S > 0 ->
?LAZY(oneof([
json_list(S),
json_object(S)
]));
json_text(_) ->
json_value().
json() ->
?SIZED(S, json_text(S)).
%% XXX: Add generators
%
% We should add generators that generate JSON binaries directly
% so we can test things that aren't produced by the encoder.
%
% We should also have a version of the JSON generator that inserts
% errors into the JSON that we can test for.
escaped_utf8_bin() ->
?SUCHTHAT(Bin,
?LET(S, ?SUCHTHAT(L, list(escaped_char()), L /= []),
unicode:characters_to_binary(S, unicode, utf8)),
is_binary(Bin)
).
escaped_char() ->
?LET(C, char(),
case C of
$" -> "\\\"";
C when C == 65534 -> 65533;
C when C == 65535 -> 65533;
C when C > 1114111 -> 1114111;
C -> C
end
).
-endif.