All checks were successful
Gajumaru Bytecode Tests / tests (push) Successful in -3m34s
Add Gitea tests Rename Remove oracle references Package for zx Reviewed-on: #235 Reviewed-by: dimitar.p.ivanov <dimitarivanov@qpq.swiss> Co-authored-by: Craig Everett <zxq9@zxq9.com> Co-committed-by: Craig Everett <zxq9@zxq9.com>
168 lines
6.5 KiB
Erlang
168 lines
6.5 KiB
Erlang
%%% @author Thomas Arts
|
|
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
|
%%%
|
|
%%% We want to be sure that we can deserialize all FATE assembler that is accepted on chain.
|
|
%%%
|
|
%%% We test something slightly weaker here,
|
|
%%% viz. All FATE assembler we serialize, we can deserialize
|
|
%%%
|
|
%%% Negative testing modelled:
|
|
%%% Failure 1: function names differ from 4 bytes
|
|
%%% Failure 2: pointer to empty code block
|
|
%%% Failure 3: end_BB operation as not ending block or not at end of block
|
|
%%% - empty code blocks
|
|
%%% - blocks that are not of the form (not end_bb)* end_bb.
|
|
%%%
|
|
%%% @end
|
|
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
|
|
|
-module(gmfate_code_eqc).
|
|
|
|
-include_lib("eqc/include/eqc.hrl").
|
|
|
|
-compile([export_all, nowarn_export_all]).
|
|
%%-define(Failure(Failures, Number), case lists:member(Number, Failures) of true -> 1; false -> 0 end)
|
|
|
|
|
|
prop_serializes() ->
|
|
in_parallel(
|
|
?FORALL(FateCode, fate_code(0),
|
|
begin
|
|
{T0, Binary} = timer:tc(fun() -> gmb_fate_code:serialize(FateCode) end),
|
|
?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]),
|
|
begin
|
|
{T1, Decoded} = timer:tc(fun() -> gmb_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 gmb_fate_code:serialize(FateCode),
|
|
is_binary(Binary))))
|
|
=/= true} || Failure <- [1, 2, 3, 4, 5] ]).
|
|
|
|
prop_fuzz() ->
|
|
in_parallel(
|
|
?FORALL(Binary, ?LET(FateCode, fate_code(0), gmb_fate_code:serialize(FateCode)),
|
|
?FORALL(FuzzedBin, fuzz(Binary),
|
|
try gmb_fate_code:deserialize(FuzzedBin) of
|
|
Code ->
|
|
?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]),
|
|
begin
|
|
Bin1 = gmb_fate_code:serialize(Code),
|
|
Code1 = gmb_fate_code:deserialize(Bin1),
|
|
?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]),
|
|
equals(Code, Code1))
|
|
end)
|
|
catch _:_ -> true
|
|
end))).
|
|
|
|
prop_opcodes() ->
|
|
?FORALL(Opcode, choose(0, 16#ff),
|
|
try M = gmb_fate_opcodes:mnemonic(Opcode),
|
|
?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]),
|
|
conjunction([{valid, lists:member(Opcode, valid_opcodes())},
|
|
{eq, equals(gmb_fate_opcodes:m_to_op(M), Opcode)}]))
|
|
catch
|
|
_:_ ->
|
|
not lists:member(Opcode, valid_opcodes())
|
|
end).
|
|
|
|
|
|
valid_opcodes() ->
|
|
[ Op || #{opcode := Op} <- gmb_fate_generate_ops:get_ops() ].
|
|
|
|
|
|
fate_code(Failure) ->
|
|
?SIZED(Size,
|
|
?LET({FMap, SMap, AMap},
|
|
{non_empty(map(if Failure == 1 -> binary(1);
|
|
true -> binary(4) end,
|
|
{sublist(lists:sort([private, payable])), %% deserialize sorts them
|
|
{list(gmfate_type_eqc:fate_type(Size div 3)), gmfate_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))},
|
|
gmb_fate_code:update_annotations(
|
|
gmb_fate_code:update_symbols(
|
|
gmb_fate_code:update_functions(
|
|
gmb_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, short_list(6, bb_code(Failure)),
|
|
maps:from_list(
|
|
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
|
|
|
|
bb_code(Failure) ->
|
|
EndBB = [ Op || Op <- valid_opcodes(), gmb_fate_opcodes:end_bb(Op) ],
|
|
NonEndBB = valid_opcodes() -- EndBB,
|
|
frequency(
|
|
[{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) ->
|
|
[ begin
|
|
Mnemonic = gmb_fate_opcodes:mnemonic(Op),
|
|
Arity = gmb_fate_opcodes:args(Op),
|
|
case Arity of
|
|
0 -> Mnemonic;
|
|
_ -> list_to_tuple([Mnemonic |
|
|
[ frequency([{if Failure == 5 -> 5; true -> 0 end, {stack, nat()}},
|
|
{5, {stack, 0}},
|
|
{5, {arg, nat()}},
|
|
{5, {var, nat()}},
|
|
{5, {immediate, small_fate_data(4)}}]) ||
|
|
_ <- lists:seq(1, Arity) ]])
|
|
end
|
|
end || Op <- Ops ].
|
|
|
|
fuzz(Binary) ->
|
|
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0, 255)},
|
|
begin
|
|
M = N * 8,
|
|
<<X:M, _:8, Z/binary>> = Binary,
|
|
<<X:M, Inj:8, Z/binary>>
|
|
end).
|
|
|
|
prop_small() ->
|
|
?FORALL(Value, small_fate_data(4),
|
|
begin
|
|
Bin = gmb_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, gmfate_type_eqc:fate_type(Size div 3)),
|
|
begin
|
|
Bin = iolist_to_binary(gmb_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, gmfate_eqc:fate_data())).
|
|
|
|
small_fate_data_key(N) ->
|
|
?SIZED(Size, ?LET(Data, gmfate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))).
|