Pt 166330348 check map keys fate #189

Merged
zxq9 merged 4 commits from PT-166330348-check-map-keys-fate into master 2019-08-26 15:37:52 +09:00
4 changed files with 62 additions and 12 deletions

View File

@ -22,5 +22,6 @@ quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000), [ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000), ?EQC_EUNIT(aefate_eqc, prop_serializes, 1000),
?EQC_EUNIT(aefate_eqc, prop_no_maps_in_keys, 1000),
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000) ?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000)
]}. ]}.

View File

@ -77,7 +77,7 @@ prop_opcodes() ->
valid_opcodes() -> valid_opcodes() ->
lists:seq(0, 16#7c) ++ lists:seq(16#fa, 16#fd). lists:seq(0, 16#7f) ++ lists:seq(16#fa, 16#fd).
fate_code(Failure) -> fate_code(Failure) ->
@ -85,7 +85,8 @@ fate_code(Failure) ->
?LET({FMap, SMap, AMap}, ?LET({FMap, SMap, AMap},
{non_empty(map(if Failure == 1 -> binary(1); {non_empty(map(if Failure == 1 -> binary(1);
true -> binary(4) end, true -> binary(4) end,
{{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})), {sublist(lists:sort([private, payable])), %% deserialize sorts them
{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})),
small_map(small_fate_data_key(5), small_fate_data(4)), small_map(small_fate_data_key(5), small_fate_data(4)),
small_map(small_fate_data_key(5), small_fate_data(4))}, small_map(small_fate_data_key(5), small_fate_data(4))},
aeb_fate_code:update_annotations( aeb_fate_code:update_annotations(

View File

@ -10,6 +10,7 @@
-module(aefate_eqc). -module(aefate_eqc).
-include_lib("eqc/include/eqc.hrl"). -include_lib("eqc/include/eqc.hrl").
-include("../include/aeb_fate_data.hrl").
-compile([export_all, nowarn_export_all]). -compile([export_all, nowarn_export_all]).
@ -23,7 +24,7 @@ prop_roundtrip() ->
end)). end)).
prop_format_scan() -> prop_format_scan() ->
?FORALL(FateData, fate_data(), ?FORALL(FateData, fate_data([variant, map]),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]), ?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin begin
String = aeb_fate_data:format(FateData), String = aeb_fate_data:format(FateData),
@ -43,6 +44,18 @@ prop_serializes() ->
{size, size(Binary) < 500000}])) {size, size(Binary) < 500000}]))
end)). end)).
prop_no_maps_in_keys() ->
?FORALL(FateData, fate_bad_map(), %% may contain a map in its keys
begin
HasMapInKeys = lists:any(fun(K) -> has_map(K) end, maps:keys(FateData)),
try aeb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Should not serialize, contains a map in key\n", []),
not HasMapInKeys)
catch error:Reason ->
?WHENFAIL(eqc:format("(~p) Should serialize\n", [Reason]), HasMapInKeys)
end
end).
prop_fuzz() -> prop_fuzz() ->
in_parallel( in_parallel(
?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)), ?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)),
@ -59,13 +72,14 @@ prop_fuzz() ->
prop_order() -> prop_order() ->
?FORALL(Items, vector(3, fate_data()), ?FORALL(Items, vector(3, fate_data([variant, map])),
begin begin
%% Use lt to take minimum %% Use lt to take minimum
Min = lt_min(Items), Min = lt_min(Items),
Max = lt_max(Items), Max = lt_max(Items),
conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])}, conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])},
{maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])}]) {maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])},
{asym, aeb_fate_data:lt(Min, Max) orelse Min == Max}])
end). end).
lt_min([X, Y | Rest]) -> lt_min([X, Y | Rest]) ->
@ -88,18 +102,24 @@ prop_idempotent() ->
aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))). aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))).
fate_data(Kind) ->
?SIZED(Size, ?LET(Data, fate_data(Size, Kind), eqc_symbolic:eval(Data))).
fate_data() -> fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))). fate_data([map, variant, store_map]).
%% keys may contain variants but no maps
fate_data_key() -> fate_data_key() ->
?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))). fate_data([variant]).
fate_data(0, _Options) -> fate_data(0, Options) ->
?LAZY( ?LAZY(
frequency( frequency(
[{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])}, [{50, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
{1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(), {10, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}])); fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}] ++
[{1, fate_store_map()} || lists:member(store_map, Options)]));
fate_data(Size, Options) -> fate_data(Size, Options) ->
?LAZY( ?LAZY(
oneof([fate_data(0, Options), oneof([fate_data(0, Options),
@ -148,7 +168,18 @@ fate_list(Size, Options) ->
fate_map(Size, Options) -> fate_map(Size, Options) ->
?LET(N, choose(0, 6), ?LET(N, choose(0, 6),
?LETSHRINK(Values, fate_values(Size, N, Options), ?LETSHRINK(Values, fate_values(Size, N, Options),
?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map])), ?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map, store_map])),
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
fate_store_map() ->
%% only #{} is allowed as cache in serialization
?LET(X, oneof([int(), largeint()]),
return(aeb_fate_data:make_store_map(abs(X)))).
fate_bad_map() ->
?LET(N, choose(0, 6),
?LET(Values, vector(N, ?SIZED(Size, resize(Size div 8, fate_data()))),
?LET(Keys, vector(N, ?SIZED(Size, resize(Size div 4, fate_data()))),
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))). return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
non_quote_string() -> non_quote_string() ->
@ -167,3 +198,14 @@ injection(Binary) ->
is_empty(L) -> is_empty(L) ->
?WHENFAIL(eqc:format("~p\n", [L]), L == []). ?WHENFAIL(eqc:format("~p\n", [L]), L == []).
has_map(L) when is_list(L) ->
lists:any(fun(V) -> has_map(V) end, L);
has_map(T) when is_tuple(T) ->
has_map(tuple_to_list(T));
has_map(M) when is_map(M) ->
true;
has_map(?FATE_STORE_MAP(_, _)) ->
true;
has_map(_) ->
false.

View File

@ -487,5 +487,11 @@ sort(KVList) ->
valid_key_type(K) when ?IS_FATE_MAP(K) -> valid_key_type(K) when ?IS_FATE_MAP(K) ->
error({map_as_key_in_map, K}); error({map_as_key_in_map, K});
valid_key_type(?FATE_STORE_MAP(_, _) = K) ->
error({map_as_key_in_map, K});
valid_key_type(K) when is_list(K) ->
lists:all(fun(E) -> valid_key_type(E) end, K);
valid_key_type(K) when is_tuple(K) ->
lists:all(fun(E) -> valid_key_type(E) end, tuple_to_list(K));
valid_key_type(_K) -> valid_key_type(_K) ->
true. true.