From 75f3eeffa7b06f406eb992266833b623e1664850 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Tue, 9 Apr 2019 10:32:08 +0200 Subject: [PATCH] Add quickcheck properties --- quickcheck/aefate_eqc.erl | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 quickcheck/aefate_eqc.erl diff --git a/quickcheck/aefate_eqc.erl b/quickcheck/aefate_eqc.erl new file mode 100644 index 0000000..3049c41 --- /dev/null +++ b/quickcheck/aefate_eqc.erl @@ -0,0 +1,107 @@ +%%% @author Thomas Arts +%%% @copyright (C) 2018, Thomas Arts +%%% @doc Use `rebar3 as eqc shell` to run properties in the shell +%%% +%%% +%%% @end +%%% Created : 13 Dec 2018 by Thomas Arts + +-module(aefate_eqc). + +-include_lib("eqc/include/eqc.hrl"). + +-compile([export_all, nowarn_export_all]). + +prop_roundtrip() -> + ?FORALL(FateData, fate_type(), + measure(bytes, size(term_to_binary(FateData)), + begin + Serialized = aeb_fate_encoding:serialize(FateData), + ?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]), + equals(aeb_fate_encoding:deserialize(Serialized), FateData)) + end + )). + +prop_format() -> + ?FORALL(FateData, fate_type(), + ?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() -> + ?FORALL(FateData, fate_type(), + ?WHENFAIL(eqc:format("Trying to format ~p failed~n~p~n", [FateData, catch unicode:characters_to_list(aeb_fate_data:format(FateData), utf8) ]), + begin + String = aeb_fate_data:format(FateData), + {ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)), + true + end)). + +fate_type() -> + ?SIZED(Size, fate_type(Size, [map])). + +fate_type(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_name(), + fate_bits(), + fate_channel()])); +fate_type(Size, Options) -> + ?LETSHRINK([Smaller], [?LAZY(fate_type(Size div 5, Options))], + oneof([?LAZY(fate_type(Size - 1, Options)), + ?LAZY(fate_list( Smaller )), + ?LAZY(fate_tuple( list( Smaller ))), + ?LAZY(fate_variant(?LET(L, list( Smaller), list_to_tuple(L))))] ++ + [ + ?LAZY(fate_map( fate_type(Size div 3, Options -- [map]), + Smaller)) + || lists:member(map, Options) + ])). + + +fate_integer() -> oneof([int(), largeint()]). +fate_bits() -> {bits, oneof([int(), largeint()])}. +fate_boolean() -> elements([true, false]). +fate_nil() -> []. +fate_unit() -> {tuple, {}}. +fate_string() -> ?SUCHTHAT(S, utf8(), string:find(S, "\"") == nomatch). +fate_address() -> {address, non_zero_binary(256 div 8)}. +fate_hash() -> {hash, non_zero_binary(32)}. +fate_signature() -> {signature, non_zero_binary(64)}. +fate_contract() -> {contract, non_zero_binary(256 div 8)}. +fate_oracle() -> {oracle, non_zero_binary(256 div 8)}. +fate_name() -> {name, non_zero_binary(256 div 8)}. +fate_channel() -> {channel, non_zero_binary(256 div 8)}. + +%% May shrink to fate_unit +fate_tuple(ListGen) -> + {tuple, ?LET(Elements, ListGen, list_to_tuple(Elements))}. + +fate_variant(TupleGen) -> + ?LET({L1, L2, Tuple}, {list(choose(0, 255)), list(choose(0,255)), TupleGen}, + {variant, L1 ++ [size(Tuple)] ++ L2, length(L1), Tuple}). + +fate_list(Gen) -> + oneof([fate_nil(), ?SHRINK(list(Gen), [fate_nil()])]). + +fate_map(KeyGen, ValGen) -> + map(KeyGen, ValGen). + + +non_zero_binary(N) -> + Bits = N*8, + ?SUCHTHAT(Bin, binary(N), begin <> = Bin, V =/= 0 end). + +char() -> + choose(1, 255).