Add support for rebar3preencode-sketch
@ -1,10 +1,15 @@ | |||
.eunit | |||
.rebar | |||
.jiffy.dev | |||
*.app | |||
*.beam | |||
*.d | |||
*.o | |||
*.so | |||
_build | |||
compile_commands.json | |||
deps | |||
erln8.config | |||
hexer.config | |||
rebar.lock | |||
TEST-*.xml |
@ -1,37 +1,18 @@ | |||
% This file is part of Jiffy released under the MIT license. | |||
% See the LICENSE file for more information. | |||
% Only include PropEr as a dependency when the JIFFY_DEV | |||
% environment variable is defined. This allows downstream | |||
% applications to avoid requiring PropEr. | |||
% | |||
% This script is based on the example provided with Rebar. | |||
ErlOpts = [{d, 'JIFFY_DEV'}], | |||
% Only run the EQC checks when EQC is present. | |||
Proper = [ | |||
{proper, ".*", {git, "git://github.com/manopapad/proper.git", "master"}} | |||
], | |||
HaveEQC = code:which(eqc) =/= non_existing, | |||
ConfigPath = filename:dirname(SCRIPT), | |||
DevMarker = filename:join([ConfigPath, ".jiffy.dev"]), | |||
ErlOpts = if not HaveEQC -> []; true -> | |||
[{d, 'HAVE_EQC'}] | |||
end, | |||
case filelib:is_file(DevMarker) of | |||
true -> | |||
% Don't override existing dependencies | |||
Config0 = case lists:keyfind(deps, 1, CONFIG) of | |||
false -> | |||
CONFIG ++ [{deps, Proper}]; | |||
{deps, DepsList} -> | |||
lists:keyreplace(deps, 1, CONFIG, {deps, DepsList ++ Proper}) | |||
end, | |||
Config1 = case lists:keyfind(erl_opts, 1, Config0) of | |||
false -> | |||
Config0 ++ [{erl_opts, ErlOpts}]; | |||
{erl_opts, Opts} -> | |||
NewOpts = {erl_opts, Opts ++ ErlOpts}, | |||
lists:keyreplace(erl_opts, 1, Config0, NewOpts) | |||
end; | |||
case lists:keyfind(erl_opts, 1, CONFIG) of | |||
{erl_opts, Opts} -> | |||
NewOpts = {erl_opts, Opts ++ ErlOpts}, | |||
lists:keyreplace(erl_opts, 1, CONFIG, NewOpts); | |||
false -> | |||
CONFIG | |||
CONFIG ++ [{erl_opts, ErlOpts}] | |||
end. |
@ -1,185 +0,0 @@ | |||
% 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. |
@ -0,0 +1,257 @@ | |||
% This file is part of Jiffy released under the MIT license. | |||
% See the LICENSE file for more information. | |||
-module(jiffy_11_property_tests). | |||
-ifdef(HAVE_EQC). | |||
-include_lib("eqc/include/eqc.hrl"). | |||
-include_lib("eunit/include/eunit.hrl"). | |||
-include("jiffy_util.hrl"). | |||
property_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) | |||
] ++ map_props(). | |||
-ifndef(JIFFY_NO_MAPS). | |||
map_props() -> | |||
[ | |||
run(prop_map_enc_dec) | |||
]. | |||
-else. | |||
map_props() -> | |||
[]. | |||
-endif. | |||
prop_enc_dec() -> | |||
?FORALL(Data, json(), begin | |||
Data == jiffy:decode(jiffy:encode(Data)) | |||
end). | |||
prop_dec_trailer() -> | |||
?FORALL({T1, Comb, T2}, {json(), combiner(), json()}, | |||
begin | |||
B1 = jiffy:encode(T1), | |||
B2 = jiffy:encode(T2), | |||
Bin = <<B1/binary, Comb/binary, B2/binary>>, | |||
{has_trailer, T1, Rest} = jiffy:decode(Bin, [return_trailer]), | |||
T2 = jiffy:decode(Rest), | |||
true | |||
end | |||
). | |||
prop_enc_dec_pretty() -> | |||
?FORALL(Data, json(), | |||
begin | |||
Data == jiffy:decode(jiffy:encode(Data, [pretty])) | |||
end | |||
). | |||
-ifndef(JIFFY_NO_MAPS). | |||
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_no_crash() -> | |||
?FORALL(Data, any(), begin catch jiffy:encode(Data), true end). | |||
prop_dec_no_crash_any() -> | |||
?FORALL(Data, any(), begin catch jiffy:decode(Data), true end). | |||
prop_dec_no_crash_bin() -> | |||
?FORALL(Data, binary(), begin catch jiffy:decode(Data), true end). | |||
opts() -> | |||
[ | |||
{numtests, [1000]} | |||
]. | |||
apply_opts(Prop) -> | |||
apply_opts(Prop, opts()). | |||
apply_opts(Prop, []) -> | |||
Prop; | |||
apply_opts(Prop, [{Name, Args} | Rest]) -> | |||
NewProp = erlang:apply(eqc, Name, Args ++ [Prop]), | |||
apply_opts(NewProp, Rest). | |||
log(F, A) -> | |||
io:format(standard_error, F, A). | |||
run(Name) -> | |||
Prop = apply_opts(?MODULE:Name()), | |||
{msg("~s", [Name]), [ | |||
{timeout, 300, ?_assert(eqc:quickcheck(Prop))} | |||
]}. | |||
-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. | |||
-endif. | |||
% Random any term generation | |||
any() -> | |||
?SIZED(Size, any(Size)). | |||
any(0) -> | |||
any_value(); | |||
any(S) -> | |||
oneof(any_value_types() ++ [ | |||
?LAZY(any_list(S)), | |||
?LAZY(any_tuple(S)) | |||
]). | |||
any_value() -> | |||
oneof(any_value_types()). | |||
any_value_types() -> | |||
[ | |||
largeint(), | |||
int(), | |||
real(), | |||
atom(), | |||
binary() | |||
]. | |||
any_list(0) -> | |||
[]; | |||
any_list(Size) -> | |||
ListSize = Size div 5, | |||
vector(ListSize, any(Size div 2)). | |||
any_tuple(0) -> | |||
{}; | |||
any_tuple(Size) -> | |||
?LET(L, any_list(Size), list_to_tuple(L)). | |||
% JSON Generation | |||
json() -> | |||
?SIZED(Size, json(Size)). | |||
json(0) -> | |||
oneof([ | |||
json_null(), | |||
json_true(), | |||
json_false(), | |||
json_number(), | |||
json_string() | |||
]); | |||
json(Size) -> | |||
frequency([ | |||
{1, json_null()}, | |||
{1, json_true()}, | |||
{1, json_false()}, | |||
{1, json_number()}, | |||
{1, json_string()}, | |||
{5, ?LAZY(json_array(Size))}, | |||
{5, ?LAZY(json_object(Size))} | |||
]). | |||
json_null() -> | |||
null. | |||
json_true() -> | |||
true. | |||
json_false() -> | |||
false. | |||
json_number() -> | |||
oneof([largeint(), int(), real()]). | |||
json_string() -> | |||
utf8(). | |||
json_array(0) -> | |||
[]; | |||
json_array(Size) -> | |||
ArrSize = Size div 5, | |||
vector(ArrSize, json(Size div 2)). | |||
json_object(0) -> | |||
{[]}; | |||
json_object(Size) -> | |||
ObjSize = Size div 5, | |||
{vector(ObjSize, {json_string(), json(Size div 2)})}. | |||
combiner() -> | |||
?SIZED( | |||
Size, | |||
?LET( | |||
L, | |||
vector((Size div 4) + 1, oneof([$\r, $\n, $\t, $\s])), | |||
list_to_binary(L) | |||
) | |||
). | |||
atom() -> | |||
?LET(L, ?SIZED(Size, vector(Size rem 254, char())), list_to_atom(L)). | |||
%% 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. | |||
-endif. |