Merge pull request #35 from aeternity/PT-property-based-tests
Pt property based tests
This commit is contained in:
commit
91c4ab5bea
25
quickcheck/aeb_fate_data_tests.erl
Normal file
25
quickcheck/aeb_fate_data_tests.erl
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
%%% @author Thomas Arts
|
||||||
|
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||||
|
%%% `rebar3 as eqc eunit --cover`
|
||||||
|
%%% or `rebar3 as eqc eunit --module=aeb_fate_data`
|
||||||
|
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||||
|
|
||||||
|
-module(aeb_fate_data_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-compile([export_all, nowarn_export_all]).
|
||||||
|
|
||||||
|
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||||
|
{ atom_to_list(PropName),
|
||||||
|
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||||
|
|
||||||
|
quickcheck_test_() ->
|
||||||
|
{setup, fun() -> eqc:start() end,
|
||||||
|
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
|
||||||
|
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000)
|
||||||
|
]}.
|
25
quickcheck/aeb_fate_encoding_tests.erl
Normal file
25
quickcheck/aeb_fate_encoding_tests.erl
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
%%% @author Thomas Arts
|
||||||
|
%%% @doc Allow to run QuickCheck tests as eunit tests
|
||||||
|
%%% `rebar3 as eqc eunit --cover`
|
||||||
|
%%% or `rebar3 as eqc eunit --module=aeb_fate_encoding`
|
||||||
|
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Dec 2018 by Thomas Arts
|
||||||
|
|
||||||
|
-module(aeb_fate_encoding_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-compile([export_all, nowarn_export_all]).
|
||||||
|
|
||||||
|
-define(EQC_EUNIT(Module, PropName, Ms),
|
||||||
|
{ atom_to_list(PropName),
|
||||||
|
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
|
||||||
|
|
||||||
|
quickcheck_test_() ->
|
||||||
|
{setup, fun() -> eqc:start() end,
|
||||||
|
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
|
||||||
|
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000)
|
||||||
|
]}.
|
@ -1,7 +1,8 @@
|
|||||||
%%% @author Thomas Arts
|
%%% @author Thomas Arts
|
||||||
%%% @copyright (C) 2018, Thomas Arts
|
|
||||||
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||||
%%%
|
%%%
|
||||||
|
%%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc.
|
||||||
|
%%% In other words make some rather broad terms as well as some deep terms
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||||
@ -21,14 +22,6 @@ prop_roundtrip() ->
|
|||||||
equals(aeb_fate_encoding:deserialize(Serialized), FateData))
|
equals(aeb_fate_encoding:deserialize(Serialized), FateData))
|
||||||
end)).
|
end)).
|
||||||
|
|
||||||
prop_format() ->
|
|
||||||
?FORALL(FateData, fate_data(),
|
|
||||||
?WHENFAIL(eqc:format("Trying to format ~p failed~n",[FateData]),
|
|
||||||
begin
|
|
||||||
String = aeb_fate_data:format(FateData),
|
|
||||||
collect([FateData, unicode:characters_to_binary(String, latin1)], true)
|
|
||||||
end)).
|
|
||||||
|
|
||||||
prop_format_scan() ->
|
prop_format_scan() ->
|
||||||
?FORALL(FateData, fate_data(),
|
?FORALL(FateData, fate_data(),
|
||||||
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
|
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
|
||||||
@ -38,10 +31,28 @@ prop_format_scan() ->
|
|||||||
true
|
true
|
||||||
end)).
|
end)).
|
||||||
|
|
||||||
fate_data() ->
|
prop_serializes() ->
|
||||||
?SIZED(Size, ?LET(Data, fate_type(Size, [map]), eqc_symbolic:eval(Data))).
|
?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))),
|
||||||
|
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]),
|
||||||
|
begin
|
||||||
|
{T1, Binary} =
|
||||||
|
timer:tc( fun() ->
|
||||||
|
<< begin B = aeb_fate_encoding:serialize(Data),
|
||||||
|
<<B/binary>> end || Data <- FateDatas >>
|
||||||
|
end),
|
||||||
|
{T2, {FateData, _}} =
|
||||||
|
timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end),
|
||||||
|
measure(binary_size, size(Binary),
|
||||||
|
measure(encode, T1,
|
||||||
|
measure(decode, T2,
|
||||||
|
conjunction([{equal, equals(hd(FateDatas), FateData)},
|
||||||
|
{size, size(Binary) < 500000}]))))
|
||||||
|
end)).
|
||||||
|
|
||||||
fate_type(0, _Options) ->
|
fate_data() ->
|
||||||
|
?SIZED(Size, ?LET(Data, fate_data(Size, [map]), eqc_symbolic:eval(Data))).
|
||||||
|
|
||||||
|
fate_data(0, _Options) ->
|
||||||
?LAZY(
|
?LAZY(
|
||||||
oneof([fate_integer(),
|
oneof([fate_integer(),
|
||||||
fate_boolean(),
|
fate_boolean(),
|
||||||
@ -56,17 +67,16 @@ fate_type(0, _Options) ->
|
|||||||
fate_name(),
|
fate_name(),
|
||||||
fate_bits(),
|
fate_bits(),
|
||||||
fate_channel()]));
|
fate_channel()]));
|
||||||
fate_type(Size, Options) ->
|
fate_data(Size, Options) ->
|
||||||
?LETSHRINK([Smaller], [?LAZY(fate_type(Size div 5, Options))],
|
oneof([?LAZY(fate_data(Size - 1, Options)),
|
||||||
oneof([?LAZY(fate_type(Size - 1, Options)),
|
?LAZY(fate_list( fate_data(Size div 5, Options) )),
|
||||||
?LAZY(fate_list( Smaller )),
|
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) )),
|
||||||
?LAZY(fate_tuple( list( Smaller ))),
|
?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) ] ++
|
||||||
?LAZY(fate_variant(?LET(L, list( Smaller), list_to_tuple(L))))] ++
|
|
||||||
[
|
[
|
||||||
?LAZY(fate_map( fate_type(Size div 3, Options -- [map]),
|
?LAZY(fate_map( fate_data(Size div 8, Options -- [map]),
|
||||||
Smaller))
|
fate_data(Size div 5, Options)))
|
||||||
|| lists:member(map, Options)
|
|| lists:member(map, Options)
|
||||||
])).
|
]).
|
||||||
|
|
||||||
|
|
||||||
fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}.
|
fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}.
|
||||||
@ -74,7 +84,9 @@ fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()]
|
|||||||
fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}.
|
fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}.
|
||||||
fate_nil() -> {call, aeb_fate_data, make_list, [[]]}.
|
fate_nil() -> {call, aeb_fate_data, make_list, [[]]}.
|
||||||
fate_unit() -> {call, aeb_fate_data, make_unit, []}.
|
fate_unit() -> {call, aeb_fate_data, make_unit, []}.
|
||||||
fate_string() -> {call, aeb_fate_data, make_string, [?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == [])]}.
|
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_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_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}.
|
||||||
fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}.
|
fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}.
|
||||||
@ -87,12 +99,13 @@ fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 di
|
|||||||
fate_tuple(ListGen) ->
|
fate_tuple(ListGen) ->
|
||||||
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}.
|
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}.
|
||||||
|
|
||||||
fate_variant(TupleGen) ->
|
fate_variant(ListGen) ->
|
||||||
?LET({L1, L2, Tuple}, {list(choose(0, 255)), list(choose(0,255)), TupleGen},
|
?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen},
|
||||||
{call, aeb_fate_data, make_variant, [L1 ++ [size(Tuple)] ++ L2, length(L1), Tuple]}).
|
{call, aeb_fate_data, make_variant,
|
||||||
|
[L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}).
|
||||||
|
|
||||||
fate_list(Gen) ->
|
fate_list(Gen) ->
|
||||||
{call, aeb_fate_data, make_list, [oneof([fate_nil(), ?SHRINK(list(Gen), [fate_nil()])])]}.
|
{call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}.
|
||||||
|
|
||||||
fate_map(KeyGen, ValGen) ->
|
fate_map(KeyGen, ValGen) ->
|
||||||
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}.
|
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}.
|
||||||
@ -102,5 +115,8 @@ non_zero_binary(N) ->
|
|||||||
Bits = N*8,
|
Bits = N*8,
|
||||||
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
|
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
|
||||||
|
|
||||||
|
non_quote_string() ->
|
||||||
|
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
|
||||||
|
|
||||||
char() ->
|
char() ->
|
||||||
choose(1, 255).
|
choose(1, 255).
|
||||||
|
49
quickcheck/aefate_type_eqc.erl
Normal file
49
quickcheck/aefate_type_eqc.erl
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
%%% @author Thomas Arts
|
||||||
|
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
|
||||||
|
%%% Properties for testing Fate type representations
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
|
||||||
|
|
||||||
|
-module(aefate_type_eqc).
|
||||||
|
|
||||||
|
-include_lib("eqc/include/eqc.hrl").
|
||||||
|
|
||||||
|
-compile([export_all, nowarn_export_all]).
|
||||||
|
|
||||||
|
prop_roundtrip() ->
|
||||||
|
?FORALL(FateType, fate_type(),
|
||||||
|
collect(FateType,
|
||||||
|
begin
|
||||||
|
Serialized = aeb_fate_encoding:serialize_type(FateType),
|
||||||
|
BinSerialized = list_to_binary(Serialized),
|
||||||
|
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
|
||||||
|
begin
|
||||||
|
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized),
|
||||||
|
equals(Type, FateType)
|
||||||
|
end)
|
||||||
|
end)).
|
||||||
|
|
||||||
|
|
||||||
|
fate_type() ->
|
||||||
|
?SIZED(Size, fate_type(Size)).
|
||||||
|
|
||||||
|
fate_type(0) ->
|
||||||
|
oneof([integer,
|
||||||
|
boolean,
|
||||||
|
address,
|
||||||
|
hash,
|
||||||
|
signature,
|
||||||
|
contract,
|
||||||
|
oracle,
|
||||||
|
name,
|
||||||
|
channel,
|
||||||
|
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})]).
|
@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
-type fate_type_type() :: integer
|
-type fate_type_type() :: integer
|
||||||
| boolean
|
| boolean
|
||||||
| {list, fate_type()}
|
| {list, fate_type_type()}
|
||||||
| {map, fate_type(), fate_type()}
|
| {map, fate_type_type(), fate_type_type()}
|
||||||
| {tuple, [fate_type()]}
|
| {tuple, [fate_type_type()]}
|
||||||
| address
|
| address
|
||||||
| hash
|
| hash
|
||||||
| signature
|
| signature
|
||||||
@ -35,7 +35,8 @@
|
|||||||
| name
|
| name
|
||||||
| channel
|
| channel
|
||||||
| bits
|
| bits
|
||||||
| {variant, list()}.
|
| string
|
||||||
|
| {variant, [fate_type_type()]}.
|
||||||
|
|
||||||
|
|
||||||
-type fate_type() ::
|
-type fate_type() ::
|
||||||
|
Loading…
x
Reference in New Issue
Block a user