diff --git a/quickcheck/aefate_code_eqc.erl b/quickcheck/aefate_code_eqc.erl index 29f7821..2c72a21 100644 --- a/quickcheck/aefate_code_eqc.erl +++ b/quickcheck/aefate_code_eqc.erl @@ -27,22 +27,25 @@ prop_serializes() -> in_parallel( ?FORALL(FateCode, fate_code(0), - ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateCode]), - begin - Binary = aeb_fate_code:serialize(FateCode), - ?WHENFAIL(eqc:format("serialized: ~p~n", [Binary]), - begin - Decoded = aeb_fate_code:deserialize(Binary), - measure(binary_size, size(Binary), - equals(Decoded, FateCode)) - end) - end))). + begin + {T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end), + ?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]), + begin + {T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end), + measure(binary_size, size(Binary), + measure(serialize, T0 / 1000, + measure(deserialize, T1 / 1000, + conjunction([{equal, equals(Decoded, FateCode)}, + {serialize_time, T0 / 1000 < 500}, + {deserialize_time, T1 / 1000 < 500}])))) + end) + end)). prop_fail_serializes() -> conjunction([{Failure, eqc:counterexample( ?FORALL(FateCode, fate_code(Failure), ?FORALL(Binary, catch aeb_fate_code:serialize(FateCode), - is_binary(aeb_fate_code:serialize(FateCode))))) + is_binary(Binary)))) =/= true} || Failure <- [1,2,3,4, 5] ]). prop_fuzz() -> @@ -51,7 +54,7 @@ prop_fuzz() -> ?FORALL(InjectedBin, injection(Binary), try Org = aeb_fate_code:deserialize(InjectedBin), NewBin = aeb_fate_code:serialize(Org), - NewOrg = aeb_fate_code:deserialize(NewBin), + NewOrg = (catch aeb_fate_code:deserialize(NewBin)), ?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]), equals(NewBin, InjectedBin)) catch _:_ -> @@ -80,16 +83,23 @@ fate_code(Failure) -> {non_empty(map(if Failure == 1 -> binary(1); true -> binary(4) end, {{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})), - map(small_fate_data_key(5), small_fate_data(4)), - 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_symbols( aeb_fate_code:update_functions( aeb_fate_code:new(), FMap), SMap), AMap))). +short_list(Max, Gen) -> + ?LET(N, choose(0, Max), eqc_gen:list(N, Gen)). + +small_map(KeyGen, ValGen) -> + ?LET(KeyVals, short_list(6, {KeyGen, ValGen}), + return(maps:from_list(KeyVals))). + bbs_code(Failure) -> frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}}, - {10, ?LET(BBs, list(bb_code(Failure)), + {10, ?LET(BBs, short_list(6, bb_code(Failure)), maps:from_list( lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]). @@ -97,9 +107,9 @@ bb_code(Failure) -> EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ], NonEndBB = valid_opcodes() -- EndBB, frequency( - [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(list(elements(NonEndBB))), bblock(Failure, Ops))}, - {if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {list(elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}, - {10, ?LET({Ops, Op}, {list(elements(NonEndBB)), elements(EndBB)}, + [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))}, + {if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {short_list(6, elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}, + {10, ?LET({Ops, Op}, {short_list(6, elements(NonEndBB)), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}]). bblock(Failure, Ops) -> @@ -126,6 +136,26 @@ injection(Binary) -> <> end). +prop_small() -> + ?FORALL(Value, small_fate_data(4), + begin + Bin = aeb_fate_encoding:serialize(Value), + Size = byte_size(Bin), + measure(size, Size, + ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), + Size < 1000)) + end). + +prop_small_type() -> + ?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)), + begin + Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)), + Size = byte_size(Bin), + measure(size, Size, + ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), + Size < 1000)) + end). + small_fate_data(N) -> ?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())). diff --git a/quickcheck/aefate_eqc.erl b/quickcheck/aefate_eqc.erl index 3cf183d..4a06401 100644 --- a/quickcheck/aefate_eqc.erl +++ b/quickcheck/aefate_eqc.erl @@ -32,21 +32,15 @@ prop_format_scan() -> end)). prop_serializes() -> - ?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))), - ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]), + ?FORALL({Data, Garbage}, {fate_data(), binary()}, + ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]), begin - {T1, Binary} = - timer:tc( fun() -> - << begin B = aeb_fate_encoding:serialize(Data), - <> end || Data <- FateDatas >> - end), - {T2, {FateData, _}} = - timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end), + Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>, + {FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary), measure(binary_size, size(Binary), - measure(encode, T1, - measure(decode, T2, - conjunction([{equal, equals(hd(FateDatas), FateData)}, - {size, size(Binary) < 500000}])))) + conjunction([{equal, equals(Data, FateData)}, + {rest, equals(Garbage, Rest)}, + {size, size(Binary) < 500000}])) end)). prop_fuzz() -> @@ -98,74 +92,65 @@ fate_data() -> ?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))). fate_data_key() -> - ?SIZED(Size, ?LET(Data, fate_data(Size div 4, []), eqc_symbolic:eval(Data))). + ?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))). fate_data(0, _Options) -> ?LAZY( - oneof([fate_integer(), - fate_boolean(), - fate_nil(), - fate_unit(), - fate_string(), - fate_address(), - fate_hash(), - fate_signature(), - fate_contract(), - fate_oracle(), - fate_oracle_q(), - fate_name(), - fate_bits(), - fate_channel()])); + frequency( + [{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])}, + {1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(), + fate_oracle(), fate_oracle_q(), fate_name(), fate_bits(), fate_channel()])}])); fate_data(Size, Options) -> - oneof([?LAZY(fate_data(Size - 1, Options)), - ?LAZY(fate_list( fate_data(Size div 5, Options) )), - ?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) ))] ++ - [?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) - || lists:member(variant, Options) - ] ++ - [ - ?LAZY(fate_map( fate_data(Size div 8, Options -- [map, variant]), - fate_data(Size div 5, Options))) - || lists:member(map, Options) - ]). + ?LAZY( + oneof([fate_data(0, Options), + fate_list(Size, Options), + fate_tuple(Size, Options)] ++ + [fate_variant(Size, Options) + || lists:member(variant, Options)] ++ + [fate_map(Size, Options) + || lists:member(map, Options)])). -fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}. -fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}. -fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}. -fate_nil() -> {call, aeb_fate_data, make_list, [[]]}. -fate_unit() -> {call, aeb_fate_data, make_unit, []}. -fate_string() -> {call, aeb_fate_data, make_string, - [frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, - {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}. -fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}. -fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}. -fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}. -fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}. -fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}. -fate_oracle_q() -> {call, aeb_fate_data, make_oracle_query, [non_zero_binary(256 div 8)]}. -fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}. -fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}. +fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))). +fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))). +fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))). +fate_nil() -> aeb_fate_data:make_list([]). +fate_unit() -> aeb_fate_data:make_unit(). +fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, + {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]), + return(aeb_fate_data:make_string(X))). +fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))). +fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))). +fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))). +fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))). +fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))). +fate_name() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_name(X))). +fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))). + +fate_values(Size, N, Options) -> + eqc_gen:list(N, fate_data(Size div max(1, N), Options)). %% May shrink to fate_unit -fate_tuple(ListGen) -> - {call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}. +fate_tuple(Size, Options) -> + ?LET(N, choose(0, 6), + ?LETSHRINK(Elements, fate_values(Size, N, Options), + return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))). -fate_variant(ListGen) -> - ?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen}, - {call, aeb_fate_data, make_variant, - [L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}). +fate_variant(Size, Options) -> + ?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)}, + return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2, + length(L1), Args))). -fate_list(Gen) -> - {call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}. +fate_list(Size, Options) -> + ?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]), + ?LETSHRINK(Vs, fate_values(Size, N, Options), + return(aeb_fate_data:make_list(Vs)))). -fate_map(KeyGen, ValGen) -> - {call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}. - - -non_zero_binary(N) -> - Bits = N*8, - ?SUCHTHAT(Bin, binary(N), begin <> = Bin, V =/= 0 end). +fate_map(Size, Options) -> + ?LET(N, choose(0, 6), + ?LETSHRINK(Values, fate_values(Size, N, Options), + ?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map])), + return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))). non_quote_string() -> ?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []). diff --git a/quickcheck/aefate_type_eqc.erl b/quickcheck/aefate_type_eqc.erl index 7d5596b..d81f6d2 100644 --- a/quickcheck/aefate_type_eqc.erl +++ b/quickcheck/aefate_type_eqc.erl @@ -11,9 +11,12 @@ -compile([export_all, nowarn_export_all]). +kind(X) when is_atom(X) -> X; +kind(T) when is_tuple(T) -> element(1, T). + prop_roundtrip() -> ?FORALL(FateType, fate_type(), - collect(FateType, + collect(kind(FateType), begin Serialized = aeb_fate_encoding:serialize_type(FateType), BinSerialized = list_to_binary(Serialized), @@ -32,8 +35,7 @@ fate_type(0) -> oneof([integer, boolean, address, - hash, - signature, + {bytes, nat()}, contract, oracle, name, @@ -41,9 +43,15 @@ fate_type(0) -> bits, string]); fate_type(Size) -> - oneof([?LAZY(fate_type(Size div 2)), - {list, ?LAZY(fate_type(Size div 2))}, - {tuple, list(?LAZY(fate_type(Size div 2)))}, - {variant, list(?LAZY(fate_type(Size div 2)))}, - ?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))], - {map, T1, T2})]). + ?LAZY( + oneof([fate_type(0), + {list, fate_type(Size div 2)}, + ?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}), + ?LETSHRINK(Ts, fate_types(Size), {variant, Ts}), + ?LETSHRINK([T1, T2], vector(2, fate_type(Size div 2)), + {map, T1, T2})])). + +fate_types(Size) -> + ?LET(N, choose(0, 6), + eqc_gen:list(N, fate_type(Size div max(2, N)))). +