|
|
@ -0,0 +1,138 @@ |
|
|
|
% This file is part of Jiffy released under the MIT license. |
|
|
|
% See the LICENSE file for more information. |
|
|
|
|
|
|
|
-module(jiffy_tests). |
|
|
|
|
|
|
|
-include_lib("proper/include/proper.hrl"). |
|
|
|
-include_lib("eunit/include/eunit.hrl"). |
|
|
|
|
|
|
|
|
|
|
|
proper_test_() -> |
|
|
|
PropErOpts = [ |
|
|
|
{to_file, user}, |
|
|
|
{max_size, 60}, |
|
|
|
{numtests, 1000} |
|
|
|
], |
|
|
|
{timeout, 3600, ?_assertEqual([], proper:module(jiffy_tests, PropErOpts))}. |
|
|
|
|
|
|
|
|
|
|
|
prop_encode_decode() -> |
|
|
|
?FORALL(Data, json(), |
|
|
|
begin |
|
|
|
%io:format(standard_error, "Data: ~p~n", [Data]), |
|
|
|
ExpData = conv_keys(Data), |
|
|
|
ExpData == jiffy:decode(jiffy:encode(Data)) |
|
|
|
end |
|
|
|
). |
|
|
|
|
|
|
|
prop_encode_not_crash() -> |
|
|
|
?FORALL(Data, any(), begin catch jiffy:encode(Data), true end). |
|
|
|
|
|
|
|
prop_decode_not_crash_bin() -> |
|
|
|
?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end). |
|
|
|
|
|
|
|
prop_decode_not_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_key() -> |
|
|
|
oneof([json_string(), escaped_atom()]). |
|
|
|
|
|
|
|
|
|
|
|
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_key(), 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)). |
|
|
|
|
|
|
|
|
|
|
|
escaped_atom() -> |
|
|
|
?LET(A, atom(), |
|
|
|
unicode:characters_to_binary(atom_to_list(A), unicode, utf8) |
|
|
|
). |
|
|
|
|
|
|
|
|
|
|
|
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 -> C |
|
|
|
end |
|
|
|
). |
|
|
|
|
|
|
|
|
|
|
|
% Atoms get munged to binaries in the round trip. |
|
|
|
conv_keys({Props}) -> |
|
|
|
{lists:map(fun({K, V}) -> {conv_key(K), conv_keys(V)} end, Props)}; |
|
|
|
conv_keys(Vals) when is_list(Vals) -> |
|
|
|
lists:map(fun(V) -> conv_keys(V) end, Vals); |
|
|
|
conv_keys(Other) -> |
|
|
|
Other. |
|
|
|
|
|
|
|
conv_key(K) when is_atom(K) -> |
|
|
|
list_to_binary(atom_to_list(K)); |
|
|
|
conv_key(K) -> |
|
|
|
K. |
|
|
|
|