From 9dddb1b140bef1001e8d300b4493dd2b65517325 Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 2 Jun 2020 13:45:35 +0200 Subject: [PATCH 01/11] #38 First tests, these are not yet fully functional. --- Makefile | 8 +++- eqc/core_vnode_eqc.erl | 8 ++-- include/stats.hrl | 3 ++ rebar.config | 15 ++++-- test/eqc/bprops_eqc.erl | 50 ++++++++++---------- test/eqc/node_watcher_qc.erl | 15 +++--- test/eqc/riak_core_claim_statem.erl | 72 ++++++++++++++--------------- test/eqc/vclock_qc.erl | 38 ++++++++------- 8 files changed, 114 insertions(+), 95 deletions(-) diff --git a/Makefile b/Makefile index 8a0994fd6..7e2946a9e 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,13 @@ clean-test: pulse: ${REBAR} compile -D PULSE ${REBAR} eunit -D PULSE skip_deps=true suite=$(PULSE_TESTS) + +proper: + ${REBAR} as proper do eunit +epc: + ${REBAR} as epc eunit + test: compile ${REBAR} eunit @@ -40,7 +46,7 @@ coverage: compile ${REBAR} cover --verbose docs: - ${REBAR} edoc + ${REBAR} doc xref: compile ${REBAR} xref diff --git a/eqc/core_vnode_eqc.erl b/eqc/core_vnode_eqc.erl index 66e68f93f..59bdc4218 100644 --- a/eqc/core_vnode_eqc.erl +++ b/eqc/core_vnode_eqc.erl @@ -23,9 +23,10 @@ %% @doc QuickCheck tests for riak_core_vnode code -module(core_vnode_eqc). --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --include_lib("eqc/include/eqc_fsm.hrl"). +-ifdef(TEST). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +%-include_lib("eqc/include/eqc_fsm.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("riak_core/include/riak_core_vnode.hrl"). -compile([export_all]). @@ -450,3 +451,4 @@ filter_work(Work, Pid) -> end, Work). -endif. +-endif. \ No newline at end of file diff --git a/include/stats.hrl b/include/stats.hrl index 2ff8fd929..03060fa35 100644 --- a/include/stats.hrl +++ b/include/stats.hrl @@ -2,6 +2,9 @@ -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). -ifdef(TEST). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-endif. -ifdef(EQC). -include_lib("eqc/include/eqc.hrl"). -endif. diff --git a/rebar.config b/rebar.config index fafd68568..c80e61c91 100644 --- a/rebar.config +++ b/rebar.config @@ -1,11 +1,10 @@ %% Add debug information in compiled code for other tools like debuggers, code coverage tools, or analysis tools {erl_opts, [debug_info]}. - +{plugins, [rebar3_proper]}. {deps, [ % worker pool library {poolboy, "~>1.5.2"} ]}. - %% Defensive xref configuration {xref_checks, [ undefined_function_calls, locals_not_used, deprecated_function_calls ]}. @@ -20,8 +19,16 @@ {deps, [meck]} ]}, {docs, [{deps, [{edown, "0.7.0"}]}]}, - {eqc, [{erl_opts, [nowarn_export_all, {d, 'EQC'}, {d, 'TEST'}]}, {deps, [meck]}, {plugins, [{rebar_eqc, "~>0.1.0"}]}]}, - {prod, [{relx, [{dev_mode, false}]}]}, + {eqc, [ + {erl_opts, [nowarn_export_all, {d, 'EQC'}, {d, 'TEST'}]}, + {deps, [meck]}, + {plugins, [{rebar_eqc, "~>0.1.0"}]} + ]}, + {proper, [ + {erl_opts, [nowarn_export_all,{d, 'PROPER'}, {d, 'TEST'}]}, + {plugins, [{coveralls, {git, "https://github.com/markusn/coveralls-erl", {branch, "master"}}}]}, + {deps, [meck, {proper, "1.2.0"}, recon]} + ]}, {lint, [ {plugins, [ {rebar3_lint, {git, "https://github.com/project-fifo/rebar3_lint.git", {tag, "0.1.11"}}} diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index 63fa45b21..d1732923a 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -31,9 +31,8 @@ %% More attention needs to be spent on these tests! %% --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --include_lib("eqc/include/eqc_statem.hrl"). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -compile(export_all). @@ -45,7 +44,7 @@ -define(BPROP_KEYS, [foo, bar, tapas]). -define(DEFAULT_BPROPS, [{n_val, 3}]). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). %% @@ -63,7 +62,8 @@ bprops_test_() -> { timeout, 60, ?_test(?assert( - eqc:quickcheck(?QC_OUT(eqc:testing_time(50, prop_buckets()))))) + %proper:quickcheck(?QC_OUT(proper:testing_time(50, prop_buckets()))))) + proper:quickcheck(?QC_OUT(prop_buckets())))) }. %% @@ -74,17 +74,17 @@ run() -> run(100). run(N) -> - eqc:quickcheck(eqc:numtests(N, prop_buckets())). + proper:quickcheck(proper:numtests(N, prop_buckets())). rerun() -> - eqc:check(eqc_statem:show_states(prop_buckets())). + proper:check(proper:show_states(prop_buckets())). cover() -> cover(100). cover(N) -> cover:compile_beam(riak_core_bucket), - eqc:quickcheck(eqc:numtests(N, prop_buckets())), + proper:quickcheck(proper:numtests(N, prop_buckets())), cover:analyse_to_file(riak_core_bucket, [html]). @@ -92,7 +92,7 @@ cover(N) -> %% eqc_statem initial model %% --spec initial_state() -> eqc_statem:symbolic_state(). +-spec initial_state() -> proper:symbolic_state(). initial_state() -> #state{}. @@ -161,14 +161,14 @@ get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) -> BPropsFind = orddict:find(Bucket, Buckets), case {Res, BPropsFind} of {error, _} -> - eq(Res, error); + equals(Res, error); {_, {ok, BProps}} -> - eq( + equals( orddict:from_list(Res), orddict:from_list(BProps) ); {_, error} -> - eq( + equals( orddict:from_list(Res), orddict:from_list(?DEFAULT_BPROPS ++ [{name, Bucket}]) ) @@ -193,7 +193,7 @@ all_n_post(#state{buckets=Buckets}, [], Res) -> [], Buckets ) ++ [proplists:get_value(n_val, ?DEFAULT_BPROPS)], - eq(ordsets:from_list(Res), ordsets:from_list(AllNVals)). + equals(ordsets:from_list(Res), ordsets:from_list(AllNVals)). %% TODO Add more commands here @@ -203,27 +203,27 @@ all_n_post(#state{buckets=Buckets}, [], Res) -> %% bucket_name() -> - eqc_gen:elements(?NAMES). + proper:elements(?NAMES). bucket_props() -> - eqc_gen:list(bucket_prop()). + proper:list(bucket_prop()). bucket_prop() -> - eqc_gen:oneof( + proper:oneof( [ - {n_val, pos_integer()}, + {n_val, proper:pos_integer()}, {bucket_prop_name(), bucket_prop_value()} ] ). -pos_integer() -> - ?LET(N, eqc_gen:nat(), N + 1). +%pos_integer() -> +% ?LET(N, proper:nat(), N + 1). bucket_prop_name() -> - eqc_gen:elements(?BPROP_KEYS). + proper:elements(?BPROP_KEYS). bucket_prop_value() -> - eqc_gen:bool(). + proper:bool(). %% @@ -240,14 +240,14 @@ prop_buckets() -> fun() -> run_commands(?MODULE, Cmds) end), - pretty_commands( - ?MODULE, Cmds, - {H, S, Res}, + % pretty_commands( + % ?MODULE, Cmds, + % {H, S, Res}, aggregate( command_names(Cmds), Res == ok ) - ) + % ) end ) ) diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index 69fd4952f..58d471f3b 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -21,10 +21,9 @@ %% ------------------------------------------------------------------- -module(node_watcher_qc). --ifdef(EQC). +-ifdef(PROPER). --include_lib("eqc/include/eqc.hrl"). --include_lib("eqc/include/eqc_statem.hrl"). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -compile(export_all). @@ -36,16 +35,16 @@ peers = []}). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -define(ORDSET(L), ordsets:from_list(L)). qc_test_() -> - {timeout, 120, fun() -> ?assert(eqc:quickcheck(?QC_OUT(prop_main()))) end}. + {timeout, 120, fun() -> ?assert(proper:quickcheck(?QC_OUT(prop_main()))) end}. prop_main() -> - ?SETUP( - fun setup_cleanup/0, + %?SETUP( + % fun setup_cleanup/0, ?FORALL(Cmds, commands(?MODULE), begin %% Setup ETS table to recv broadcasts @@ -72,7 +71,7 @@ prop_main() -> _ -> io:format(user, "QC result: ~p\n", [Res]) end, aggregate(command_names(Cmds), Res == ok) - end)). + end).%). setup_cleanup() -> meck:unload(), diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index 6e9c84ee6..e151261ab 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -7,10 +7,8 @@ -module(riak_core_claim_statem). --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). -%%-include_lib("eqc/include/eqc_statem.hrl"). - -include_lib("eqc/include/eqc_fsm.hrl"). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -define(CLAIMANT, node_0). @@ -29,14 +27,14 @@ }). %% @doc run the statem with a ring of size `math:pow(`N', 2)'. --spec with_ring_size(pos_integer()) -> eqc_statem:symbolic_state(). +-spec with_ring_size(pos_integer()) -> proper:symbolic_state(). with_ring_size(N) -> RingSize = trunc(math:pow(2, N)), #state{ring_size=RingSize, ring=riak_core_ring:fresh(RingSize, ?CLAIMANT)}. %% @doc Returns the state in which each test case starts. (Unless a different %% initial state is supplied explicitly to, e.g. commands/2.) --spec initial_state_data() -> eqc_statem:symbolic_state(). +-spec initial_state_data() -> proper:symbolic_state(). initial_state_data() -> #state{}. @@ -66,10 +64,10 @@ add_node_pre(_From, _To, _) -> true. %% @doc add_node_args - Argument generator --spec add_node_args(From, To, S) -> eqc_gen:gen([term()]) - when From :: eqc_fsm:state_name(), - To :: eqc_fsm:state_name(), - S :: eqc_statem:symbolic_state(). +-spec add_node_args(From, To, S) -> proper:gen([term()]) + when From :: proper:state_name(), + To :: proper:state_name(), + S :: proper:symbolic_state(). add_node_args(_From, _To, #state{node_counter=NC, ring=Ring}) -> %% TODO consider re-adding removed nodes [list_to_atom("node_" ++ integer_to_list(NC)), @@ -82,16 +80,16 @@ add_node(NodeName, Ring) -> %% @doc add_node_next - Next state function -spec add_node_next(_From, _To, S, Var, Args) -> NewS - when S :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(), - Var :: eqc_statem:var() | term(), + when S :: proper:symbolic_state() | proper:dynamic_state(), + Var :: proper:var() | term(), Args :: [term()], - NewS :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(). + NewS :: proper:symbolic_state() | proper:dynamic_state(). add_node_next(_From, _To, S=#state{node_counter=NC, nodes=Nodes}, Ring, [Node, _RingIn]) -> S#state{ring=Ring, node_counter=NC+1, nodes=[Node | Nodes]}. %% @doc add_node_post - Postcondition for add_node -spec add_node_post(_From, _To, S, Args, Res) -> true | term() - when S :: eqc_state:dynamic_state(), + when S :: proper:dynamic_state(), Args :: [term()], Res :: term(). add_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> @@ -105,10 +103,10 @@ leave_node_pre(_From, _To, _) -> true. %% @doc leave_node_args - Argument generator --spec leave_node_args(From, To, S) -> eqc_gen:gen([term()]) - when From :: eqc_fsm:state_name(), - To :: eqc_fsm:state_name(), - S :: eqc_statem:symbolic_state(). +-spec leave_node_args(From, To, S) -> proper:gen([term()]) + when From :: proper:state_name(), + To :: proper:state_name(), + S :: proper:symbolic_state(). leave_node_args(_From, _To, #state{nodes=Nodes, ring=Ring}) -> %% TODO consider re-leaveing leaved nodes [elements(Nodes), @@ -126,16 +124,16 @@ leave_node(NodeName, Ring) -> %% @doc leave_node_next - Next state function -spec leave_node_next(_From, _To, S, Var, Args) -> NewS - when S :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(), - Var :: eqc_statem:var() | term(), + when S :: proper:symbolic_state() | proper:dynamic_state(), + Var :: proper:var() | term(), Args :: [term()], - NewS :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(). + NewS :: proper:symbolic_state() | proper:dynamic_state(). leave_node_next(_From, _To, S=#state{committed_nodes=Committed, nodes=Nodes}, Ring, [Node, _RingIn]) -> S#state{ring=Ring, committed_nodes=lists:delete(Node , Committed), nodes=lists:delete(Node, Nodes)}. %% @doc leave_node_post - Postcondition for leave_node -spec leave_node_post(_From, _To, S, Args, Res) -> true | term() - when S :: eqc_state:dynamic_state(), + when S :: proper:dynamic_state(), Args :: [term()], Res :: term(). leave_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> @@ -144,14 +142,14 @@ leave_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> %% --- Operation: claim --- %% @doc claim_pre/3 - Precondition for generation --spec claim_pre(_From, _To, S :: eqc_statem:symbolic_state()) -> boolean(). +-spec claim_pre(_From, _To, S :: proper:symbolic_state()) -> boolean(). claim_pre(_From, _To, #state{ring=undefined}) -> false; claim_pre(_From, _To, _S) -> true. %% @doc claim_args - Argument generator --spec claim_args(_From, _To, S :: eqc_statem:symbolic_state()) -> eqc_gen:gen([term()]). +-spec claim_args(_From, _To, S :: proper:symbolic_state()) -> proper:gen([term()]). claim_args(_From, _To, #state{ring=Ring}) -> [Ring]. @@ -162,16 +160,16 @@ claim(Ring) -> %% @doc claim_next - Next state function -spec claim_next(_From, _To, S, Var, Args) -> NewS - when S :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(), - Var :: eqc_statem:var() | term(), + when S :: proper:symbolic_state() | proper:dynamic_state(), + Var :: proper:var() | term(), Args :: [term()], - NewS :: eqc_statem:symbolic_state() | eqc_state:dynamic_state(). + NewS :: proper:symbolic_state() | proper:dynamic_state(). claim_next(_From, _To, S=#state{nodes=Nodes}, NewRing, [_OldRing]) -> S#state{ring=NewRing, committed_nodes=Nodes}. %% @doc claim_post - Postcondition for claim -spec claim_post(_From, _To, S, Args, Res) -> true | term() - when S :: eqc_state:dynamic_state(), + when S :: proper:dynamic_state(), Args :: [term()], Res :: term(). claim_post(_From, _To, #state{nodes=Nodes}, [_Ring], _NewRing) when length(Nodes) < 4 -> @@ -212,17 +210,17 @@ claim_post(_From, _To, _S, [_Ring], NewRing) -> %% true. %% @doc Default generated property --spec prop_claim(eqc_statem:symbolic_state()) -> eqc:property(). +-spec prop_claim(proper:symbolic_state()) -> proper:property(). prop_claim(InitialState) -> ?FORALL(Cmds, commands(?MODULE, {starting, InitialState}), begin {H, {_FinalStateName, S}, Res} = run_commands(?MODULE, Cmds), - Ring = S#state.ring, - pretty_commands(?MODULE, Cmds, {H, S, Res}, - aggregate(command_names(Cmds), - measure(ring_size, ring_size(Ring), - measure(node_count, node_count(Ring), - Res == ok)))) + Ring = S#state.ring%, + %pretty_commands(?MODULE, Cmds, {H, S, Res}, + % aggregate(command_names(Cmds), + % measure(ring_size, ring_size(Ring), + % measure(node_count, node_count(Ring), + % Res == ok)))) end). ring_size(undefined) -> @@ -242,12 +240,12 @@ weight(_, _, _, _) -> %% eunit stuff -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). eqc_check(File, Prop) -> {ok, Bytes} = file:read_file(File), CE = binary_to_term(Bytes), - eqc:check(Prop, CE). + proper:check(Prop, CE). %% Helpers transfer_ring(Ring) -> diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index 0d31b0460..d995eb166 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -1,30 +1,30 @@ -module(vclock_qc). --ifdef(EQC). - --include_lib("eqc/include/eqc.hrl"). --include_lib("eqc/include/eqc_statem.hrl"). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -compile(export_all). -define(ACTOR_IDS, [a,b,c,d,e]). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -record(state, {vclocks = []}). -define(TEST_TIME, 20). -eqc_test_() -> +proper_test_() -> {timeout, - 60, - ?_assert(quickcheck(eqc:testing_time(?TEST_TIME, more_commands(10,?QC_OUT(prop_vclock())))))}. - + 60, + %?_assert(proper:quickcheck(testing_time(?TEST_TIME, more_commands(10,prop_vclock()))))}. + ?_assert(proper:quickcheck(more_commands(10,prop_vclock())))}. test() -> - quickcheck(eqc:testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). + %proper:quickcheck(testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). + proper:quickcheck(more_commands(10, prop_vclock())). test(Time) -> - quickcheck(eqc:testing_time(Time, more_commands(10, prop_vclock()))). + %proper:quickcheck(testing_time(Time, more_commands(10, prop_vclock()))). + proper:quickcheck(more_commands(10, prop_vclock())). %% Initialize the state @@ -99,12 +99,16 @@ postcondition(_S, _C, _Res) -> prop_vclock() -> ?FORALL(Cmds,commands(?MODULE), begin - put(timestamp, 1), - {H,S,Res} = run_commands(?MODULE,Cmds), - aggregate([ length(V) || {_,V} <- S#state.vclocks ], - aggregate(command_names(Cmds), - collect({num_vclocks_div_10, length(S#state.vclocks) div 10}, - pretty_commands(?MODULE,Cmds, {H,S,Res}, Res == ok)))) + put(timestamp,1), + {_H, _S, Res} = run_commands(?MODULE, Cmds), + aggregate(command_names(Cmds), Res == ok) + %put(timestamp, 1), + %{H,S,Res} = run_commands(?MODULE, Cmds), + %aggregate([ length(V) || {_,V} <- S#state.vclocks], + %aggregate(command_names(Cmds), + % collect({num_vclocks_div_10, length(S#state.vclocks) div 10}, + % pretty_commands(?MODULE, Cmds, {H,S,Res}, Res == ok) %maybe not supported in proper + % ))) end). gen_actor_id() -> From cfe33f6d7df6b921eb5e9174f2a70d0760959333 Mon Sep 17 00:00:00 2001 From: Peter Zeller Date: Tue, 2 Jun 2020 15:20:05 +0200 Subject: [PATCH 02/11] quickcheck wip --- rebar.config | 2 +- test/eqc/bprops_eqc.erl | 7 +++++++ test/eqc/node_watcher_qc.erl | 6 +++--- test/eqc/vclock_qc.erl | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index c80e61c91..669dd5bba 100644 --- a/rebar.config +++ b/rebar.config @@ -27,7 +27,7 @@ {proper, [ {erl_opts, [nowarn_export_all,{d, 'PROPER'}, {d, 'TEST'}]}, {plugins, [{coveralls, {git, "https://github.com/markusn/coveralls-erl", {branch, "master"}}}]}, - {deps, [meck, {proper, "1.2.0"}, recon]} + {deps, [meck, {proper, "1.3.0"}, recon]} ]}, {lint, [ {plugins, [ diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index d1732923a..c0da87b5e 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -87,6 +87,13 @@ cover(N) -> proper:quickcheck(proper:numtests(N, prop_buckets())), cover:analyse_to_file(riak_core_bucket, [html]). +%% + +command(State) -> + oneof([ + {call, ?MODULE, set_bucket, set_bucket_args(State)} + % TODO marcel ... + ]). %% %% eqc_statem initial model diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index 58d471f3b..44d9e0d8a 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -43,8 +43,8 @@ qc_test_() -> {timeout, 120, fun() -> ?assert(proper:quickcheck(?QC_OUT(prop_main()))) end}. prop_main() -> - %?SETUP( - % fun setup_cleanup/0, + ?SETUP( + fun setup_cleanup/0, ?FORALL(Cmds, commands(?MODULE), begin %% Setup ETS table to recv broadcasts @@ -71,7 +71,7 @@ prop_main() -> _ -> io:format(user, "QC result: ~p\n", [Res]) end, aggregate(command_names(Cmds), Res == ok) - end).%). + end)). setup_cleanup() -> meck:unload(), diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index d995eb166..262af1435 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -17,7 +17,7 @@ proper_test_() -> {timeout, 60, %?_assert(proper:quickcheck(testing_time(?TEST_TIME, more_commands(10,prop_vclock()))))}. - ?_assert(proper:quickcheck(more_commands(10,prop_vclock())))}. + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}, {spec_timeout, 1}]))}. test() -> %proper:quickcheck(testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). proper:quickcheck(more_commands(10, prop_vclock())). From 3de90804c9111ad8824c25e7c311f79924e7bba4 Mon Sep 17 00:00:00 2001 From: woelki Date: Mon, 8 Jun 2020 17:29:41 +0200 Subject: [PATCH 03/11] wip --- Makefile | 3 +- test/eqc/bprops_eqc.erl | 71 +++++++++++++++++------------ test/eqc/node_watcher_qc.erl | 4 +- test/eqc/riak_core_claim_statem.erl | 16 ++++--- test/eqc/vclock_qc.erl | 30 ++++++------ 5 files changed, 71 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index 7e2946a9e..215fc23ca 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,8 @@ pulse: ${REBAR} eunit -D PULSE skip_deps=true suite=$(PULSE_TESTS) proper: - ${REBAR} as proper do eunit + ${REBAR} as proper do eunit + ${REBAR} cover --verbose epc: ${REBAR} as epc eunit diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index c0da87b5e..804715ca1 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -59,12 +59,13 @@ %% Eunit entrypoints %% -bprops_test_() -> { - timeout, 60, - ?_test(?assert( +bprops_test_() -> %{ + %timeout, 60, + %?_test(?assert( %proper:quickcheck(?QC_OUT(proper:testing_time(50, prop_buckets()))))) - proper:quickcheck(?QC_OUT(prop_buckets())))) - }. + %proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 100}]))) + ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 100}])). + %}. %% %% top level drivers (for testing by hand, typically) @@ -88,11 +89,20 @@ cover(N) -> cover:analyse_to_file(riak_core_bucket, [html]). %% - +%TODO command(State) -> - oneof([ - {call, ?MODULE, set_bucket, set_bucket_args(State)} - % TODO marcel ... + oneof([{call, ?MODULE, set_bucket, set_bucket_args(State)}, + {call, ?MODULE, get_bucket, get_bucket_args(State)} + %[{call, ?MODULE,append_bucket_defaults/1}] + %[{call, ?MODULE,get_bucket/2}] ++ + %[{call, ?MODULE,reset_bucket/1}] ++ + %[{call, ?MODULE,get_buckets/1}] ++ + %[{call, ?MODULE,bucket_nval_map/1}] ++ + %[{call, ?MODULE,default_object_navl,[]}] ++ + %[{call, ?MODULE,merge_props/2}] ++ + %[{call, ?MODULE,name/1}] ++ + %[{call, ?MODULE,nval/1}] ++ + %[{call, ?MODULE,get_vault/2}] ]). %% @@ -113,21 +123,8 @@ set_bucket_args(_S) -> set_bucket(Bucket, BProps) -> riak_core_bucket:set_bucket(Bucket, BProps). -set_bucket_post(#state{buckets=Buckets}, [Bucket, _BProps], Res) -> - case {Res, orddict:find(Bucket, Buckets)} of - %% first time bucket has been set - {ok, error} -> - true; - %% bucket has been set before - {ok, {ok, _OldBProps}} -> - true; - %% anything other than ok is a failure - %% TODO revisit, e.g., generate invalid inputs to force an error - _ -> - false - end. - -set_bucket_next(#state{buckets=Buckets} = S, _Res, [Bucket, BProps]) -> +next_state(#state{buckets=Buckets} = S,{call,?MODULE, set_bucket, [Bucket, BProps]}) -> +%set_bucket_next(#state{buckets=Buckets} = S, _Res, [Bucket, BProps]) %% %% Get any previously defined properties from the model %% @@ -164,7 +161,8 @@ get_bucket_args(_S) -> get_bucket(Bucket) -> riak_core_bucket:get_bucket(Bucket). -get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) -> +%get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) +postcondition(#state{buckets=Buckets},{call,?MODULE,get_bucket, [Bucket]}, Res) -> BPropsFind = orddict:find(Bucket, Buckets), case {Res, BPropsFind} of {error, _} -> @@ -179,6 +177,20 @@ get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) -> orddict:from_list(Res), orddict:from_list(?DEFAULT_BPROPS ++ [{name, Bucket}]) ) + end; +postcondition(#state{buckets=Buckets},{call,?MODULE, set_bucket, [Bucket, _BProps]}, Res) -> +%set_bucket_post(#state{buckets=Buckets}, [Bucket, _BProps], Res) + case {Res, orddict:find(Bucket, Buckets)} of + %% first time bucket has been set + {ok, error} -> + true; + %% bucket has been set before + {ok, {ok, _OldBProps}} -> + true; + %% anything other than ok is a failure + %% TODO revisit, e.g., generate invalid inputs to force an error + _ -> + false end. %% @@ -192,6 +204,7 @@ all_n() -> riak_core_bucket:all_n(Ring). all_n_post(#state{buckets=Buckets}, [], Res) -> +%postcondition(#state{buckets=Buckets}, [], Res) -> AllNVals = orddict:fold( fun(_Bucket, BProps, Accum) -> {ok, NVal} = orddict:find(n_val, BProps), @@ -216,15 +229,15 @@ bucket_props() -> proper:list(bucket_prop()). bucket_prop() -> - proper:oneof( + oneof( [ - {n_val, proper:pos_integer()}, + {n_val, pos_int()}, {bucket_prop_name(), bucket_prop_value()} ] ). -%pos_integer() -> -% ?LET(N, proper:nat(), N + 1). +pos_int() -> + ?LET(N, proper:nat(), N + 1). bucket_prop_name() -> proper:elements(?BPROP_KEYS). diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index 44d9e0d8a..34584d7d4 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -40,7 +40,9 @@ -define(ORDSET(L), ordsets:from_list(L)). qc_test_() -> - {timeout, 120, fun() -> ?assert(proper:quickcheck(?QC_OUT(prop_main()))) end}. + %{timeout, 100, fun() -> + %?assert(proper:quickcheck(prop_main(),[{numtests, 10}])) end}. + ?_assert(proper:quickcheck(prop_main(),[{numtests, 10}])). prop_main() -> ?SETUP( diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index e151261ab..ab65ceeca 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -15,6 +15,9 @@ -compile(export_all). +%Entry EUNIT +claimStatem_test()-> + ?_assert(proper:quickcheck(prop_claim(initial_state_data()))). %% -- State ------------------------------------------------------------------ -record(state, @@ -215,12 +218,13 @@ prop_claim(InitialState) -> ?FORALL(Cmds, commands(?MODULE, {starting, InitialState}), begin {H, {_FinalStateName, S}, Res} = run_commands(?MODULE, Cmds), - Ring = S#state.ring%, - %pretty_commands(?MODULE, Cmds, {H, S, Res}, - % aggregate(command_names(Cmds), - % measure(ring_size, ring_size(Ring), - % measure(node_count, node_count(Ring), - % Res == ok)))) + Ring = S#state.ring, + %pretty_commands(?MODULE, Cmds, + %{H, S, Res}, + aggregate(command_names(Cmds), + measure(ring_size, ring_size(Ring), + measure(node_count, node_count(Ring), + Res == ok)))%) end). ring_size(undefined) -> diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index 262af1435..d9832c4c8 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -14,17 +14,17 @@ -define(TEST_TIME, 20). proper_test_() -> - {timeout, - 60, + %{timeout, + %60, %?_assert(proper:quickcheck(testing_time(?TEST_TIME, more_commands(10,prop_vclock()))))}. - ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}, {spec_timeout, 1}]))}. + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 100}])).%}. test() -> %proper:quickcheck(testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). proper:quickcheck(more_commands(10, prop_vclock())). -test(Time) -> - %proper:quickcheck(testing_time(Time, more_commands(10, prop_vclock()))). - proper:quickcheck(more_commands(10, prop_vclock())). +% test(Time) -> +% %proper:quickcheck(testing_time(Time, more_commands(10, prop_vclock()))). +% proper:quickcheck(more_commands(10, prop_vclock())). %% Initialize the state @@ -99,16 +99,14 @@ postcondition(_S, _C, _Res) -> prop_vclock() -> ?FORALL(Cmds,commands(?MODULE), begin - put(timestamp,1), - {_H, _S, Res} = run_commands(?MODULE, Cmds), - aggregate(command_names(Cmds), Res == ok) - %put(timestamp, 1), - %{H,S,Res} = run_commands(?MODULE, Cmds), - %aggregate([ length(V) || {_,V} <- S#state.vclocks], - %aggregate(command_names(Cmds), - % collect({num_vclocks_div_10, length(S#state.vclocks) div 10}, - % pretty_commands(?MODULE, Cmds, {H,S,Res}, Res == ok) %maybe not supported in proper - % ))) + put(timestamp, 1), + {H,S,Res} = run_commands(?MODULE, Cmds), + aggregate([ length(V) || {_,V} <- S#state.vclocks], + aggregate(command_names(Cmds), + collect({num_vclocks_div_10, length(S#state.vclocks) div 10}, + %pretty_commands(?MODULE, Cmds, {H,S,Res}, + Res == ok%) %maybe not supported in proper + ))) end). gen_actor_id() -> From 78ce0141587056f432ded90d52f020853d57ab4e Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 9 Jun 2020 01:12:44 +0200 Subject: [PATCH 04/11] #38 rewrite done --- test/eqc/bprops_eqc.erl | 76 +++++++++++++++-------------- test/eqc/node_watcher_qc.erl | 7 ++- test/eqc/riak_core_claim_statem.erl | 8 ++- test/eqc/vclock_qc.erl | 7 ++- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index 804715ca1..18f8e0bc2 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -20,9 +20,9 @@ -module(bprops_eqc). %% -%% This module defines a collection of EQC state_m commands, for +%% This module defines a collection of PROPER state_m commands, for %% testing the riak_core_bucket module. In order to understand this -%% test, you should understand EQC generally, and the EQC state machine +%% test, you should understand PROPER generally, and the PROPER state machine %% testing framework and callback conventions. %% %% TODO This module currently tests a limited subset of the @@ -59,13 +59,10 @@ %% Eunit entrypoints %% -bprops_test_() -> %{ - %timeout, 60, - %?_test(?assert( - %proper:quickcheck(?QC_OUT(proper:testing_time(50, prop_buckets()))))) - %proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 100}]))) - ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 100}])). - %}. +bprops_test_() -> + {timeout, 10000, + ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 10000}])) + }. %% %% top level drivers (for testing by hand, typically) @@ -123,7 +120,7 @@ set_bucket_args(_S) -> set_bucket(Bucket, BProps) -> riak_core_bucket:set_bucket(Bucket, BProps). -next_state(#state{buckets=Buckets} = S,{call,?MODULE, set_bucket, [Bucket, BProps]}) -> +next_state(#state{buckets=Buckets} = S,_Res,{call,?MODULE, set_bucket, [Bucket, BProps]}) -> %set_bucket_next(#state{buckets=Buckets} = S, _Res, [Bucket, BProps]) %% %% Get any previously defined properties from the model @@ -144,13 +141,17 @@ next_state(#state{buckets=Buckets} = S,{call,?MODULE, set_bucket, [Bucket, BProp ), Buckets ) - }. + }; +next_state(S,_Res,{call,?MODULE, get_bucket, [Bucket]}) -> + S. -spec expected_properties(bucket_name(), orddict(), orddict()) -> orddict(). expected_properties(Bucket, OldProps, NewProps) -> Props = riak_core_bucket_props:merge(NewProps, OldProps), orddict:store(name, Bucket, Props). +eq(A,B)-> + A=:=B. %% %% get_bucket command %% @@ -161,23 +162,26 @@ get_bucket_args(_S) -> get_bucket(Bucket) -> riak_core_bucket:get_bucket(Bucket). +precondition(_S, {call, ?MODULE, _,_})-> + true. %get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) postcondition(#state{buckets=Buckets},{call,?MODULE,get_bucket, [Bucket]}, Res) -> BPropsFind = orddict:find(Bucket, Buckets), case {Res, BPropsFind} of {error, _} -> - equals(Res, error); + eq(Res, error); {_, {ok, BProps}} -> - equals( + eq( orddict:from_list(Res), orddict:from_list(BProps) ); {_, error} -> - equals( + eq( orddict:from_list(Res), orddict:from_list(?DEFAULT_BPROPS ++ [{name, Bucket}]) ) end; + postcondition(#state{buckets=Buckets},{call,?MODULE, set_bucket, [Bucket, _BProps]}, Res) -> %set_bucket_post(#state{buckets=Buckets}, [Bucket, _BProps], Res) case {Res, orddict:find(Bucket, Buckets)} of @@ -194,26 +198,26 @@ postcondition(#state{buckets=Buckets},{call,?MODULE, set_bucket, [Bucket, _BProp end. %% -%% all_n command +%% all_n command %TODO %% -all_n_args(_) -> []. +% all_n_args(_) -> []. -all_n() -> - {ok, Ring} = riak_core_ring_manager:get_my_ring(), - riak_core_bucket:all_n(Ring). +% all_n() -> +% {ok, Ring} = riak_core_ring_manager:get_my_ring(), +% riak_core_bucket:all_n(Ring). -all_n_post(#state{buckets=Buckets}, [], Res) -> -%postcondition(#state{buckets=Buckets}, [], Res) -> - AllNVals = orddict:fold( - fun(_Bucket, BProps, Accum) -> - {ok, NVal} = orddict:find(n_val, BProps), - [NVal | Accum] - end, - [], - Buckets - ) ++ [proplists:get_value(n_val, ?DEFAULT_BPROPS)], - equals(ordsets:from_list(Res), ordsets:from_list(AllNVals)). +% all_n_post(#state{buckets=Buckets}, [], Res) -> +% %postcondition(#state{buckets=Buckets}, {call,?MODULE,_,[]}, Res) -> +% AllNVals = orddict:fold( +% fun(_Bucket, BProps, Accum) -> +% {ok, NVal} = orddict:find(n_val, BProps), +% [NVal | Accum] +% end, +% [], +% Buckets +% ) ++ [proplists:get_value(n_val, ?DEFAULT_BPROPS)], +% equals(ordsets:from_list(Res), ordsets:from_list(AllNVals)). %% TODO Add more commands here @@ -223,10 +227,10 @@ all_n_post(#state{buckets=Buckets}, [], Res) -> %% bucket_name() -> - proper:elements(?NAMES). + proper_types:elements(?NAMES). bucket_props() -> - proper:list(bucket_prop()). + proper_types:list(bucket_prop()). bucket_prop() -> oneof( @@ -237,17 +241,17 @@ bucket_prop() -> ). pos_int() -> - ?LET(N, proper:nat(), N + 1). + ?LET(N, proper_types:nat(), N + 1). bucket_prop_name() -> - proper:elements(?BPROP_KEYS). + proper_types:elements(?BPROP_KEYS). bucket_prop_value() -> - proper:bool(). + proper_types:bool(). %% -%% eqc properties +%% proper properties %% prop_buckets() -> diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index 34584d7d4..faf0e5e07 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -40,9 +40,8 @@ -define(ORDSET(L), ordsets:from_list(L)). qc_test_() -> - %{timeout, 100, fun() -> - %?assert(proper:quickcheck(prop_main(),[{numtests, 10}])) end}. - ?_assert(proper:quickcheck(prop_main(),[{numtests, 10}])). + {timeout, 10000, + ?_assert(proper:quickcheck(prop_main(),[{numtests, 10000}]))}. prop_main() -> ?SETUP( @@ -103,7 +102,7 @@ ensure_started(Mod) -> %% ==================================================================== -%% eqc_statem callbacks +%%proper_statem callbacks %% ==================================================================== initial_state() -> diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index ab65ceeca..19daab3ce 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -15,9 +15,10 @@ -compile(export_all). -%Entry EUNIT +%Entry Eunit claimStatem_test()-> - ?_assert(proper:quickcheck(prop_claim(initial_state_data()))). + {timeout, 10000, + ?_assert(proper:quickcheck(prop_claim(initial_state_data()),[{numtests, 10000}]))}. %% -- State ------------------------------------------------------------------ -record(state, @@ -208,9 +209,6 @@ claim_post(_From, _To, _S, [_Ring], NewRing) -> %% -- Property --------------------------------------------------------------- %% @doc Optional callback, Invariant, checked for each visited state %% during test execution. -%% -spec invariant(S :: eqc_statem:dynamic_state()) -> boolean(). -%% invariant(_S) -> -%% true. %% @doc Default generated property -spec prop_claim(proper:symbolic_state()) -> proper:property(). diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index d9832c4c8..a7868eb9a 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -14,10 +14,9 @@ -define(TEST_TIME, 20). proper_test_() -> - %{timeout, - %60, - %?_assert(proper:quickcheck(testing_time(?TEST_TIME, more_commands(10,prop_vclock()))))}. - ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 100}])).%}. + {timeout, + 10000, + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}]))}. test() -> %proper:quickcheck(testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). proper:quickcheck(more_commands(10, prop_vclock())). From 54ffa3f18487dc8d637db8bba0a1f86800724aca Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 9 Jun 2020 01:22:12 +0200 Subject: [PATCH 05/11] #38 update travis --- .travis.yml | 1 + test/eqc/vclock_qc.erl | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index fe49fe924..0dea3a613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ install: - ./rebar3 update script: - make test + - make proper - make coverage - ./rebar3 as test coveralls send - make lint diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index a7868eb9a..2c6605586 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -18,12 +18,8 @@ proper_test_() -> 10000, ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}]))}. test() -> - %proper:quickcheck(testing_time(?TEST_TIME, more_commands(10, prop_vclock()))). proper:quickcheck(more_commands(10, prop_vclock())). -% test(Time) -> -% %proper:quickcheck(testing_time(Time, more_commands(10, prop_vclock()))). -% proper:quickcheck(more_commands(10, prop_vclock())). %% Initialize the state From fdee67adeb8c8e4bd78be5cf545cd529d23622cf Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 9 Jun 2020 13:18:03 +0200 Subject: [PATCH 06/11] #38 adjust number of test executions --- Makefile | 2 +- test/eqc/bprops_eqc.erl | 4 ++-- test/eqc/node_watcher_qc.erl | 4 ++-- test/eqc/riak_core_claim_statem.erl | 4 ++-- test/eqc/vclock_qc.erl | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 215fc23ca..d04be2824 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ coverage: compile ${REBAR} cover --verbose docs: - ${REBAR} doc + ${REBAR} edoc xref: compile ${REBAR} xref diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index 18f8e0bc2..e5a3aeedd 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -60,8 +60,8 @@ %% bprops_test_() -> - {timeout, 10000, - ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 10000}])) + {timeout, 5000, + ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 5000}])) }. %% diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index faf0e5e07..d0494ddbe 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -40,8 +40,8 @@ -define(ORDSET(L), ordsets:from_list(L)). qc_test_() -> - {timeout, 10000, - ?_assert(proper:quickcheck(prop_main(),[{numtests, 10000}]))}. + {timeout, 5000, + ?_assert(proper:quickcheck(prop_main(),[{numtests, 5000}]))}. prop_main() -> ?SETUP( diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index 19daab3ce..efce6f7fb 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -17,8 +17,8 @@ %Entry Eunit claimStatem_test()-> - {timeout, 10000, - ?_assert(proper:quickcheck(prop_claim(initial_state_data()),[{numtests, 10000}]))}. + {timeout, 5000, + ?_assert(proper:quickcheck(prop_claim(initial_state_data()),[{numtests, 5000}]))}. %% -- State ------------------------------------------------------------------ -record(state, diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index 2c6605586..8e4d7c15f 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -15,8 +15,8 @@ proper_test_() -> {timeout, - 10000, - ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}]))}. + 5000, + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 5000}]))}. test() -> proper:quickcheck(more_commands(10, prop_vclock())). From a258bc0ae29b7e40521ed1ba5b657d23a7fb12a3 Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 16 Jun 2020 13:58:32 +0200 Subject: [PATCH 07/11] #38 update coverage + bug fixing in riak_core_claim --- Makefile | 9 +- rebar.config | 2 +- test/eqc/bprops_eqc.erl | 4 +- test/eqc/node_watcher_qc.erl | 9 +- test/eqc/riak_core_claim_statem.erl | 264 ++++++++++++++-------------- test/eqc/vclock_qc.erl | 4 +- 6 files changed, 151 insertions(+), 141 deletions(-) diff --git a/Makefile b/Makefile index d04be2824..10de56d2a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PULSE_TESTS = worker_pool_pulse - +COVERPATH = ./_build/test/cover REBAR ?= ./rebar3 .PHONY: deps test docs xref dialyzer @@ -12,7 +12,6 @@ compile: deps clean: clean-test ${REBAR} clean - distclean: clean clean-test: @@ -35,8 +34,9 @@ pulse: proper: ${REBAR} as proper do eunit - ${REBAR} cover --verbose - +p: + ${REBAR} proper + epc: ${REBAR} as epc eunit @@ -44,6 +44,7 @@ test: compile ${REBAR} eunit coverage: compile + cp _build/proper+test/cover/eunit.coverdata ${COVERPATH}/proper.coverdata ;\ ${REBAR} cover --verbose docs: diff --git a/rebar.config b/rebar.config index 669dd5bba..1bb99de04 100644 --- a/rebar.config +++ b/rebar.config @@ -38,5 +38,5 @@ {cover_enabled, true}. {cover_export_enabled, true}. -{coveralls_coverdata, "_build/test/cover/eunit.coverdata"}. +{coveralls_coverdata, "_build/test/cover/*.coverdata"}. {coveralls_service_name, "travis-ci"}. diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index e5a3aeedd..18f8e0bc2 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -60,8 +60,8 @@ %% bprops_test_() -> - {timeout, 5000, - ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 5000}])) + {timeout, 10000, + ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 10000}])) }. %% diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index d0494ddbe..30fd2ee23 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -40,8 +40,9 @@ -define(ORDSET(L), ordsets:from_list(L)). qc_test_() -> - {timeout, 5000, - ?_assert(proper:quickcheck(prop_main(),[{numtests, 5000}]))}. + {timeout, 10000, + ?_assert(proper:quickcheck(prop_main(),[{numtests, 10000}])) + }. prop_main() -> ?SETUP( @@ -114,8 +115,8 @@ command(S) -> {call, ?MODULE, local_service_up, [g_service()]}, {call, ?MODULE, local_service_down, [g_service()]}, {call, ?MODULE, local_service_kill, [g_service(), S]}, - {call, ?MODULE, local_node_up, []}, - {call, ?MODULE, local_node_down, []}, + %{call, ?MODULE, local_node_up, []}, + %{call, ?MODULE, local_node_down, []}, {call, ?MODULE, remote_service_up, [g_node(), g_services()]}, {call, ?MODULE, remote_service_down, [g_node()]}, {call, ?MODULE, remote_service_down_disterl, [g_node()]}, diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index efce6f7fb..f0347d82f 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -16,9 +16,9 @@ -compile(export_all). %Entry Eunit -claimStatem_test()-> - {timeout, 5000, - ?_assert(proper:quickcheck(prop_claim(initial_state_data()),[{numtests, 5000}]))}. +claim_test_()-> + {timeout, 10000, + ?_assert(proper:quickcheck(prop_claim(with_ring_size(5)),[{numtests, 10000}] ))}. %% -- State ------------------------------------------------------------------ -record(state, @@ -31,154 +31,124 @@ claimStatem_test()-> }). %% @doc run the statem with a ring of size `math:pow(`N', 2)'. --spec with_ring_size(pos_integer()) -> proper:symbolic_state(). +-spec with_ring_size(pos_integer()) -> proper_statem:symbolic_state(). with_ring_size(N) -> RingSize = trunc(math:pow(2, N)), #state{ring_size=RingSize, ring=riak_core_ring:fresh(RingSize, ?CLAIMANT)}. %% @doc Returns the state in which each test case starts. (Unless a different %% initial state is supplied explicitly to, e.g. commands/2.) --spec initial_state_data() -> proper:symbolic_state(). +-spec initial_state_data() -> proper_statem:symbolic_state(). initial_state_data() -> #state{}. initial_state(_S) -> starting. -starting() -> - [{planning, add_node}]. +starting(S) -> + [{planning, {call, ?MODULE, add_node, add_node_args(S)}}]. -planning() -> - [{planning, add_node}, - {planning, leave_node}, - {claiming, claim}]. +planning(S) -> + [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}%, + %{claiming, {call, ?MODULE, claim, claim_args(S)}} %TODO + ]. -claiming() -> - [{planning, add_node}, - {planning, leave_node}]. +claiming(S) -> + [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}]. %% -- Operations ------------------------------------------------------------- -%% --- Operation: add_node --- +%% --- Operation: preconditions --- %% @doc add_node_pre/1 - Precondition for generation -add_node_pre(_From, _To, S=#state{nodes=Nodes}) when +%add_node_pre(_From, _To, S=#state{nodes=Nodes}) +precondition(_F,_T,S=#state{nodes=Nodes}, {call, _, add_node, _}) when (S#state.ring_size div length(Nodes)) =< 3 -> false; -add_node_pre(_From, _To, _) -> - true. - -%% @doc add_node_args - Argument generator --spec add_node_args(From, To, S) -> proper:gen([term()]) - when From :: proper:state_name(), - To :: proper:state_name(), - S :: proper:symbolic_state(). -add_node_args(_From, _To, #state{node_counter=NC, ring=Ring}) -> - %% TODO consider re-adding removed nodes - [list_to_atom("node_" ++ integer_to_list(NC)), - Ring]. - -%% @doc add_node - The actual operation -add_node(NodeName, Ring) -> - R = riak_core_ring:add_member(?CLAIMANT, Ring, NodeName), - R. - -%% @doc add_node_next - Next state function --spec add_node_next(_From, _To, S, Var, Args) -> NewS - when S :: proper:symbolic_state() | proper:dynamic_state(), - Var :: proper:var() | term(), - Args :: [term()], - NewS :: proper:symbolic_state() | proper:dynamic_state(). -add_node_next(_From, _To, S=#state{node_counter=NC, nodes=Nodes}, Ring, [Node, _RingIn]) -> - S#state{ring=Ring, node_counter=NC+1, nodes=[Node | Nodes]}. - -%% @doc add_node_post - Postcondition for add_node --spec add_node_post(_From, _To, S, Args, Res) -> true | term() - when S :: proper:dynamic_state(), - Args :: [term()], - Res :: term(). -add_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> - lists:member(NodeName, riak_core_ring:members(NextRing, [joining])). - -%% --- Operation: leave_node --- +%add_node_pre(_From, _To, _) -> +precondition(_F,_T,_S, {call, _, add_node, _}) -> + true; %% @doc leave_node_pre/1 - Precondition for generation -leave_node_pre(_From, _To, #state{nodes=Nodes}) when length(Nodes) < 5 -> +%leave_node_pre(_From, _To, #state{nodes=Nodes}) when length(Nodes) < 5 -> +precondition(_F,_T,#state{nodes=Nodes},{call,_,leave_node,_}) when length(Nodes) < 5 -> false; -leave_node_pre(_From, _To, _) -> - true. - -%% @doc leave_node_args - Argument generator --spec leave_node_args(From, To, S) -> proper:gen([term()]) - when From :: proper:state_name(), - To :: proper:state_name(), - S :: proper:symbolic_state(). -leave_node_args(_From, _To, #state{nodes=Nodes, ring=Ring}) -> - %% TODO consider re-leaveing leaved nodes - [elements(Nodes), - Ring]. - -leave_node_pre(_From, _To, #state{nodes=Nodes}, [Node, _Ring]) -> +%leave_node_pre(_From, _To, _) -> +precondition(_F,_T,_S, {call,_,leave_node,_})-> + true; +%leave_node_pre(_From, _To, #state{nodes=Nodes}, [Node, _Ring]) -> +precondition(_F,_T,#state{nodes=Nodes},{call,_,leave_node,[Node, _Ring]}) -> lists:member(Node, Nodes); -leave_node_pre(_, _, _, _) -> - false. - -%% @doc leave_node - The actual operation -leave_node(NodeName, Ring) -> - R = riak_core_ring:leave_member(?CLAIMANT, Ring, NodeName), - R. - -%% @doc leave_node_next - Next state function --spec leave_node_next(_From, _To, S, Var, Args) -> NewS - when S :: proper:symbolic_state() | proper:dynamic_state(), - Var :: proper:var() | term(), - Args :: [term()], - NewS :: proper:symbolic_state() | proper:dynamic_state(). -leave_node_next(_From, _To, S=#state{committed_nodes=Committed, nodes=Nodes}, Ring, [Node, _RingIn]) -> - S#state{ring=Ring, committed_nodes=lists:delete(Node , Committed), nodes=lists:delete(Node, Nodes)}. - -%% @doc leave_node_post - Postcondition for leave_node --spec leave_node_post(_From, _To, S, Args, Res) -> true | term() - when S :: proper:dynamic_state(), - Args :: [term()], - Res :: term(). -leave_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> - lists:member(NodeName, riak_core_ring:members(NextRing, [leaving])). - -%% --- Operation: claim --- - -%% @doc claim_pre/3 - Precondition for generation --spec claim_pre(_From, _To, S :: proper:symbolic_state()) -> boolean(). -claim_pre(_From, _To, #state{ring=undefined}) -> +precondition(_F,_T,_S,{call,_,leave_node,_}) -> false; -claim_pre(_From, _To, _S) -> +% @doc claim_pre/3 - Precondition for generation +%-spec claim_pre(_From, _To, S :: proper:symbolic_state()) -> boolean(). +%claim_pre(_From, _To, #state{ring=undefined}) -> + precondition(_F,_T,#state{ring=undefined},{call, _, claim,_}) -> + false; +%claim_pre(_From, _To, _S) -> +precondition(_F,_T,_S,{call,_ , claim, _}) -> true. -%% @doc claim_args - Argument generator --spec claim_args(_From, _To, S :: proper:symbolic_state()) -> proper:gen([term()]). -claim_args(_From, _To, #state{ring=Ring}) -> - [Ring]. +%% --- Operation: Next state --- +%% @doc add_node_next - Next state function +% -spec add_node_next(_From, _To, S, Var, Args) -> NewS +% when S :: proper:symbolic_state() | proper:dynamic_state(), +% Var :: proper:var() | term(), +% Args :: [term()], +% NewS :: proper:symbolic_state() | proper:dynamic_state(). +%add_node_next(_From, _To, S=#state{node_counter=NC, nodes=Nodes}, Ring, [Node, _RingIn]) -> +next_state_data(_F,_T,S=#state{node_counter=NC, nodes=Nodes}, Ring, + {call,_, add_node, [Node, _RingIn]}) -> + S#state{ring=Ring, node_counter=NC+1, nodes=[Node | Nodes]}; -%% @doc claim - The actual operation -claim(Ring) -> - R =riak_core_claim:claim(Ring, {riak_core_claim, wants_claim_v2}, {riak_core_claim, choose_claim_v2}), - R. +%% @doc leave_node_next - Next state function +% -spec leave_node_next(_From, _To, S, Var, Args) -> NewS +% when S :: proper:symbolic_state() | proper:dynamic_state(), +% Var :: proper:var() | term(), +% Args :: [term()], +% NewS :: proper:symbolic_state() | proper:dynamic_state(). +next_state_data(_F,_T,S=#state{committed_nodes=Committed, nodes=Nodes},Ring,{call, _, leave_node, [Node, _RingIn]}) -> + S#state{ring=Ring, committed_nodes=lists:delete(Node , Committed), nodes=lists:delete(Node, Nodes)}; %% @doc claim_next - Next state function --spec claim_next(_From, _To, S, Var, Args) -> NewS - when S :: proper:symbolic_state() | proper:dynamic_state(), - Var :: proper:var() | term(), - Args :: [term()], - NewS :: proper:symbolic_state() | proper:dynamic_state(). -claim_next(_From, _To, S=#state{nodes=Nodes}, NewRing, [_OldRing]) -> +% -spec claim_next(_From, _To, S, Var, Args) -> NewS +% when S :: proper:symbolic_state() | proper:dynamic_state(), +% Var :: proper:var() | term(), +% Args :: [term()], +% NewS :: proper:symbolic_state() | proper:dynamic_state(). +%claim_next(_From, _To, S=#state{nodes=Nodes}, NewRing, [_OldRing]) -> +next_state_data(_F,_T,S=#state{nodes=Nodes}, NewRing, {call, _, claim, [_OldRing]}) -> S#state{ring=NewRing, committed_nodes=Nodes}. +%% --- Operation: postconditions --- +%% @doc add_node_post - Postcondition for add_node +% -spec add_node_post(_From, _To, S, Args, Res) -> true | term() +% when S :: proper:dynamic_state(), +% Args :: [term()], +% Res :: term(). +%add_node_post(_Frim, _To, _S, [NodeName, _Ring], NextRing) -> +postcondition(_F,_T,_S,{call,_, add_node, [NodeName, _Ring]}, NextRing) -> + lists:member(NodeName, riak_core_ring:members(NextRing, [joining])); + +%% @doc leave_node_post - Postcondition for leave_node +% -spec leave_node_post(_From, _To, S, Args, Res) -> true | term() +% when S :: proper:dynamic_state(), +% Args :: [term()], +% Res :: term(). +postcondition(_F,_T,_S,{call,_,leave_node,[NodeName, _Ring]}, NextRing) -> + lists:member(NodeName, riak_core_ring:members(NextRing, [leaving])); + %% @doc claim_post - Postcondition for claim --spec claim_post(_From, _To, S, Args, Res) -> true | term() - when S :: proper:dynamic_state(), - Args :: [term()], - Res :: term(). -claim_post(_From, _To, #state{nodes=Nodes}, [_Ring], _NewRing) when length(Nodes) < 4 -> - true; -claim_post(_From, _To, _S, [_Ring], NewRing) -> +% -spec claim_post(_From, _To, S, Args, Res) -> true | term() +% when S :: proper:dynamic_state(), +% Args :: [term()], +% Res :: term(). +%claim_post(_From, _To, #state{nodes=Nodes}, [_Ring], _NewRing) when length(Nodes) < 4 -> +postcondition(_F,_T,#state{nodes=Nodes}, {call, _, claim, [_Ring]}, _NewRing) when length(Nodes) < 4 -> + true; +%claim_post(_From, _To, _S, [_Ring], NewRing) -> +postcondition(_F,_T,_S, {call, _, claim, [_Ring]}, NewRing)-> Nval = 3, TNval = 4, Preflists = riak_core_ring:all_preflists(NewRing, Nval), @@ -206,23 +176,61 @@ claim_post(_From, _To, _S, [_Ring], NewRing) -> {balanced_ring, Z}}} end. +%% --- Operation: main functions --- +%% @doc add_node_args - Argument generator +% -spec add_node_args(From, To, S) -> proper:gen([term()]) +% when From :: proper:state_name(), +% To :: proper:state_name(), +% S :: proper:symbolic_state(). +add_node_args(#state{node_counter=NC, ring=Ring}) -> + %% TODO consider re-adding removed nodes + %io:fwrite("~n", NC), + [list_to_atom("node_" ++ integer_to_list(NC)), Ring]. + +%% @doc add_node - The actual operation +add_node(NodeName, Ring) -> + R = riak_core_ring:add_member(?CLAIMANT, Ring, NodeName), + R. + +%% --- Operation: leave_node --- +%% @doc leave_node_args - Argument generator +% -spec leave_node_args(From, To, S) -> proper:gen([term()]) +% when From :: proper:state_name(), +% To :: proper:state_name(), +% S :: proper:symbolic_state(). +leave_node_args(#state{nodes=Nodes, ring=Ring}) -> + %% TODO consider re-leaveing leaved nodes + [elements(Nodes), Ring]. +%% @doc leave_node - The actual operation +leave_node(NodeName, Ring) -> + R = riak_core_ring:leave_member(?CLAIMANT, Ring, NodeName), + R. +%% --- Operation: claim --- +%% @doc claim_args - Argument generator +%-spec claim_args(S :: proper:symbolic_state()) -> proper:gen([term()]). +%claim_args(_From, _To, #state{ring=Ring}) -> +claim_args(#state{ring=Ring}) -> + [Ring]. +%% @doc claim - The actual operation +claim(Ring) -> + R =riak_core_claim:claim(Ring, {riak_core_claim, wants_claim_v2}, {riak_core_claim, choose_claim_v2}), + R. + %% -- Property --------------------------------------------------------------- %% @doc Optional callback, Invariant, checked for each visited state %% during test execution. %% @doc Default generated property --spec prop_claim(proper:symbolic_state()) -> proper:property(). +-spec prop_claim(proper_statem:symbolic_state()) -> proper:property(). prop_claim(InitialState) -> - ?FORALL(Cmds, commands(?MODULE, {starting, InitialState}), + ?FORALL(Cmds, proper_fsm:commands(?MODULE, {starting, InitialState}), begin - {H, {_FinalStateName, S}, Res} = run_commands(?MODULE, Cmds), + {_H, {_FinalStateName, S}, Res} = proper_fsm:run_commands(?MODULE, Cmds), Ring = S#state.ring, - %pretty_commands(?MODULE, Cmds, - %{H, S, Res}, - aggregate(command_names(Cmds), - measure(ring_size, ring_size(Ring), - measure(node_count, node_count(Ring), - Res == ok)))%) + aggregate(command_names(Cmds), + measure(ring_size, ring_size(Ring), + measure(node_count, node_count(Ring), + Res == ok))) end). ring_size(undefined) -> diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index 8e4d7c15f..2c6605586 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -15,8 +15,8 @@ proper_test_() -> {timeout, - 5000, - ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 5000}]))}. + 10000, + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}]))}. test() -> proper:quickcheck(more_commands(10, prop_vclock())). From 8a135f2b85dce53dcc9e863654ffb0e682e01469 Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 7 Jul 2020 13:34:48 +0200 Subject: [PATCH 08/11] adapt the remaining tests --- .travis.yml | 1 + include/stats.hrl | 1 + src/basho_stats_histogram.erl | 186 ++++++++++++++-------------- src/basho_stats_sample.erl | 51 ++++---- src/basho_stats_utils.erl | 6 +- src/riak_core_claim.erl | 115 +++++------------ src/riak_core_claim_util.erl | 23 ++-- src/riak_core_node_watcher.erl | 4 +- src/riak_core_ring_util.erl | 26 ++-- src/riak_core_util.erl | 15 ++- test/eqc/bprops_eqc.erl | 47 +------ test/eqc/node_watcher_qc.erl | 12 +- test/eqc/riak_core_claim_statem.erl | 10 +- test/eqc/vclock_qc.erl | 9 +- 14 files changed, 213 insertions(+), 293 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0dea3a613..8803584ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ otp_release: install: - make - ./rebar3 update + - travis_wait 30 mvn install script: - make test - make proper diff --git a/include/stats.hrl b/include/stats.hrl index 03060fa35..ff91a7f63 100644 --- a/include/stats.hrl +++ b/include/stats.hrl @@ -4,6 +4,7 @@ -ifdef(TEST). -ifdef(PROPER). -include_lib("proper/include/proper.hrl"). +-compile(export_all). -endif. -ifdef(EQC). -include_lib("eqc/include/eqc.hrl"). diff --git a/src/basho_stats_histogram.erl b/src/basho_stats_histogram.erl index d8df3ddd7..bd2bb8019 100644 --- a/src/basho_stats_histogram.erl +++ b/src/basho_stats_histogram.erl @@ -28,11 +28,12 @@ counts/1, observations/1, summary_stats/1]). - --ifdef(EQC). --export([prop_count/0, - prop_quantile/0 - ]). +-ifdef(TEST). +-ifdef(PROPER). +% -export([prop_count/0, +% prop_quantile/0 +% ]). +-endif. -endif. -include("stats.hrl"). @@ -176,97 +177,100 @@ bin_count(Bin, Hist) -> %% Unit Tests %% =================================================================== --ifdef(EUNIT). +-ifdef(TEST). simple_test() -> %% Pre-calculated tests [7, 0] = counts(update_all([10, 10, 10, 10, 10, 10, 14], new(10, 18, 2))). --ifdef(EQC). - - -qc_count_check(Min, Max, Bins, Xs) -> - LCounts = counts(update_all(Xs, new(Min, Max, Bins))), - RCounts = basho_stats_utils:r_run(Xs, - ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", - [Min, Max, Bins+1])), - case LCounts == RCounts of - true -> - true; - _ -> - io:format("LCounts ~p, RCounts ~p~n", [LCounts, RCounts]), - false - end. - - -prop_count() -> - ?FORALL({Min, Bins, Xlen}, {choose(0, 99), choose(2, 20), choose(2, 100)}, - ?LET(Max, choose(Min+1, 100), - ?LET(Xs, vector(Xlen, choose(Min, Max)), - ?WHENFAIL( - begin - io:format("Min ~p, Max ~p, Bins ~p, Xs ~w~n", - [Min, Max, Bins, Xs]), - Command = ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", - [Min, Max, Bins+1]), - InputStr = [integer_to_list(I) || I <- Xs], - io:format(?FMT("x <- c(~s)\n", - [string:join(InputStr, ",")])), - io:format(?FMT("write(~s, ncolumns=1,file=stdout())\n", [Command])) - end, - qc_count_check(Min, Max, Bins, Xs))))). - -qc_count_test() -> - true = eqc:quickcheck(prop_count()). - -qc_quantile_check(Q, Min, Max, Bins, Xs) -> - Hist = new(Min, Max, Bins), - LCounts = counts(update_all(Xs, Hist)), - Lq = quantile(Q * 0.01, update_all(Xs, Hist)), - [Rq] = basho_stats_utils:r_run(Xs, - ?FMT("quantile(x, ~4.2f, type=4)", [Q * 0.01])), - case abs(Lq - Rq) < 1 of - true -> - true; - false -> - ?debugMsg("----\n"), - ?debugFmt("Q: ~p Min: ~p Max: ~p Bins: ~p\n", [Q, Min, Max, Bins]), - ?debugFmt("Lq: ~p != Rq: ~p\n", [Lq, Rq]), - ?debugFmt("Xs: ~w\n", [Xs]), - false - end. - -prop_quantile() -> - %% Loosey-goosey checking of the quantile estimation - %% against R's more precise method. - %% - %% To ensure a minimal level of accuracy, - %% we ensure that we have between 50-200 bins - %% and between 100-500 data points. - %% - %% TODO: Need to nail down the exact error bounds - %% - %% XXX since we try to generate the quantile from the histogram, not the - %% original data, our results and Rs don't always agree and this means the - %% test will occasionally fail. There's not an easy way to fix this. - ?FORALL({Min, Bins, Xlen, Q}, {choose(1, 99), choose(50, 200), - choose(100, 500), choose(0, 100)}, - ?LET(Max, choose(Min+1, 100), - ?LET(Xs, vector(Xlen, choose(Min, Max)), - ?WHENFAIL( - begin - io:format("Min ~p, Max ~p, Bins ~p, Q ~p, Xs ~w~n", - [Min, Max, Bins, Q, Xs]), - Command = ?FMT("quantile(x, ~4.2f, type=4)", [Q * 0.01]), - InputStr = [integer_to_list(I) || I <- Xs], - io:format(?FMT("x <- c(~s)\n", - [string:join(InputStr, ",")])), - io:format(?FMT("write(~s, ncolumns=1,file=stdout())\n", [Command])) - end, - qc_quantile_check(Q, Min, Max, Bins, Xs))))). - -qc_quantile_test() -> - true = eqc:quickcheck(prop_quantile()). +-ifdef(PROPER). + +%TODO ckeck bahso_stats_utils +% qc_count_check(Min, Max, Bins, Xs) -> +% LCounts = counts(update_all(Xs, new(Min, Max, Bins))), +% RCounts = basho_stats_utils:r_run(Xs, +% ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", +% [Min, Max, Bins+1])), +% io:format("Min ~p, Max ~p, Bins ~p, Xs ~w~n", +% [Min, Max, Bins, Xs]), +% case LCounts == RCounts of +% true -> +% true; +% _ -> +% io:format("LCounts ~p, RCounts ~p~n", [LCounts, RCounts]), +% false +% end. + + +% prop_count() -> +% ?FORALL({Min, Bins, Xlen}, {choose(0, 99), choose(2, 20), choose(2, 100)}, +% ?LET(Max, choose(Min+1, 100), +% ?LET(Xs, vector(Xlen, choose(Min, Max)), +% ?WHENFAIL( +% begin +% io:format("Min ~p, Max ~p, Bins ~p, Xs ~w~n", [Min, Max, Bins, Xs]), +% Command = ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", [Min, Max, Bins+1]), +% InputStr = [integer_to_list(I) || I <- Xs], +% io:format(?FMT("x <- c(~s)\n", [string:join(InputStr, ",")])), +% io:format(?FMT("write(~s, ncolumns=1,file=stdout())\n", [Command])) +% end, +% qc_count_check(Min, Max, Bins, Xs) +% )))). + +% qc_count_test() -> +% %{timeout, 5000, +% Res = prop_count(), +% Result = proper:quickcheck(Res,[long_result]),%} +% true= Result. + +% qc_quantile_check(Q, Min, Max, Bins, Xs) -> +% Hist = new(Min, Max, Bins), +% LCounts = counts(update_all(Xs, Hist)), +% Lq = quantile(Q * 0.01, update_all(Xs, Hist)), +% [Rq] = basho_stats_utils:r_run(Xs, +% ?FMT("quantile(x, ~4.2f, type=4)", [Q * 0.01])), +% case abs(Lq - Rq) < 1 of +% true -> +% true; +% false -> +% ?debugMsg("----\n"), +% ?debugFmt("Q: ~p Min: ~p Max: ~p Bins: ~p\n", [Q, Min, Max, Bins]), +% ?debugFmt("Lq: ~p != Rq: ~p\n", [Lq, Rq]), +% ?debugFmt("Xs: ~w\n", [Xs]), +% false +% end. + +% prop_quantile() -> +% %% Loosey-goosey checking of the quantile estimation +% %% against R's more precise method. +% %% +% %% To ensure a minimal level of accuracy, +% %% we ensure that we have between 50-200 bins +% %% and between 100-500 data points. +% %% +% %% TODO: Need to nail down the exact error bounds +% %% +% %% XXX since we try to generate the quantile from the histogram, not the +% %% original data, our results and Rs don't always agree and this means the +% %% test will occasionally fail. There's not an easy way to fix this. +% ?FORALL({Min, Bins, Xlen, Q}, {choose(1, 99), choose(50, 200), +% choose(100, 500), choose(0, 100)}, +% (?LET(Max, choose(Min+1, 100), +% (?LET(Xs, vector(Xlen, choose(Min, Max)), +% (?WHENFAIL( +% begin +% io:format("Min ~p, Max ~p, Bins ~p, Q ~p, Xs ~w~n", +% [Min, Max, Bins, Q, Xs]), +% Command = ?FMT("quantile(x, ~4.2f, type=4)", [Q * 0.01]), +% InputStr = [integer_to_list(I) || I <- Xs], +% io:format(?FMT("x <- c(~s)\n", +% [string:join(InputStr, ",")])), +% io:format(?FMT("write(~s, ncolumns=1,file=stdout())\n", [Command])) +% end, +% (qc_quantile_check(Q, Min, Max, Bins, Xs))))))))). + +% qc_quantile_test() -> +% true = proper:quickcheck(prop_quantile()). -endif. -endif. diff --git a/src/basho_stats_sample.erl b/src/basho_stats_sample.erl index 69f5af0d6..e9ea322b3 100644 --- a/src/basho_stats_sample.erl +++ b/src/basho_stats_sample.erl @@ -34,8 +34,8 @@ -include("stats.hrl"). --ifdef(EQC). --export([prop_main/0]). +-ifdef(PROPER). +%-export([prop_main/0]). -endif. -record(state, { n = 0, @@ -114,7 +114,7 @@ nan_max(V1, V2) -> erlang:max(V1, V2). %% Unit Tests %% =================================================================== --ifdef(EUNIT). +-ifdef(TEST). simple_test() -> %% A few hand-checked values @@ -127,27 +127,30 @@ empty_test() -> {'NaN', 'NaN', 'NaN', 'NaN', 'NaN'} = summary(new()). --ifdef(EQC). - -lists_equal([], []) -> - true; -lists_equal([V1 | R1], [V2 | R2]) -> - case abs(V1-V2) < 0.01 of - true -> - lists_equal(R1, R2); - false -> - false - end. - -prop_main() -> - ?FORALL(Xlen, choose(2, 100), - ?LET(Xs, vector(Xlen, int()), - lists_equal(basho_stats_utils:r_run(Xs, "c(min(x), mean(x), max(x), - var(x), sd(x))"), - tuple_to_list(summary(update_all(Xs, new())))))). - -qc_test() -> - true = eqc:quickcheck(prop_main()). +-ifdef(PROPER). +%TODO problem in basho_stats_utils +% lists_equal([], []) -> +% true; +% lists_equal([V1 | R1], [V2 | R2]) -> +% case abs(V1-V2) < 0.01 of +% true -> +% lists_equal(R1, R2); +% false -> +% false +% end. + +% prop_main() -> +% ?FORALL(Xlen, choose(2, 100), +% (?LET(Xs, vector(Xlen, int()), +% (lists_equal(basho_stats_utils:r_run(Xs, +% "c(min(x), mean(x), max(x),\n " +% " " +% " var(x), sd(x))"), +% tuple_to_list(summary(update_all(Xs, new())))))))). + +% qc_test() -> +% %{timeout, 5000, +% true = proper:quickcheck(prop_main()).%}. -endif. diff --git a/src/basho_stats_utils.erl b/src/basho_stats_utils.erl index 6a8f8a456..1c4962fa1 100644 --- a/src/basho_stats_utils.erl +++ b/src/basho_stats_utils.erl @@ -23,16 +23,12 @@ -include("stats.hrl"). --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --compile(export_all). --endif. %% =================================================================== %% Unit Test Helpers %% =================================================================== --ifdef(EUNIT). +-ifdef(TEST). r_run(Input, Command) -> case r_port() of diff --git a/src/riak_core_claim.erl b/src/riak_core_claim.erl index 263d62eb4..48e5951aa 100644 --- a/src/riak_core_claim.erl +++ b/src/riak_core_claim.erl @@ -81,6 +81,14 @@ meets_target_n/2, diagonal_stripe/2]). +-ifdef(TEST). +-ifdef(PROPER). +-compile(export_all). +-export([prop_claim_ensures_unique_nodes/1, prop_wants/0, prop_wants_counts/0, eqc_check/2]). +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-endif. +-endif. -define(DEF_TARGET_N, 4). @@ -723,22 +731,17 @@ has_violations(Diag) -> (Overhang > 0 andalso Overhang < 4). %% hardcoded target n of 4 --ifdef(EQC). - --export([prop_claim_ensures_unique_nodes/1, prop_wants/0, prop_wants_counts/0, eqc_check/2]). --include_lib("eqc/include/eqc.hrl"). --include_lib("eunit/include/eunit.hrl"). - +-ifdef(PROPER). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -define(POW_2(N), trunc(math:pow(2, N))). eqc_check(File, Prop) -> {ok, Bytes} = file:read_file(File), CE = binary_to_term(Bytes), - eqc:check(Prop, CE). + proper:check(Prop, CE). test_nodes(Count) -> [node() | [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(1, Count-1)]]. @@ -747,25 +750,23 @@ test_nodes(Count, StartNode) -> [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(StartNode, StartNode + Count)]. property_claim_ensures_unique_nodes_v2_test_() -> - Prop = eqc:testing_time(30, ?QC_OUT(prop_claim_ensures_unique_nodes(choose_claim_v2))), - {timeout, 120, fun() -> ?assert(eqc:quickcheck(Prop)) end}. + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. property_claim_ensures_unique_nodes_adding_groups_v2_test_() -> - Prop = eqc:testing_time(30, ?QC_OUT( - prop_claim_ensures_unique_nodes_adding_groups(choose_claim_v2))), - {timeout, 120, fun() -> ?assert(eqc:quickcheck(Prop)) end}. + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_groups(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. property_claim_ensures_unique_nodes_adding_singly_v2_test_() -> - Prop = eqc:testing_time(30, ?QC_OUT( - prop_claim_ensures_unique_nodes_adding_singly(choose_claim_v2))), - {timeout, 120, fun() -> ?assert(eqc:quickcheck(Prop)) end}. + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_singly(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. prop_claim_ensures_unique_nodes(ChooseFun) -> %% NOTE: We know that this doesn't work for the case of {_, 3}. %% NOTE2: uses undocumented "double_shrink", is expensive, but should get %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, eqc_gen:double_shrink({choose(4, 9), choose(4, 15)}), + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)},%proper_gen:double_shrink({choose(4, 9), choose(4, 15)}), begin Nval = 3, TNval = Nval + 1, @@ -814,7 +815,7 @@ prop_claim_ensures_unique_nodes_adding_groups(ChooseFun) -> %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations ?FORALL({PartsPow, BaseNodes, AddedNodes}, - eqc_gen:double_shrink({choose(4, 9), choose(2, 10), choose(2, 5)}), + {choose(4, 9), choose(2, 10), choose(2, 5)},%proper_gen:double_shrink({choose(4, 9), choose(2, 10), choose(2, 5)}), begin Nval = 3, TNval = Nval + 1, @@ -871,7 +872,7 @@ prop_claim_ensures_unique_nodes_adding_singly(ChooseFun) -> %% NOTE2: uses undocumented "double_shrink", is expensive, but should get %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, eqc_gen:double_shrink({choose(4, 9), choose(4, 15)}), + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)},%proper:double_shrink({choose(4, 9), choose(4, 15)}), begin Nval = 3, TNval = Nval + 1, @@ -945,18 +946,20 @@ balanced_ring(RingSize, NodeCount, Ring) -> wants_counts_test() -> - ?assert(eqc:quickcheck(?QC_OUT((prop_wants_counts())))). + {timeout, 120, + ?assert(proper:quickcheck(?QC_OUT((prop_wants_counts())), [{numtests, 5000}]))}. prop_wants_counts() -> ?FORALL({S, Q}, {large_pos(100), large_pos(100000)}, begin Wants = wants_counts(S, Q), - conjunction([{len, equals(S, length(Wants))}, - {sum, equals(Q, lists:sum(Wants))}]) + conjunction([{len, S == length(Wants)}, + {sum, Q == lists:sum(Wants)}]) end). wants_test() -> - ?assert(eqc:quickcheck(?QC_OUT((prop_wants())))). + {timeout, 120, + ?_assert(proper:quickcheck(?QC_OUT(prop_wants()), [{numtests, 5000}]))}. prop_wants() -> ?FORALL({NodeStatus, Q}, @@ -994,75 +997,15 @@ prop_wants() -> io:format(user, "ActiveWants: ~p\n", [ActiveWants]), io:format(user, "InactiveWants: ~p\n", [InactiveWants]) end, - conjunction([{wants, equals(length(Wants), length(NodeStatus))}, - {active, equals(Q, ActiveSum)}, - {inactive, equals(0, InactiveSum)}])) + conjunction([{wants, length(Wants) == length(NodeStatus)}, + {active, Q == ActiveSum}, + {inactive, 0 == InactiveSum}])) end). %% Large positive integer between 1 and Max large_pos(Max) -> ?LET(X, largeint(), 1 + (abs(X) rem Max)). -take_idxs_test() -> - ?assert(eqc:quickcheck(?QC_OUT((prop_take_idxs())))). - -prop_take_idxs() -> - ?FORALL({OwnersSeed, CIdxsSeed, ExchangesSeed, TNSeed}, - {non_empty(list(largeint())), % [OwnerSeed] - non_empty(list(largeint())), % [CIdxSeed] - non_empty(list({int(), int()})), % {GiveSeed, TakeSeed} - int()}, % TNSeed - begin - %% Generate Nis - duplicate owners seed to make sure Q > S - S = length(ExchangesSeed), - Dup = roundup(S / length(OwnersSeed)), - Owners = lists:flatten( - lists:duplicate(Dup, - [tnode(abs(OwnerSeed) rem S) || - OwnerSeed <- OwnersSeed])), - Q = length(Owners), - TN = 1+abs(TNSeed), - - - Ownership0 = orddict:from_list([{tnode(I), []} || I <- lists:seq(0, S -1)]), - Ownership = lists:foldl(fun({I, O}, A) -> - orddict:append_list(O, [I], A) - end, - Ownership0, - lists:zip(lists:seq(0, Q-1), Owners)), - NIs = [{Node, undefined, Owned} || {Node, Owned} <- Ownership], - - %% Generate claimable indices - CIdxs = ordsets:from_list([abs(Idx) rem Q || Idx <- CIdxsSeed]), - - %% io:format(user, "ExchangesSeed (~p): ~p\n", [length(ExchangesSeed), - %% ExchangesSeed]), - %% io:format(user, "NIs (~p): ~p\n", [length(NIs), NIs]), - - %% Generate exchanges - Exchanges = [{Node, % node name - abs(GiveSeed) rem (length(OIdxs) + 1), % maximum indices to give - abs(TakeSeed) rem (Q+1), % maximum indices to take - CIdxs} || % indices that can be claimed by node - {{Node, _Want, OIdxs}, {GiveSeed, TakeSeed}} <- - lists:zip(NIs, ExchangesSeed)], - - %% Fire the test - NIs2 = take_idxs(Exchanges, NIs, Q, TN), - - %% Check All nodes are still in NIs - %% Check that no node lost more than it wanted to give - ?WHENFAIL( - begin - io:format(user, "Exchanges:\n~p\n", [Exchanges]), - io:format(user, "NIs:\n~p\n", [NIs]), - io:format(user, "NIs2:\n~p\n", [NIs2]), - io:format(user, "Q: ~p\nTN: ~p\n", [Q, TN]) - end, - check_deltas(Exchanges, NIs, NIs2, Q, TN)) - %% conjunction([{len, equals(length(NIs), length(NIs2))}, - %% {delta, check_deltas(Exchanges, NIs, NIs2, Q, TN)}])) - end). tnode(I) -> list_to_atom("n" ++ integer_to_list(I)). diff --git a/src/riak_core_claim_util.erl b/src/riak_core_claim_util.erl index 22bd143f5..1545770d2 100644 --- a/src/riak_core_claim_util.erl +++ b/src/riak_core_claim_util.erl @@ -60,8 +60,9 @@ substitutions/2]). -ifdef(TEST). --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +%-compile(export_all). -endif. -include_lib("eunit/include/eunit.hrl"). -endif. @@ -633,20 +634,22 @@ substitute(Names, Mapping, L) -> %% ------------------------------------------------------------------- -ifdef(TEST). --ifdef(EQC). +-ifdef(PROPER). property_adjacency_summary_test_() -> - {timeout, 60, ?_test(eqc:quickcheck(eqc:testing_time(30, prop_adjacency_summary())))}. + {timeout,120, + ?_test(proper:quickcheck(prop_adjacency_summary(),[{numtest,5000}]))}. longer_list(K, G) -> - ?SIZED(Size, resize(trunc(K*Size), list(resize(Size, G)))). + ?SIZED(Size, proper_types:resize(trunc(K*Size), list(proper_types:resize(Size, G)))). %% Compare directly constructing the adjacency matrix against %% one using prepend/fixup. prop_adjacency_summary() -> ?FORALL({OwnersSeed, S}, - {non_empty(longer_list(40, largeint())), ?LET(X, int(), 1 + abs(X))}, + {non_empty(longer_list(40, proper_types:largeint())), ?LET(X, proper_types:int(), 1 + abs(X))}, begin + io:format("1\n"), Owners = [list_to_atom("n" ++ integer_to_list(1 + (abs(I) rem S))) || I <- OwnersSeed], AM = adjacency_matrix(Owners), @@ -654,8 +657,7 @@ prop_adjacency_summary() -> {Owners2, _DAM2, FixDAM2} = build(Owners), AS2 = summarize_am(dict:to_list(FixDAM2)), - - + io:format("2\n"), ?WHENFAIL( begin io:format(user, "S=~p\nOwners =~p\n", [S, Owners]), @@ -663,8 +665,9 @@ prop_adjacency_summary() -> io:format(user, "=== FixAM2 ===\n~p\n", [dict:to_list(FixDAM2)]), io:format(user, "=== AS2 ===\n~p\n", [AS2]) end, - conjunction([{owners, equals(Owners, Owners2)}, - {am2, equals(lists:sort(AS), lists:sort(AS2))}])) + proper:conjunction([{owners, Owners == Owners2}, + {am2, lists:sort(AS)== lists:sort(AS2)}])) + end). build(Owners) -> diff --git a/src/riak_core_node_watcher.erl b/src/riak_core_node_watcher.erl index f3ae8237e..c0d673679 100644 --- a/src/riak_core_node_watcher.erl +++ b/src/riak_core_node_watcher.erl @@ -41,7 +41,9 @@ %% TEST API -ifdef(TEST). - +-ifdef(PROPER). +-compile(export_all). +-endif. -export([avsn/0, set_broadcast_module/2]). diff --git a/src/riak_core_ring_util.erl b/src/riak_core_ring_util.erl index c99467314..a0e7ceb22 100644 --- a/src/riak_core_ring_util.erl +++ b/src/riak_core_ring_util.erl @@ -30,13 +30,15 @@ hash_is_partition_boundary/2]). -ifdef(TEST). --ifdef(EQC). +-ifdef(PROPER). + +-compile(export_all). -export([prop_ids_are_boundaries/0, prop_reverse/0, prop_monotonic/0, prop_only_boundaries/0]). --include_lib("eqc/include/eqc.hrl"). +-include_lib("proper/include/proper.hrl"). -endif. -include_lib("eunit/include/eunit.hrl"). -endif. @@ -136,10 +138,10 @@ boundary_test() -> ?assertNot(riak_core_ring_util:hash_is_partition_boundary(<<(BoundaryIndex + 2):160>>, 32)), ?assertNot(riak_core_ring_util:hash_is_partition_boundary(<<(BoundaryIndex + 10):160>>, 32)). --ifdef(EQC). +-ifdef(PROPER). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -define(TEST_TIME_SECS, 5). @@ -154,18 +156,18 @@ ids_are_boundaries_test_() -> test_ids_are_boundaries() -> test_ids_are_boundaries(?TEST_TIME_SECS). - -test_ids_are_boundaries(TestTimeSecs) -> - eqc:quickcheck(eqc:testing_time(TestTimeSecs, ?QC_OUT(prop_ids_are_boundaries()))). +%TODO check time sec +test_ids_are_boundaries(_TestTimeSecs) -> + proper:quickcheck(?QC_OUT(prop_ids_are_boundaries()),[{numtests, 5000}]). reverse_test_() -> {timeout, ?TEST_TIME_SECS+5, [?_assert(test_reverse() =:= true)]}. test_reverse() -> test_reverse(?TEST_TIME_SECS). - -test_reverse(TestTimeSecs) -> - eqc:quickcheck(eqc:testing_time(TestTimeSecs, ?QC_OUT(prop_reverse()))). +%TODO check time sec +test_reverse(_TestTimeSecs) -> + proper:quickcheck(prop_reverse(),[{numtests, 5000}]). monotonic_test_() -> @@ -175,7 +177,7 @@ test_monotonic() -> test_monotonic(?TEST_TIME_SECS). test_monotonic(TestTimeSecs) -> - eqc:quickcheck(eqc:testing_time(TestTimeSecs, ?QC_OUT(prop_monotonic()))). + proper:quickcheck(?QC_OUT(prop_monotonic()),[{numtests, 5000}]). %% `prop_only_boundaries' should run a little longer: not quite as @@ -188,7 +190,7 @@ test_only_boundaries() -> test_only_boundaries(?TEST_TIME_SECS+10). test_only_boundaries(TestTimeSecs) -> - eqc:quickcheck(eqc:testing_time(TestTimeSecs, ?QC_OUT(prop_only_boundaries()))). + proper:quickcheck(prop_only_boundaries(),[{numtests, 5000}]). %% Partition IDs should map to hash values which are partition boundaries prop_ids_are_boundaries() -> diff --git a/src/riak_core_util.erl b/src/riak_core_util.erl index 8090b079d..d9577c613 100644 --- a/src/riak_core_util.erl +++ b/src/riak_core_util.erl @@ -85,14 +85,17 @@ -include("riak_core_vnode.hrl"). -ifdef(TEST). --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --endif. %% EQC +-ifdef(PROPER). + +-include_lib("proper/include/proper.hrl"). +%-compile(export_all). +-endif. -include_lib("eunit/include/eunit.hrl"). -export([counter_loop/1, incr_counter/1, decr_counter/1]). --endif. %% TEST + +-endif. -type riak_core_ring() :: riak_core_ring:riak_core_ring(). -type index() :: non_neg_integer(). @@ -1158,10 +1161,10 @@ proxy_spawn_test() -> ok end. --ifdef(EQC). +-ifdef(PROPER). count_test() -> - ?assert(eqc:quickcheck(prop_count_correct())). + ?assert(proper:quickcheck(prop_count_correct())). prop_count_correct() -> ?FORALL(List, list(bool()), diff --git a/test/eqc/bprops_eqc.erl b/test/eqc/bprops_eqc.erl index 18f8e0bc2..73cc0b323 100644 --- a/test/eqc/bprops_eqc.erl +++ b/test/eqc/bprops_eqc.erl @@ -60,8 +60,8 @@ %% bprops_test_() -> - {timeout, 10000, - ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 10000}])) + {timeout,360, + ?_assert(proper:quickcheck(?QC_OUT(prop_buckets()), [{numtests, 5000}])) }. %% @@ -86,20 +86,9 @@ cover(N) -> cover:analyse_to_file(riak_core_bucket, [html]). %% -%TODO command(State) -> oneof([{call, ?MODULE, set_bucket, set_bucket_args(State)}, {call, ?MODULE, get_bucket, get_bucket_args(State)} - %[{call, ?MODULE,append_bucket_defaults/1}] - %[{call, ?MODULE,get_bucket/2}] ++ - %[{call, ?MODULE,reset_bucket/1}] ++ - %[{call, ?MODULE,get_buckets/1}] ++ - %[{call, ?MODULE,bucket_nval_map/1}] ++ - %[{call, ?MODULE,default_object_navl,[]}] ++ - %[{call, ?MODULE,merge_props/2}] ++ - %[{call, ?MODULE,name/1}] ++ - %[{call, ?MODULE,nval/1}] ++ - %[{call, ?MODULE,get_vault/2}] ]). %% @@ -142,7 +131,7 @@ next_state(#state{buckets=Buckets} = S,_Res,{call,?MODULE, set_bucket, [Bucket, Buckets ) }; -next_state(S,_Res,{call,?MODULE, get_bucket, [Bucket]}) -> +next_state(S,_Res,{call,?MODULE, get_bucket, [_Bucket]}) -> S. -spec expected_properties(bucket_name(), orddict(), orddict()) -> orddict(). @@ -165,7 +154,7 @@ get_bucket(Bucket) -> precondition(_S, {call, ?MODULE, _,_})-> true. %get_bucket_post(#state{buckets=Buckets}, [Bucket], Res) -postcondition(#state{buckets=Buckets},{call,?MODULE,get_bucket, [Bucket]}, Res) -> +postcondition(#state{buckets=Buckets},{call, ?MODULE, get_bucket, [Bucket]}, Res) -> BPropsFind = orddict:find(Bucket, Buckets), case {Res, BPropsFind} of {error, _} -> @@ -197,28 +186,6 @@ postcondition(#state{buckets=Buckets},{call,?MODULE, set_bucket, [Bucket, _BProp false end. -%% -%% all_n command %TODO -%% - -% all_n_args(_) -> []. - -% all_n() -> -% {ok, Ring} = riak_core_ring_manager:get_my_ring(), -% riak_core_bucket:all_n(Ring). - -% all_n_post(#state{buckets=Buckets}, [], Res) -> -% %postcondition(#state{buckets=Buckets}, {call,?MODULE,_,[]}, Res) -> -% AllNVals = orddict:fold( -% fun(_Bucket, BProps, Accum) -> -% {ok, NVal} = orddict:find(n_val, BProps), -% [NVal | Accum] -% end, -% [], -% Buckets -% ) ++ [proplists:get_value(n_val, ?DEFAULT_BPROPS)], -% equals(ordsets:from_list(Res), ordsets:from_list(AllNVals)). - %% TODO Add more commands here @@ -259,19 +226,15 @@ prop_buckets() -> aggregate(command_names(Cmds), ?TRAPEXIT( begin - {H, S, Res} = + {_H, _S, Res} = bucket_eqc_utils:per_test_setup(?DEFAULT_BPROPS, fun() -> run_commands(?MODULE, Cmds) end), - % pretty_commands( - % ?MODULE, Cmds, - % {H, S, Res}, aggregate( command_names(Cmds), Res == ok ) - % ) end ) ) diff --git a/test/eqc/node_watcher_qc.erl b/test/eqc/node_watcher_qc.erl index 30fd2ee23..8efd6e930 100644 --- a/test/eqc/node_watcher_qc.erl +++ b/test/eqc/node_watcher_qc.erl @@ -39,10 +39,10 @@ -define(ORDSET(L), ordsets:from_list(L)). -qc_test_() -> - {timeout, 10000, - ?_assert(proper:quickcheck(prop_main(),[{numtests, 10000}])) - }. +% qc_test_() -> +% {timeout, 1500, +% ?_assert(proper:quickcheck(?QC_OUT(prop_main()),[{numtests, 5000}])) +% }. prop_main() -> ?SETUP( @@ -115,8 +115,8 @@ command(S) -> {call, ?MODULE, local_service_up, [g_service()]}, {call, ?MODULE, local_service_down, [g_service()]}, {call, ?MODULE, local_service_kill, [g_service(), S]}, - %{call, ?MODULE, local_node_up, []}, - %{call, ?MODULE, local_node_down, []}, + {call, ?MODULE, local_node_up, []}, + {call, ?MODULE, local_node_down, []}, {call, ?MODULE, remote_service_up, [g_node(), g_services()]}, {call, ?MODULE, remote_service_down, [g_node()]}, {call, ?MODULE, remote_service_down_disterl, [g_node()]}, diff --git a/test/eqc/riak_core_claim_statem.erl b/test/eqc/riak_core_claim_statem.erl index f0347d82f..c3457671b 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/eqc/riak_core_claim_statem.erl @@ -17,8 +17,8 @@ %Entry Eunit claim_test_()-> - {timeout, 10000, - ?_assert(proper:quickcheck(prop_claim(with_ring_size(5)),[{numtests, 10000}] ))}. + {timeout, 120, + ?_assert(proper:quickcheck(prop_claim(with_ring_size(5)),[{numtests, 5000}] ))}. %% -- State ------------------------------------------------------------------ -record(state, @@ -50,8 +50,8 @@ starting(S) -> planning(S) -> [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, - {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}%, - %{claiming, {call, ?MODULE, claim, claim_args(S)}} %TODO + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}, + {claiming, {call, ?MODULE, claim, claim_args(S)}} %TODO ]. claiming(S) -> @@ -84,7 +84,7 @@ precondition(_F,_T,_S,{call,_,leave_node,_}) -> % @doc claim_pre/3 - Precondition for generation %-spec claim_pre(_From, _To, S :: proper:symbolic_state()) -> boolean(). %claim_pre(_From, _To, #state{ring=undefined}) -> - precondition(_F,_T,#state{ring=undefined},{call, _, claim,_}) -> +precondition(_F,_T,#state{ring=undefined},{call, _, claim,_}) -> false; %claim_pre(_From, _To, _S) -> precondition(_F,_T,_S,{call,_ , claim, _}) -> diff --git a/test/eqc/vclock_qc.erl b/test/eqc/vclock_qc.erl index 2c6605586..7a6918803 100644 --- a/test/eqc/vclock_qc.erl +++ b/test/eqc/vclock_qc.erl @@ -15,8 +15,8 @@ proper_test_() -> {timeout, - 10000, - ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 10000}]))}. + 120, + ?_assert(proper:quickcheck(prop_vclock(), [{numtests, 5000}]))}. test() -> proper:quickcheck(more_commands(10, prop_vclock())). @@ -95,12 +95,11 @@ prop_vclock() -> ?FORALL(Cmds,commands(?MODULE), begin put(timestamp, 1), - {H,S,Res} = run_commands(?MODULE, Cmds), + {_H,S,Res} = run_commands(?MODULE, Cmds), aggregate([ length(V) || {_,V} <- S#state.vclocks], aggregate(command_names(Cmds), collect({num_vclocks_div_10, length(S#state.vclocks) div 10}, - %pretty_commands(?MODULE, Cmds, {H,S,Res}, - Res == ok%) %maybe not supported in proper + Res == ok ))) end). From 15a3202ff3b0b0bfda9f88f1e1637011f4d7be24 Mon Sep 17 00:00:00 2001 From: woelki Date: Tue, 7 Jul 2020 13:58:25 +0200 Subject: [PATCH 09/11] Adaption to lint --- src/basho_stats_histogram.erl | 5 +++-- src/basho_stats_sample.erl | 2 +- src/riak_core_claim.erl | 8 ++++---- src/riak_core_claim_util.erl | 8 ++++---- src/riak_core_ring_util.erl | 8 ++++---- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/basho_stats_histogram.erl b/src/basho_stats_histogram.erl index bd2bb8019..1c97f56a1 100644 --- a/src/basho_stats_histogram.erl +++ b/src/basho_stats_histogram.erl @@ -192,7 +192,7 @@ simple_test() -> % ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", % [Min, Max, Bins+1])), % io:format("Min ~p, Max ~p, Bins ~p, Xs ~w~n", -% [Min, Max, Bins, Xs]), +% [Min, Max, Bins, Xs]), % case LCounts == RCounts of % true -> % true; @@ -209,7 +209,8 @@ simple_test() -> % ?WHENFAIL( % begin % io:format("Min ~p, Max ~p, Bins ~p, Xs ~w~n", [Min, Max, Bins, Xs]), -% Command = ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", [Min, Max, Bins+1]), +% Command = ?FMT("hist(x, seq(~w,~w,length.out=~w), plot=FALSE)$counts", +% [Min, Max, Bins+1]), % InputStr = [integer_to_list(I) || I <- Xs], % io:format(?FMT("x <- c(~s)\n", [string:join(InputStr, ",")])), % io:format(?FMT("write(~s, ncolumns=1,file=stdout())\n", [Command])) diff --git a/src/basho_stats_sample.erl b/src/basho_stats_sample.erl index e9ea322b3..df7a66d1d 100644 --- a/src/basho_stats_sample.erl +++ b/src/basho_stats_sample.erl @@ -149,7 +149,7 @@ empty_test() -> % tuple_to_list(summary(update_all(Xs, new())))))))). % qc_test() -> -% %{timeout, 5000, +% %{timeout, 5000, % true = proper:quickcheck(prop_main()).%}. -endif. diff --git a/src/riak_core_claim.erl b/src/riak_core_claim.erl index 48e5951aa..1c40447fe 100644 --- a/src/riak_core_claim.erl +++ b/src/riak_core_claim.erl @@ -766,8 +766,8 @@ prop_claim_ensures_unique_nodes(ChooseFun) -> %% NOTE2: uses undocumented "double_shrink", is expensive, but should get %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)},%proper_gen:double_shrink({choose(4, 9), choose(4, 15)}), - begin + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, + begin Nval = 3, TNval = Nval + 1, _Params = [{target_n_val, TNval}], @@ -815,7 +815,7 @@ prop_claim_ensures_unique_nodes_adding_groups(ChooseFun) -> %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations ?FORALL({PartsPow, BaseNodes, AddedNodes}, - {choose(4, 9), choose(2, 10), choose(2, 5)},%proper_gen:double_shrink({choose(4, 9), choose(2, 10), choose(2, 5)}), + {choose(4, 9), choose(2, 10), choose(2, 5)}, begin Nval = 3, TNval = Nval + 1, @@ -872,7 +872,7 @@ prop_claim_ensures_unique_nodes_adding_singly(ChooseFun) -> %% NOTE2: uses undocumented "double_shrink", is expensive, but should get %% around those case where we shrink to a non-minimal case because %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)},%proper:double_shrink({choose(4, 9), choose(4, 15)}), + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, begin Nval = 3, TNval = Nval + 1, diff --git a/src/riak_core_claim_util.erl b/src/riak_core_claim_util.erl index 1545770d2..67c5b8a13 100644 --- a/src/riak_core_claim_util.erl +++ b/src/riak_core_claim_util.erl @@ -637,8 +637,8 @@ substitute(Names, Mapping, L) -> -ifdef(PROPER). property_adjacency_summary_test_() -> - {timeout,120, - ?_test(proper:quickcheck(prop_adjacency_summary(),[{numtest,5000}]))}. + {timeout, 120, + ?_test(proper:quickcheck(prop_adjacency_summary(), [{numtest, 5000}]))}. longer_list(K, G) -> ?SIZED(Size, proper_types:resize(trunc(K*Size), list(proper_types:resize(Size, G)))). @@ -647,7 +647,8 @@ longer_list(K, G) -> %% one using prepend/fixup. prop_adjacency_summary() -> ?FORALL({OwnersSeed, S}, - {non_empty(longer_list(40, proper_types:largeint())), ?LET(X, proper_types:int(), 1 + abs(X))}, + {non_empty(longer_list(40, proper_types:largeint())), + ?LET(X, proper_types:int(), 1 + abs(X))}, begin io:format("1\n"), Owners = [list_to_atom("n" ++ integer_to_list(1 + (abs(I) rem S))) @@ -667,7 +668,6 @@ prop_adjacency_summary() -> end, proper:conjunction([{owners, Owners == Owners2}, {am2, lists:sort(AS)== lists:sort(AS2)}])) - end). build(Owners) -> diff --git a/src/riak_core_ring_util.erl b/src/riak_core_ring_util.erl index a0e7ceb22..af18edb38 100644 --- a/src/riak_core_ring_util.erl +++ b/src/riak_core_ring_util.erl @@ -158,7 +158,7 @@ test_ids_are_boundaries() -> test_ids_are_boundaries(?TEST_TIME_SECS). %TODO check time sec test_ids_are_boundaries(_TestTimeSecs) -> - proper:quickcheck(?QC_OUT(prop_ids_are_boundaries()),[{numtests, 5000}]). + proper:quickcheck(?QC_OUT(prop_ids_are_boundaries()), [{numtests, 5000}]). reverse_test_() -> {timeout, ?TEST_TIME_SECS+5, [?_assert(test_reverse() =:= true)]}. @@ -167,7 +167,7 @@ test_reverse() -> test_reverse(?TEST_TIME_SECS). %TODO check time sec test_reverse(_TestTimeSecs) -> - proper:quickcheck(prop_reverse(),[{numtests, 5000}]). + proper:quickcheck(prop_reverse(), [{numtests, 5000}]). monotonic_test_() -> @@ -177,7 +177,7 @@ test_monotonic() -> test_monotonic(?TEST_TIME_SECS). test_monotonic(TestTimeSecs) -> - proper:quickcheck(?QC_OUT(prop_monotonic()),[{numtests, 5000}]). + proper:quickcheck(?QC_OUT(prop_monotonic()), [{numtests, 5000}]). %% `prop_only_boundaries' should run a little longer: not quite as @@ -190,7 +190,7 @@ test_only_boundaries() -> test_only_boundaries(?TEST_TIME_SECS+10). test_only_boundaries(TestTimeSecs) -> - proper:quickcheck(prop_only_boundaries(),[{numtests, 5000}]). + proper:quickcheck(prop_only_boundaries(), [{numtests, 5000}]). %% Partition IDs should map to hash values which are partition boundaries prop_ids_are_boundaries() -> From dd450f53306075573c3d42abd3a56354e1936479 Mon Sep 17 00:00:00 2001 From: woelki Date: Wed, 8 Jul 2020 18:23:14 +0200 Subject: [PATCH 10/11] restructuring of the proper tests --- .travis.yml | 1 - eqc/bucket_eqc_utils.erl | 47 --- eqc/mock_vnode.erl | 196 ----------- src/riak_core_claim.erl | 327 +----------------- src/riak_core_claim_util.erl | 58 +--- src/riak_core_ring_util.erl | 141 -------- test/eqc/13node_12node_ring.eqc | Bin 6804 -> 0 bytes test/eqc/169_group_join.eqc | Bin 40 -> 0 bytes test/eqc/648_unbalanced_singly.eqc | Bin 38 -> 0 bytes ...claim-statem-leaving-nodes-still-claim.eqc | Bin 6370 -> 0 bytes test/eqc/claim_32_5_unbalanced.eqc | Bin 38 -> 0 bytes test/{eqc => pqc}/bprops_eqc.erl | 0 test/{eqc => pqc}/bucket_eqc_utils.erl | 0 {eqc => test/pqc}/chash_eqc.erl | 20 +- {eqc => test/pqc}/core_vnode_eqc.erl | 19 +- .../pqc}/new_cluster_membership_model_eqc.erl | 40 +-- test/{eqc => pqc}/node_watcher_qc.erl | 0 test/pqc/riak_core_claim_qc.erl | 323 +++++++++++++++++ test/{eqc => pqc}/riak_core_claim_statem.erl | 6 +- test/pqc/riak_core_claim_util_qc.erl | 50 +++ {eqc => test/pqc}/riak_core_ring_eqc.erl | 8 +- test/pqc/riak_core_ring_util_qc.erl | 141 ++++++++ test/{eqc => pqc}/vclock_qc.erl | 0 test/{eqc => pqc}/worker_pool_pulse.erl | 0 24 files changed, 563 insertions(+), 814 deletions(-) delete mode 100644 eqc/bucket_eqc_utils.erl delete mode 100644 eqc/mock_vnode.erl delete mode 100644 test/eqc/13node_12node_ring.eqc delete mode 100644 test/eqc/169_group_join.eqc delete mode 100644 test/eqc/648_unbalanced_singly.eqc delete mode 100644 test/eqc/claim-statem-leaving-nodes-still-claim.eqc delete mode 100644 test/eqc/claim_32_5_unbalanced.eqc rename test/{eqc => pqc}/bprops_eqc.erl (100%) rename test/{eqc => pqc}/bucket_eqc_utils.erl (100%) rename {eqc => test/pqc}/chash_eqc.erl (90%) rename {eqc => test/pqc}/core_vnode_eqc.erl (97%) rename {eqc => test/pqc}/new_cluster_membership_model_eqc.erl (98%) rename test/{eqc => pqc}/node_watcher_qc.erl (100%) create mode 100644 test/pqc/riak_core_claim_qc.erl rename test/{eqc => pqc}/riak_core_claim_statem.erl (99%) create mode 100644 test/pqc/riak_core_claim_util_qc.erl rename {eqc => test/pqc}/riak_core_ring_eqc.erl (95%) create mode 100644 test/pqc/riak_core_ring_util_qc.erl rename test/{eqc => pqc}/vclock_qc.erl (100%) rename test/{eqc => pqc}/worker_pool_pulse.erl (100%) diff --git a/.travis.yml b/.travis.yml index 8803584ba..0dea3a613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ otp_release: install: - make - ./rebar3 update - - travis_wait 30 mvn install script: - make test - make proper diff --git a/eqc/bucket_eqc_utils.erl b/eqc/bucket_eqc_utils.erl deleted file mode 100644 index d5959fe60..000000000 --- a/eqc/bucket_eqc_utils.erl +++ /dev/null @@ -1,47 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% Copyright (c) 2007-2016 Basho Technologies, Inc. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - --module(bucket_eqc_utils). - -%% API --export([per_test_setup/2]). - - -per_test_setup(DefaultBucketProps, TestFun) -> - try - riak_core_test_util:stop_pid(whereis(riak_core_ring_events)), - riak_core_test_util:stop_pid(whereis(riak_core_ring_manager)), - application:set_env(riak_core, claimant_tick, 4294967295), - application:set_env(riak_core, cluster_name, "eqc_test"), - application:set_env(riak_core, default_bucket_props, DefaultBucketProps), - {ok, RingEvents} = riak_core_ring_events:start_link(), - {ok, RingMgr} = riak_core_ring_manager:start_link(test), - {ok, Claimant} = riak_core_claimant:start_link(), - - Results = TestFun(), - - riak_core_test_util:stop_pid(Claimant), - unlink(RingMgr), - riak_core_ring_manager:stop(), - riak_core_test_util:stop_pid(RingEvents), - Results - after - meck:unload() - end. diff --git a/eqc/mock_vnode.erl b/eqc/mock_vnode.erl deleted file mode 100644 index 44dbe6a87..000000000 --- a/eqc/mock_vnode.erl +++ /dev/null @@ -1,196 +0,0 @@ -%% ------------------------------------------------------------------- -%% -%% mock_vnode: mock vnode for unit testing -%% -%% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved. -%% -%% This file is provided to you under the Apache License, -%% Version 2.0 (the "License"); you may not use this file -%% except in compliance with the License. You may obtain -%% a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, -%% software distributed under the License is distributed on an -%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -%% KIND, either express or implied. See the License for the -%% specific language governing permissions and limitations -%% under the License. -%% -%% ------------------------------------------------------------------- - -%% @doc mock vnode for unit testing - --module(mock_vnode). -%TODO: Work out why this gives a warning -%-behavior(riak_core_vnode). --export([start_vnode/1, - get_index/1, - get_counter/1, - neverreply/1, - returnreply/1, - latereply/1, - asyncnoreply/2, - asyncreply/2, - asynccrash/2, - crash/1, - spawn_error/2, - sync_error/2, - get_crash_reason/1, - stop/1]). --export([init/1, - handle_command/3, - terminate/2, - handle_exit/3]). --export([init_worker/3, - handle_work/3]). --behavior(riak_core_vnode_worker). - --record(state, {index, counter, crash_reason}). --record(wstate, {index, args, props, counter, crash_reason}). - --define(MASTER, mock_vnode_master). - -%% API -start_vnode(I) -> - riak_core_vnode_master:start_vnode(I, ?MODULE). - -get_index(Preflist) -> - riak_core_vnode_master:sync_command(Preflist, get_index, ?MASTER). - -get_counter(Preflist) -> - riak_core_vnode_master:sync_command(Preflist, get_counter, ?MASTER). - -neverreply(Preflist) -> - riak_core_vnode_master:command(Preflist, neverreply, ?MASTER). - -returnreply(Preflist) -> - Ref = {neverreply, make_ref()}, - riak_core_vnode_master:command(Preflist, returnreply, {raw, Ref, self()}, ?MASTER), - {ok, Ref}. - -latereply(Preflist) -> - Ref = {latereply, make_ref()}, - riak_core_vnode_master:command(Preflist, latereply, {raw, Ref, self()}, ?MASTER), - {ok, Ref}. - -asyncnoreply(Preflist, AsyncDonePid) -> - Ref = {asyncnoreply, make_ref()}, - riak_core_vnode_master:command(Preflist, {asyncnoreply, AsyncDonePid}, - {raw, Ref, AsyncDonePid}, ?MASTER), - {ok, Ref}. - -asyncreply(Preflist, AsyncDonePid) -> - Ref = {asyncreply, make_ref()}, - riak_core_vnode_master:command(Preflist, {asyncreply, AsyncDonePid}, - {raw, Ref, AsyncDonePid}, ?MASTER), - {ok, Ref}. - -asynccrash(Preflist, AsyncDonePid) -> - Ref = {asynccrash, make_ref()}, - riak_core_vnode_master:command(Preflist, {asynccrash, AsyncDonePid}, - {raw, Ref, AsyncDonePid}, ?MASTER), - {ok, Ref}. - -crash(Preflist) -> - riak_core_vnode_master:sync_command(Preflist, crash, ?MASTER). - -spawn_error(Preflist, Cmd) -> - riak_core_vnode_master:sync_spawn_command(Preflist, {sync_error, Cmd}, ?MASTER). - -sync_error(Preflist, Cmd) -> - riak_core_vnode_master:sync_command(Preflist, {sync_error, Cmd}, ?MASTER). - -get_crash_reason(Preflist) -> - riak_core_vnode_master:sync_command(Preflist, get_crash_reason, ?MASTER). - -stop(Preflist) -> - riak_core_vnode_master:sync_command(Preflist, stop, ?MASTER). - - -%% Callbacks - -init([Index]) -> - S = #state{index=Index,counter=0}, - {ok, PoolSize} = application:get_env(riak_core, core_vnode_eqc_pool_size), - case PoolSize of - PoolSize when PoolSize > 0 -> - {ok, S, [{pool, ?MODULE, PoolSize, myargs}]}; - _ -> - {ok, S} - end. - -handle_command(get_index, _Sender, State) -> - {reply, {ok, State#state.index}, State}; -handle_command(get_counter, _Sender, State) -> - {reply, {ok, State#state.counter}, State}; -handle_command(get_crash_reason, _Sender, State) -> - {reply, {ok, State#state.crash_reason}, State}; -handle_command({sync_error, error}, _Sender, State) -> - erlang:error(core_breach), - {reply, ok, State}; -handle_command({sync_error, exit}, _Sender, State) -> - erlang:exit(core_breach), - {reply, ok, State}; -handle_command({sync_error, badthrow}, _Sender, State) -> - erlang:throw({reply, {error, terrible}, State}); %% emulate gen_server -handle_command({sync_error, goodthrow}, _Sender, State) -> - erlang:throw({reply, ok, State}); %% emulate gen_server - -handle_command(crash, _Sender, State) -> - spawn_link(fun() -> exit(State#state.index) end), - {reply, ok, State}; -handle_command(stop, Sender, State = #state{counter=Counter}) -> - %% Send reply here as vnode_master:sync_command does a call - %% which is cast on to the vnode process. Returning {stop,...} - %% does not provide for returning a response. - riak_core_vnode:reply(Sender, stopped), - {stop, normal, State#state{counter = Counter + 1}}; -handle_command(neverreply, _Sender, State = #state{counter=Counter}) -> - {noreply, State#state{counter = Counter + 1}}; -handle_command(returnreply, _Sender, State = #state{counter=Counter}) -> - {reply, returnreply, State#state{counter = Counter + 1}}; -handle_command(latereply, Sender, State = #state{counter=Counter}) -> - spawn(fun() -> - timer:sleep(100), - riak_core_vnode:reply(Sender, latereply) - end), - {noreply, State#state{counter = Counter + 1}}; -handle_command({asyncnoreply, DonePid}, Sender, State = #state{counter=Counter}) -> - {async, {noreply, DonePid}, Sender, State#state{counter = Counter + 1}}; -handle_command({asyncreply, DonePid}, Sender, State = #state{counter=Counter}) -> - {async, {reply, DonePid}, Sender, State#state{counter = Counter + 1}}; -handle_command({asynccrash, DonePid}, Sender, State = #state{counter=Counter}) -> - {async, {crash, DonePid}, Sender, State#state{counter = Counter + 1}}. - -handle_exit(_Pid, Reason, State) -> - {noreply, State#state{crash_reason=Reason}}. - -terminate(_Reason, _State) -> - ok. - - - -%% -%% Vnode worker callbacks -%% -init_worker(Index, Args, Props) -> - {ok, #wstate{index=Index, args=Args, props=Props}}. - -handle_work({noreply, DonePid}, {raw, Ref, _EqcPid} = _Sender, State = #wstate{index=I}) -> - timer:sleep(100), % slow things down enough to cause issue on stops - DonePid ! {I, {ok, Ref}}, - {noreply, State}; -handle_work({reply, DonePid}, {raw, Ref, _EqcPid} = _Sender, State = #wstate{index=I}) -> - timer:sleep(100), % slow things down enough to cause issue on stops - DonePid ! {I, {ok, Ref}}, - {reply, asyncreply, State}; -handle_work({crash, DonePid}, - {raw, Ref, _EqcPid} = _Sender, _State = #wstate{index=I}) -> - timer:sleep(100), % slow things down enough to cause issue on stops - %% This msg needs to get sent, since it's counted in core_vnode_eqc work tracking - %% in next_state_data/5 - DonePid ! {I, {ok, Ref}}, - throw(deliberate_async_crash). - diff --git a/src/riak_core_claim.erl b/src/riak_core_claim.erl index 1c40447fe..a77d3faf6 100644 --- a/src/riak_core_claim.erl +++ b/src/riak_core_claim.erl @@ -81,14 +81,7 @@ meets_target_n/2, diagonal_stripe/2]). --ifdef(TEST). --ifdef(PROPER). --compile(export_all). --export([prop_claim_ensures_unique_nodes/1, prop_wants/0, prop_wants_counts/0, eqc_check/2]). --include_lib("proper/include/proper.hrl"). --include_lib("eunit/include/eunit.hrl"). --endif. --endif. + -define(DEF_TARGET_N, 4). @@ -730,320 +723,4 @@ has_violations(Diag) -> Overhang = RS rem NC, (Overhang > 0 andalso Overhang < 4). %% hardcoded target n of 4 - --ifdef(PROPER). - --define(QC_OUT(P), - proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). - --define(POW_2(N), trunc(math:pow(2, N))). - -eqc_check(File, Prop) -> - {ok, Bytes} = file:read_file(File), - CE = binary_to_term(Bytes), - proper:check(Prop, CE). - -test_nodes(Count) -> - [node() | [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(1, Count-1)]]. - -test_nodes(Count, StartNode) -> - [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(StartNode, StartNode + Count)]. - -property_claim_ensures_unique_nodes_v2_test_() -> - Prop = ?QC_OUT(prop_claim_ensures_unique_nodes(choose_claim_v2)), - {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. - -property_claim_ensures_unique_nodes_adding_groups_v2_test_() -> - Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_groups(choose_claim_v2)), - {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. - -property_claim_ensures_unique_nodes_adding_singly_v2_test_() -> - Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_singly(choose_claim_v2)), - {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. - -prop_claim_ensures_unique_nodes(ChooseFun) -> - %% NOTE: We know that this doesn't work for the case of {_, 3}. - %% NOTE2: uses undocumented "double_shrink", is expensive, but should get - %% around those case where we shrink to a non-minimal case because - %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, - begin - Nval = 3, - TNval = Nval + 1, - _Params = [{target_n_val, TNval}], - - Partitions = ?POW_2(PartsPow), - [Node0 | RestNodes] = test_nodes(NodeCount), - - R0 = riak_core_ring:fresh(Partitions, Node0), - RAdded = lists:foldl(fun(Node, Racc) -> - riak_core_ring:add_member(Node0, Racc, Node) - end, R0, RestNodes), - - Rfinal = claim(RAdded, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), - - Preflists = riak_core_ring:all_preflists(Rfinal, Nval), - ImperfectPLs = orddict:to_list( - lists:foldl(fun(PL, Acc) -> - PLNodes = lists:usort([N || {_, N} <- PL]), - case length(PLNodes) of - Nval -> - Acc; - _ -> - ordsets:add_element(PL, Acc) - end - end, [], Preflists)), - - ?WHENFAIL( - begin - io:format(user, "{Partitions, Nodes} {~p, ~p}~n", - [Partitions, NodeCount]), - io:format(user, "Owners: ~p~n", - [riak_core_ring:all_owners(Rfinal)]) - end, - conjunction([{meets_target_n, - equals({true, []}, - meets_target_n(Rfinal, TNval))}, - {perfect_preflists, equals([], ImperfectPLs)}, - {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) - end). - - -prop_claim_ensures_unique_nodes_adding_groups(ChooseFun) -> - %% NOTE: We know that this doesn't work for the case of {_, 3}. - %% NOTE2: uses undocumented "double_shrink", is expensive, but should get - %% around those case where we shrink to a non-minimal case because - %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, BaseNodes, AddedNodes}, - {choose(4, 9), choose(2, 10), choose(2, 5)}, - begin - Nval = 3, - TNval = Nval + 1, - _Params = [{target_n_val, TNval}], - - Partitions = ?POW_2(PartsPow), - [Node0 | RestNodes] = test_nodes(BaseNodes), - AddNodes = test_nodes(AddedNodes-1, BaseNodes), - NodeCount = BaseNodes + AddedNodes, - %% io:format("Base: ~p~n",[[Node0 | RestNodes]]), - %% io:format("Added: ~p~n",[AddNodes]), - - R0 = riak_core_ring:fresh(Partitions, Node0), - RBase = lists:foldl(fun(Node, Racc) -> - riak_core_ring:add_member(Node0, Racc, Node) - end, R0, RestNodes), - - Rinterim = claim(RBase, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), - RAdded = lists:foldl(fun(Node, Racc) -> - riak_core_ring:add_member(Node0, Racc, Node) - end, Rinterim, AddNodes), - - Rfinal = claim(RAdded, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), - - Preflists = riak_core_ring:all_preflists(Rfinal, Nval), - ImperfectPLs = orddict:to_list( - lists:foldl(fun(PL, Acc) -> - PLNodes = lists:usort([N || {_, N} <- PL]), - case length(PLNodes) of - Nval -> - Acc; - _ -> - ordsets:add_element(PL, Acc) - end - end, [], Preflists)), - - ?WHENFAIL( - begin - io:format(user, "{Partitions, Nodes} {~p, ~p}~n", - [Partitions, NodeCount]), - io:format(user, "Owners: ~p~n", - [riak_core_ring:all_owners(Rfinal)]) - end, - conjunction([{meets_target_n, - equals({true, []}, - meets_target_n(Rfinal, TNval))}, - {perfect_preflists, equals([], ImperfectPLs)}, - {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) - end). - - -prop_claim_ensures_unique_nodes_adding_singly(ChooseFun) -> - %% NOTE: We know that this doesn't work for the case of {_, 3}. - %% NOTE2: uses undocumented "double_shrink", is expensive, but should get - %% around those case where we shrink to a non-minimal case because - %% some intermediate combinations of ring_size/node have no violations - ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, - begin - Nval = 3, - TNval = Nval + 1, - Params = [{target_n_val, TNval}], - - Partitions = ?POW_2(PartsPow), - [Node0 | RestNodes] = test_nodes(NodeCount), - - R0 = riak_core_ring:fresh(Partitions, Node0), - Rfinal = lists:foldl(fun(Node, Racc) -> - Racc0 = riak_core_ring:add_member(Node0, Racc, Node), - %% TODO which is it? Claim or ChooseFun?? - %%claim(Racc0, {?MODULE, wants_claim_v2}, - %% {?MODULE, ChooseFun}) - ?MODULE:ChooseFun(Racc0, Node, Params) - end, R0, RestNodes), - Preflists = riak_core_ring:all_preflists(Rfinal, Nval), - ImperfectPLs = orddict:to_list( - lists:foldl(fun(PL, Acc) -> - PLNodes = lists:usort([N || {_, N} <- PL]), - case length(PLNodes) of - Nval -> - Acc; - _ -> - ordsets:add_element(PL, Acc) - end - end, [], Preflists)), - - ?WHENFAIL( - begin - io:format(user, "{Partitions, Nodes} {~p, ~p}~n", - [Partitions, NodeCount]), - io:format(user, "Owners: ~p~n", - [riak_core_ring:all_owners(Rfinal)]) - end, - conjunction([{meets_target_n, - equals({true, []}, - meets_target_n(Rfinal, TNval))}, - {perfect_preflists, equals([], ImperfectPLs)}, - {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) - end). - - - -%% @private check that no node claims more than it should --spec balanced_ring(RingSize::integer(), NodeCount::integer(), - riak_core_ring:riak_core_ring()) -> - boolean(). -balanced_ring(RingSize, NodeCount, Ring) -> - TargetClaim = ceiling(RingSize / NodeCount), - MinClaim = RingSize div NodeCount, - AllOwners0 = riak_core_ring:all_owners(Ring), - AllOwners = lists:keysort(2, AllOwners0), - {BalancedMax, AccFinal} = lists:foldl(fun({_Part, Node}, {_Balanced, [{Node, Cnt} | Acc]}) - when Cnt >= TargetClaim -> - {false, [{Node, Cnt+1} | Acc]}; - ({_Part, Node}, {Balanced, [{Node, Cnt} | Acc]}) -> - {Balanced, [{Node, Cnt+1} | Acc]}; - ({_Part, NewNode}, {Balanced, Acc}) -> - {Balanced, [{NewNode, 1} | Acc]} - end, - {true, []}, - AllOwners), - BalancedMin = lists:all(fun({_Node, Cnt}) -> Cnt >= MinClaim end, AccFinal), - case BalancedMax andalso BalancedMin of - true -> - true; - false -> - {TargetClaim, MinClaim, lists:sort(AccFinal)} - end. - - -wants_counts_test() -> - {timeout, 120, - ?assert(proper:quickcheck(?QC_OUT((prop_wants_counts())), [{numtests, 5000}]))}. - -prop_wants_counts() -> - ?FORALL({S, Q}, {large_pos(100), large_pos(100000)}, - begin - Wants = wants_counts(S, Q), - conjunction([{len, S == length(Wants)}, - {sum, Q == lists:sum(Wants)}]) - end). - -wants_test() -> - {timeout, 120, - ?_assert(proper:quickcheck(?QC_OUT(prop_wants()), [{numtests, 5000}]))}. - -prop_wants() -> - ?FORALL({NodeStatus, Q}, - {?SUCHTHAT(L, non_empty(list(elements([leaving, joining]))), - lists:member(joining, L)), - ?LET(X, choose(1, 16), trunc(math:pow(2, X)))}, - begin - R0 = riak_core_ring:fresh(Q, tnode(1)), - {_, R2, Active} = - lists:foldl( - fun(S, {I, R1, A1}) -> - N = tnode(I), - case S of - joining -> - {I+1, riak_core_ring:add_member(N, R1, N), [N|A1]}; - _ -> - {I+1, riak_core_ring:leave_member(N, R1, N), A1} - end - end, {1, R0, []}, NodeStatus), - Wants = wants(R2), - - %% Check any non-claiming nodes are set to 0 - %% Check all nodes are present - {ActiveWants, InactiveWants} = - lists:partition(fun({N, _W}) -> lists:member(N, Active) end, Wants), - - ActiveSum = lists:sum([W || {_, W} <- ActiveWants]), - InactiveSum = lists:sum([W || {_, W} <- InactiveWants]), - ?WHENFAIL( - begin - io:format(user, "NodeStatus: ~p\n", [NodeStatus]), - io:format(user, "Active: ~p\n", [Active]), - io:format(user, "Q: ~p\n", [Q]), - io:format(user, "Wants: ~p\n", [Wants]), - io:format(user, "ActiveWants: ~p\n", [ActiveWants]), - io:format(user, "InactiveWants: ~p\n", [InactiveWants]) - end, - conjunction([{wants, length(Wants) == length(NodeStatus)}, - {active, Q == ActiveSum}, - {inactive, 0 == InactiveSum}])) - end). - -%% Large positive integer between 1 and Max -large_pos(Max) -> - ?LET(X, largeint(), 1 + (abs(X) rem Max)). - - -tnode(I) -> - list_to_atom("n" ++ integer_to_list(I)). - -%% Check that no node gained more than it wanted to take -%% Check that none of the nodes took more partitions than allowed -%% Check that no nodes violate target N -check_deltas(Exchanges, Before, After, Q, TN) -> - conjunction( - lists:flatten( - [begin - Gave = length(OIdxs1 -- OIdxs2), % in original and not new - Took = length(OIdxs2 -- OIdxs1), - V1 = count_violations(OIdxs1, Q, TN), - V2 = count_violations(OIdxs2, Q, TN), - [{{give, Node, Gave, Give}, Gave =< Give}, - {{take, Node, Took, Take}, Took =< Take}, - {{valid, Node, V1, V2}, - V2 == 0 orelse - V1 > 0 orelse % check no violations if there were not before - OIdxs1 == []}] % or the node held no indices so violation was impossible - end || {{Node, Give, Take, _CIdxs}, {Node, _Want1, OIdxs1}, {Node, _Want2, OIdxs2}} <- - lists:zip3(lists:sort(Exchanges), lists:sort(Before), lists:sort(After))])). - -count_violations([], _Q, _TN) -> - 0; -count_violations(Idxs, Q, TN) -> - SOIdxs = lists:sort(Idxs), - {_, Violations} = lists:foldl( - fun(This, {Last, Vs}) -> - case Last - This >= TN of - true -> - {This, Vs}; - _ -> - {This, Vs + 1} - end - end, {Q + hd(SOIdxs), 0}, lists:reverse(SOIdxs)), - Violations. - --endif. % EQC --endif. % TEST +-endif. diff --git a/src/riak_core_claim_util.erl b/src/riak_core_claim_util.erl index 67c5b8a13..66739c147 100644 --- a/src/riak_core_claim_util.erl +++ b/src/riak_core_claim_util.erl @@ -59,14 +59,6 @@ rotations/1, substitutions/2]). --ifdef(TEST). --ifdef(PROPER). --include_lib("proper/include/proper.hrl"). -%-compile(export_all). --endif. --include_lib("eunit/include/eunit.hrl"). --endif. - -record(load, {node, % Node name num_pri, % Number of primaries num_fb, % Number of fallbacks @@ -632,52 +624,4 @@ substitute(Names, Mapping, L) -> %% ------------------------------------------------------------------- %% Unit Tests %% ------------------------------------------------------------------- - --ifdef(TEST). --ifdef(PROPER). - -property_adjacency_summary_test_() -> - {timeout, 120, - ?_test(proper:quickcheck(prop_adjacency_summary(), [{numtest, 5000}]))}. - -longer_list(K, G) -> - ?SIZED(Size, proper_types:resize(trunc(K*Size), list(proper_types:resize(Size, G)))). - -%% Compare directly constructing the adjacency matrix against -%% one using prepend/fixup. -prop_adjacency_summary() -> - ?FORALL({OwnersSeed, S}, - {non_empty(longer_list(40, proper_types:largeint())), - ?LET(X, proper_types:int(), 1 + abs(X))}, - begin - io:format("1\n"), - Owners = [list_to_atom("n" ++ integer_to_list(1 + (abs(I) rem S))) - || I <- OwnersSeed], - AM = adjacency_matrix(Owners), - AS = summarize_am(AM), - - {Owners2, _DAM2, FixDAM2} = build(Owners), - AS2 = summarize_am(dict:to_list(FixDAM2)), - io:format("2\n"), - ?WHENFAIL( - begin - io:format(user, "S=~p\nOwners =~p\n", [S, Owners]), - io:format(user, "=== AM ===\n~p\n", [AM]), - io:format(user, "=== FixAM2 ===\n~p\n", [dict:to_list(FixDAM2)]), - io:format(user, "=== AS2 ===\n~p\n", [AS2]) - end, - proper:conjunction([{owners, Owners == Owners2}, - {am2, lists:sort(AS)== lists:sort(AS2)}])) - end). - -build(Owners) -> - build(lists:usort(Owners), lists:reverse(Owners), [], dict:new()). - -build(_M, [], Owners, DAM) -> - {Owners, DAM, fixup_dam(Owners, DAM)}; -build(M, [N|Rest], Owners, DAM) -> - {Owners1, DAM1} = prepend(M, N, Owners, DAM), - build(M, Rest, Owners1, DAM1). - --endif. % EQC --endif. % TEST. +%See test - pqc - riak_core_claim_util_qc diff --git a/src/riak_core_ring_util.erl b/src/riak_core_ring_util.erl index af18edb38..24cee9bed 100644 --- a/src/riak_core_ring_util.erl +++ b/src/riak_core_ring_util.erl @@ -30,16 +30,6 @@ hash_is_partition_boundary/2]). -ifdef(TEST). --ifdef(PROPER). - --compile(export_all). --export([prop_ids_are_boundaries/0, - prop_reverse/0, - prop_monotonic/0, - prop_only_boundaries/0]). - --include_lib("proper/include/proper.hrl"). --endif. -include_lib("eunit/include/eunit.hrl"). -endif. @@ -137,135 +127,4 @@ boundary_test() -> ?assertNot(riak_core_ring_util:hash_is_partition_boundary(<<(BoundaryIndex - 1):160>>, 32)), ?assertNot(riak_core_ring_util:hash_is_partition_boundary(<<(BoundaryIndex + 2):160>>, 32)), ?assertNot(riak_core_ring_util:hash_is_partition_boundary(<<(BoundaryIndex + 10):160>>, 32)). - --ifdef(PROPER). - --define(QC_OUT(P), - proper:on_output(fun(Str, Args) -> - io:format(user, Str, Args) end, P)). --define(TEST_TIME_SECS, 5). - --define(HASHMAX, 1 bsl 160 - 1). --define(RINGSIZEEXPMAX, 11). --define(RINGSIZE(X), (1 bsl X)).%% We'll generate powers of 2 with choose() - %% and convert that to a ring size with this macro --define(PARTITIONSIZE(X), ((1 bsl 160) div (X))). - -ids_are_boundaries_test_() -> - {timeout, ?TEST_TIME_SECS+5, [?_assert(test_ids_are_boundaries() =:= true)]}. - -test_ids_are_boundaries() -> - test_ids_are_boundaries(?TEST_TIME_SECS). -%TODO check time sec -test_ids_are_boundaries(_TestTimeSecs) -> - proper:quickcheck(?QC_OUT(prop_ids_are_boundaries()), [{numtests, 5000}]). - -reverse_test_() -> - {timeout, ?TEST_TIME_SECS+5, [?_assert(test_reverse() =:= true)]}. - -test_reverse() -> - test_reverse(?TEST_TIME_SECS). -%TODO check time sec -test_reverse(_TestTimeSecs) -> - proper:quickcheck(prop_reverse(), [{numtests, 5000}]). - - -monotonic_test_() -> - {timeout, ?TEST_TIME_SECS+5, [?_assert(test_monotonic() =:= true)]}. - -test_monotonic() -> - test_monotonic(?TEST_TIME_SECS). - -test_monotonic(TestTimeSecs) -> - proper:quickcheck(?QC_OUT(prop_monotonic()), [{numtests, 5000}]). - - -%% `prop_only_boundaries' should run a little longer: not quite as -%% fast, need to scan a larger portion of hash space to establish -%% correctness -only_boundaries_test_() -> - {timeout, ?TEST_TIME_SECS+15, [?_assert(test_only_boundaries() =:= true)]}. - -test_only_boundaries() -> - test_only_boundaries(?TEST_TIME_SECS+10). - -test_only_boundaries(TestTimeSecs) -> - proper:quickcheck(prop_only_boundaries(), [{numtests, 5000}]). - -%% Partition IDs should map to hash values which are partition boundaries -prop_ids_are_boundaries() -> - ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), - ?FORALL(PartitionId, choose(0, ?RINGSIZE(RingPower) - 1), - begin - RingSize = ?RINGSIZE(RingPower), - BoundaryHash = - riak_core_ring_util:partition_id_to_hash(PartitionId, - RingSize), - equals(true, - riak_core_ring_util:hash_is_partition_boundary(BoundaryHash, - RingSize)) - end - )). - -%% Partition IDs should map to hash values which map back to the same partition IDs -prop_reverse() -> - ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), - ?FORALL(PartitionId, choose(0, ?RINGSIZE(RingPower) - 1), - begin - RingSize = ?RINGSIZE(RingPower), - BoundaryHash = - riak_core_ring_util:partition_id_to_hash(PartitionId, - RingSize), - equals(PartitionId, - riak_core_ring_util:hash_to_partition_id( - BoundaryHash, RingSize)) - end - )). - -%% For any given hash value, any larger hash value maps to a partition -%% ID of greater or equal value. -prop_monotonic() -> - ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), - ?FORALL(HashValue, choose(0, ?HASHMAX - 1), - ?FORALL(GreaterHash, choose(HashValue + 1, ?HASHMAX), - begin - RingSize = ?RINGSIZE(RingPower), - LowerPartition = - riak_core_ring_util:hash_to_partition_id(HashValue, - RingSize), - GreaterPartition = - riak_core_ring_util:hash_to_partition_id(GreaterHash, - RingSize), - LowerPartition =< GreaterPartition - end - ))). - -%% Hash values which are listed in the ring structure are boundary -%% values -ring_to_set({_RingSize, PropList}) -> - ordsets:from_list(lists:map(fun({Hash, dummy}) -> Hash end, PropList)). - -find_near_boundaries(RingSize, PartitionSize) -> - ?LET({Id, Offset}, {choose(1, RingSize-1), choose(-(RingSize*2), (RingSize*2))}, - Id * PartitionSize + Offset). - -prop_only_boundaries() -> - ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), - ?FORALL({HashValue, BoundarySet}, - {frequency([ - {5, choose(0, ?HASHMAX)}, - {2, find_near_boundaries(?RINGSIZE(RingPower), - ?PARTITIONSIZE(?RINGSIZE(RingPower)))}]), - ring_to_set(chash:fresh(?RINGSIZE(RingPower), dummy))}, - begin - RingSize = ?RINGSIZE(RingPower), - HashIsInRing = ordsets:is_element(HashValue, BoundarySet), - HashIsPartitionBoundary = - riak_core_ring_util:hash_is_partition_boundary(HashValue, - RingSize), - equals(HashIsPartitionBoundary, HashIsInRing) - end - )). - --endif. % EQC -endif. % TEST diff --git a/test/eqc/13node_12node_ring.eqc b/test/eqc/13node_12node_ring.eqc deleted file mode 100644 index b5bd40061d7b647a3dd99cfc589096e17b6f5a21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6804 zcmeI%Ele9>9DwobC#8H9=m-LVKp88nGzbLA5C{YU zfj}S-2n2#92n0(I2n1OWEI}X;2$moa2*j>8^0w=H}k@dinSGp3yYT zQ~K3mcy-NhX3awBPAFdxxq_LUHG;Ypur>-Q5x1?b1+(*dDiKQykI|rO;iO^jP80{^ zE*q9I&(!b=Pd1>v|M}xrbK2UlOL6OtRzb2+YZc1Pk0OMk5k&+=6h$YBE)?A;dQkMD z=tB`hF^FOa#W0FEiV+l}D8^8XqnJQ3iDC-HGzuMsfg*!q0Yw4DB8nvx%P3Y*tfE*$ zv5sN`#U_d^6x%3vQ0$`EL$QzI0L3ASBNWFdPEeepI74xc;sV7biYpY?C~i>PqPRnG zkKzHvBZ?;}(nh^C+~{VqAT$xNenMhg#EeL~>SlNx{#G%^_+~UM@OwrJUj4D|`M#!= zNylf6OGS|}b!AWG3?z4kXXo>Ib0Ja8XN5PNmJ62Pd4j6m{%@jES5f&`X~SBRFQ2mK zI$u%bthvo^y&6f8$>>@~&J^Dg$@yF=k<18lE@2OIbJheXbUjg;^hyscUxNO(k*{Jz z?Ue7A;dLHgyHIqa=t0qoq7Ow3#UP3y6vHUuC`M3>q8LLlj$#7EB#J2%(Py}-r7gLfzpM%e{-WcO8RQ= zevnh&HeQGPeW_*n^riJ62+AIrKh~guv%_(Q+7VOzQ||apmv*T>DJko z=KEtZ+^B||UlHz?;U+cQ@``ZzBxC)9p!)l2P>yNw; z!dHEJXZj5hv6t^hu zP~4+pzzRGysF&OSH7b1+wa2neXH0=o&h2%9 ziYz+4E$Ez$tjy;P@xExu@0sjeF_+2ar8S#z%iOHf0HtAMs!gwYXmtvD-zr~CMQvBt z%kBvHp;$(-f?^fL8j5uk8z?qWY@ygjv4dh4#U6@%6bC2{Q5>N-Msb4T z6vY{ea}*aSE>T>exJGe<;uggnihC3fC?1t~t9-SU&+onR^;I9_{p*c-l?-^#{it&N zdT!8rZokS6=(!>9xdSRUsON^g=YCSTAw4(ZJ@>Q94ePm4@4162H=^e@HIf^0XBuis z!&XMR2bQj_bkAuLyqr`X= mU9M(5tF@7=SUx8!IelLFe8HSQ2wu)tlZ_ io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -define(RINGTOP, trunc(math:pow(2,160)-1)). % SHA-1 space --export([check/0, - test/0, +-export([test/0, test/1]). %%==================================================================== @@ -55,8 +54,7 @@ eqc_test_() -> {timeout, 60000, % timeout is in msec %% Indicate the number of test iterations for each property here ?_assertEqual(true, - quickcheck(numtests(?TEST_ITERATIONS, - ?QC_OUT(prop_chash_next_index())))) + proper:quickcheck(?QC_OUT(prop_chash_next_index()),[{numtests,?TEST_ITERATIONS}])) } ] } @@ -139,9 +137,9 @@ test() -> test(100). test(N) -> - quickcheck(numtests(N, prop_chash_next_index())). + proper:quickcheck(numtests(N, prop_chash_next_index())). -check() -> - check(prop_chash_next_index(), current_counterexample()). +% check() -> +% check(prop_chash_next_index(), current_counterexample()). -endif. % EQC diff --git a/eqc/core_vnode_eqc.erl b/test/pqc/core_vnode_eqc.erl similarity index 97% rename from eqc/core_vnode_eqc.erl rename to test/pqc/core_vnode_eqc.erl index 59bdc4218..e5d87c590 100644 --- a/eqc/core_vnode_eqc.erl +++ b/test/pqc/core_vnode_eqc.erl @@ -24,9 +24,8 @@ -module(core_vnode_eqc). -ifdef(TEST). --ifdef(PROPER). +-ifdef(PORPER). -include_lib("proper/include/proper.hrl"). -%-include_lib("eqc/include/eqc_fsm.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("riak_core/include/riak_core_vnode.hrl"). -compile([export_all]). @@ -38,7 +37,7 @@ end). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -record(qcst, {started, counters, % Dict of counters for each index @@ -61,7 +60,7 @@ simple_test_() -> ok end, {timeout, 600, - ?_assertEqual(true, quickcheck(?QC_OUT(numtests(100, prop_simple()))))}}. + ?_assertEqual(true, proper:quickcheck(?QC_OUT(numtests(100, prop_simple()))))}}. setup_simple() -> %% call `meck:unload' here because there are other tests that have @@ -89,7 +88,7 @@ setup_simple() -> OldVars. test(N) -> - quickcheck(numtests(N, prop_simple())). + proper:quickcheck(numtests(N, prop_simple())). eqc_setup() -> OldVars = setup_simple(), @@ -102,10 +101,10 @@ eqc_setup() -> prop_simple() -> ?SETUP(fun eqc_setup/0, - ?FORALL(Cmds, commands(?MODULE, {setup, initial_state_data()}), + ?FORALL(Cmds, proper_fsm:commands(?MODULE, {setup, initial_state_data()}), aggregate(command_names(Cmds), begin - {H,{_SN,S},Res} = run_commands(?MODULE, Cmds), + {H,{_SN,S},Res} = proper_fsm:run_commands(?MODULE, Cmds), timer:sleep(500), %% Adjust this to make shutdown sensitive stuff pass/fail %% Do a sync operation on all the started vnodes %% to ensure any of the noreply commands have executed before @@ -214,10 +213,12 @@ next_state_data(_From,_To,S,_R,_C) -> setup(S) -> [{setup, {call,?MODULE,enable_async,[gen_async_pool()]}}, - {stopped, {call,?MODULE,prepare,[S#qcst.async_size]}}]. + {stopped, {call,?MODULE,prepare,[S#qcst.async_size]}} + ]. stopped(S) -> - [{running, {call,?MODULE,start_vnode,[index(S)]}}]. + [{running, {call,?MODULE,start_vnode,[index(S)]}} + ]. running(S) -> [ diff --git a/eqc/new_cluster_membership_model_eqc.erl b/test/pqc/new_cluster_membership_model_eqc.erl similarity index 98% rename from eqc/new_cluster_membership_model_eqc.erl rename to test/pqc/new_cluster_membership_model_eqc.erl index 5655a7ef4..f48cc7585 100644 --- a/eqc/new_cluster_membership_model_eqc.erl +++ b/test/pqc/new_cluster_membership_model_eqc.erl @@ -1,15 +1,14 @@ -module(new_cluster_membership_model_eqc). -ifdef(MODEL). --ifdef(EQC). --include_lib("eqc/include/eqc.hrl"). --include_lib("eqc/include/eqc_statem.hrl"). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -compile(export_all). -define(TEST_ITERATIONS, 3000). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). -define(OUT(S,A),ok). %%-define(OUT(S,A),io:format(S,A)). @@ -27,7 +26,7 @@ vclock :: vclock:vclock(), % for this chstate object, entries are % {Node, Ctr} chring :: chash:chash(), % chash ring of {IndexAsInt, Node} mappings - meta :: dict(), % dict of cluster-wide other data (primarily + meta :: dict:dict(), % dict of cluster-wide other data (primarily % bucket N-value, etc) clustername :: {node(), term()}, @@ -50,7 +49,7 @@ %% Global test state -record(state, { - nstates :: dict(), + nstates :: dict:dict(), ring_size :: integer(), members :: [{node(), {member_status(), vclock:vclock()}}], primary :: [integer()], @@ -63,7 +62,7 @@ active_handoffs :: [{integer(), integer(), integer()}], seed :: {integer(), integer(), integer()}, old_seed :: {integer(), integer(), integer()}, - split :: dict() + split :: dict:dict() }). eqc_test_() -> @@ -74,8 +73,8 @@ eqc_test_() -> [{inorder, [manual_test_list(), %% Run the quickcheck tests - {timeout, 60000, % timeout is in msec - ?_assertEqual(true, catch quickcheck(numtests(?TEST_ITERATIONS, ?QC_OUT(prop_join()))))} + {timeout, 60000000, % timeout is in msec + ?_assertEqual(true, catch proper:quickcheck(numtests(?TEST_ITERATIONS, ?QC_OUT(prop_join()))))} ]} ] } @@ -83,7 +82,7 @@ eqc_test_() -> }. eqc() -> - quickcheck(numtests(?TEST_ITERATIONS, ?QC_OUT(prop_join()))), + proper:quickcheck(numtests(?TEST_ITERATIONS, ?QC_OUT(prop_join()))), ok. setup() -> @@ -93,7 +92,7 @@ cleanup(_) -> ok. prop_join() -> - ?FORALL(Cmds, more_commands(100, commands(?MODULE)), + ?FORALL(Cmds, commands(?MODULE), ?TRAPEXIT( ( begin @@ -276,7 +275,8 @@ initial_state() -> g_initial_nodes() -> Nodes = lists:seq(0, ?MAX_NODES-1), - ?LET(L, shuffle(Nodes), lists:split(?INITIAL_CLUSTER_SIZE, L)). + ?LET(L, Nodes, %shuffle(Nodes) + lists:split(?INITIAL_CLUSTER_SIZE, L)). g_idx(State) -> Indices = [Idx || {Idx, _} <- chash:nodes(chash:fresh(State#state.ring_size, undefined))], @@ -287,7 +287,7 @@ g_gossip(State, Gossip) -> [{Node, get_nstate(State, Node)}, OtherNode, OtherCS]. g_random_ring(State) -> - shuffle(lists:seq(0, State#state.ring_size-1)). + lists:seq(0, State#state.ring_size-1).%shuffle( g_posint() -> ?SUCHTHAT(X, largeint(), X > 0). @@ -1386,19 +1386,19 @@ ring_ready(CState0) -> end. seed_random(State) -> - OldSeed = random:seed(State#state.seed), + OldSeed = rand:seed(State#state.seed), State#state{old_seed=OldSeed}. save_random(State=#state{old_seed=undefined}) -> - Seed = random:seed(), + Seed = rand:seed(), State#state{seed=Seed}; save_random(State=#state{old_seed=OldSeed}) -> - Seed = random:seed(OldSeed), + Seed = rand:seed(OldSeed), State#state{seed=Seed}. save_random() -> - Seed = random:seed(), - random:seed(Seed), + Seed = rand:seed(), + rand:seed(Seed), Seed. ring_changed(State, _RRing, {Node, _NState}, CState0) -> @@ -1620,7 +1620,7 @@ handle_down_nodes(CState, Next) -> case (OwnerLeaving and NextDown) of true -> Active = riak_core_ring:active_members(CState) -- [O], - RNode = lists:nth(random:uniform(length(Active)), + RNode = lists:nth(rand:uniform(length(Active)), Active), {Idx, O, RNode, Mods, Status}; _ -> @@ -1743,7 +1743,7 @@ attempt_simple_transfer(Ring, [{P, Exit}|Rest], TargetN, Exit, Idx, Last) -> target_n_fail; Qualifiers -> %% these nodes don't violate target_n forward - Chosen = lists:nth(random:uniform(length(Qualifiers)), + Chosen = lists:nth(rand:uniform(length(Qualifiers)), Qualifiers), %% choose one, and do the rest of the ring attempt_simple_transfer( diff --git a/test/eqc/node_watcher_qc.erl b/test/pqc/node_watcher_qc.erl similarity index 100% rename from test/eqc/node_watcher_qc.erl rename to test/pqc/node_watcher_qc.erl diff --git a/test/pqc/riak_core_claim_qc.erl b/test/pqc/riak_core_claim_qc.erl new file mode 100644 index 000000000..ccd39794b --- /dev/null +++ b/test/pqc/riak_core_claim_qc.erl @@ -0,0 +1,323 @@ +-module(riak_core_claim_qc). +-ifdef(TEST). +-ifdef(PROPER). + +-compile(export_all). +-export([prop_claim_ensures_unique_nodes/1, prop_wants/0, prop_wants_counts/0, eqc_check/2]). +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-define(QC_OUT(P), + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + +-define(POW_2(N), trunc(math:pow(2, N))). + +eqc_check(File, Prop) -> + {ok, Bytes} = file:read_file(File), + CE = binary_to_term(Bytes), + proper:check(Prop, CE). + +test_nodes(Count) -> + [node() | [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(1, Count-1)]]. + +test_nodes(Count, StartNode) -> + [list_to_atom(lists:concat(["n_", N])) || N <- lists:seq(StartNode, StartNode + Count)]. + +property_claim_ensures_unique_nodes_v2_test_() -> + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. + +property_claim_ensures_unique_nodes_adding_groups_v2_test_() -> + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_groups(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. + +property_claim_ensures_unique_nodes_adding_singly_v2_test_() -> + Prop = ?QC_OUT(prop_claim_ensures_unique_nodes_adding_singly(choose_claim_v2)), + {timeout, 120, fun() -> ?_assert(proper:quickcheck(Prop, [{numtests, 5000}])) end}. + +prop_claim_ensures_unique_nodes(ChooseFun) -> + %% NOTE: We know that this doesn't work for the case of {_, 3}. + %% NOTE2: uses undocumented "double_shrink", is expensive, but should get + %% around those case where we shrink to a non-minimal case because + %% some intermediate combinations of ring_size/node have no violations + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, + begin + Nval = 3, + TNval = Nval + 1, + _Params = [{target_n_val, TNval}], + + Partitions = ?POW_2(PartsPow), + [Node0 | RestNodes] = test_nodes(NodeCount), + + R0 = riak_core_ring:fresh(Partitions, Node0), + RAdded = lists:foldl(fun(Node, Racc) -> + riak_core_ring:add_member(Node0, Racc, Node) + end, R0, RestNodes), + + Rfinal = riak_core_claim:claim(RAdded, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), + + Preflists = riak_core_ring:all_preflists(Rfinal, Nval), + ImperfectPLs = orddict:to_list( + lists:foldl(fun(PL, Acc) -> + PLNodes = lists:usort([N || {_, N} <- PL]), + case length(PLNodes) of + Nval -> + Acc; + _ -> + ordsets:add_element(PL, Acc) + end + end, [], Preflists)), + + ?WHENFAIL( + begin + io:format(user, "{Partitions, Nodes} {~p, ~p}~n", + [Partitions, NodeCount]), + io:format(user, "Owners: ~p~n", + [riak_core_ring:all_owners(Rfinal)]) + end, + conjunction([{meets_target_n, + equals({true, []}, + riak_core_claim:meets_target_n(Rfinal, TNval))}, + {perfect_preflists, equals([], ImperfectPLs)}, + {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) + end). + + +prop_claim_ensures_unique_nodes_adding_groups(ChooseFun) -> + %% NOTE: We know that this doesn't work for the case of {_, 3}. + %% NOTE2: uses undocumented "double_shrink", is expensive, but should get + %% around those case where we shrink to a non-minimal case because + %% some intermediate combinations of ring_size/node have no violations + ?FORALL({PartsPow, BaseNodes, AddedNodes}, + {choose(4, 9), choose(2, 10), choose(2, 5)}, + begin + Nval = 3, + TNval = Nval + 1, + _Params = [{target_n_val, TNval}], + + Partitions = ?POW_2(PartsPow), + [Node0 | RestNodes] = test_nodes(BaseNodes), + AddNodes = test_nodes(AddedNodes-1, BaseNodes), + NodeCount = BaseNodes + AddedNodes, + %% io:format("Base: ~p~n",[[Node0 | RestNodes]]), + %% io:format("Added: ~p~n",[AddNodes]), + + R0 = riak_core_ring:fresh(Partitions, Node0), + RBase = lists:foldl(fun(Node, Racc) -> + riak_core_ring:add_member(Node0, Racc, Node) + end, R0, RestNodes), + + Rinterim = riak_core_claim:claim(RBase, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), + RAdded = lists:foldl(fun(Node, Racc) -> + riak_core_ring:add_member(Node0, Racc, Node) + end, Rinterim, AddNodes), + + Rfinal = riak_core_claim:claim(RAdded, {?MODULE, wants_claim_v2}, {?MODULE, ChooseFun}), + + Preflists = riak_core_ring:all_preflists(Rfinal, Nval), + ImperfectPLs = orddict:to_list( + lists:foldl(fun(PL, Acc) -> + PLNodes = lists:usort([N || {_, N} <- PL]), + case length(PLNodes) of + Nval -> + Acc; + _ -> + ordsets:add_element(PL, Acc) + end + end, [], Preflists)), + + ?WHENFAIL( + begin + io:format(user, "{Partitions, Nodes} {~p, ~p}~n", + [Partitions, NodeCount]), + io:format(user, "Owners: ~p~n", + [riak_core_ring:all_owners(Rfinal)]) + end, + conjunction([{meets_target_n, + equals({true, []}, + riak_core_claim:meets_target_n(Rfinal, TNval))}, + {perfect_preflists, equals([], ImperfectPLs)}, + {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) + end). + + +prop_claim_ensures_unique_nodes_adding_singly(ChooseFun) -> + %% NOTE: We know that this doesn't work for the case of {_, 3}. + %% NOTE2: uses undocumented "double_shrink", is expensive, but should get + %% around those case where we shrink to a non-minimal case because + %% some intermediate combinations of ring_size/node have no violations + ?FORALL({PartsPow, NodeCount}, {choose(4, 9), choose(4, 15)}, + begin + Nval = 3, + TNval = Nval + 1, + Params = [{target_n_val, TNval}], + + Partitions = ?POW_2(PartsPow), + [Node0 | RestNodes] = test_nodes(NodeCount), + + R0 = riak_core_ring:fresh(Partitions, Node0), + Rfinal = lists:foldl(fun(Node, Racc) -> + Racc0 = riak_core_ring:add_member(Node0, Racc, Node), + %% TODO which is it? Claim or ChooseFun?? + %%claim(Racc0, {?MODULE, wants_claim_v2}, + %% {?MODULE, ChooseFun}) + ?MODULE:ChooseFun(Racc0, Node, Params) + end, R0, RestNodes), + Preflists = riak_core_ring:all_preflists(Rfinal, Nval), + ImperfectPLs = orddict:to_list( + lists:foldl(fun(PL, Acc) -> + PLNodes = lists:usort([N || {_, N} <- PL]), + case length(PLNodes) of + Nval -> + Acc; + _ -> + ordsets:add_element(PL, Acc) + end + end, [], Preflists)), + + ?WHENFAIL( + begin + io:format(user, "{Partitions, Nodes} {~p, ~p}~n", + [Partitions, NodeCount]), + io:format(user, "Owners: ~p~n", + [riak_core_ring:all_owners(Rfinal)]) + end, + conjunction([{meets_target_n, + equals({true, []}, + riak_core_claim:meets_target_n(Rfinal, TNval))}, + {perfect_preflists, equals([], ImperfectPLs)}, + {balanced_ring, balanced_ring(Partitions, NodeCount, Rfinal)}])) + end). + + + +%% @private check that no node claims more than it should +-spec balanced_ring(RingSize::integer(), NodeCount::integer(), + riak_core_ring:riak_core_ring()) -> + boolean(). +balanced_ring(RingSize, NodeCount, Ring) -> + TargetClaim = riak_core_claim:ceiling(RingSize / NodeCount), + MinClaim = RingSize div NodeCount, + AllOwners0 = riak_core_ring:all_owners(Ring), + AllOwners = lists:keysort(2, AllOwners0), + {BalancedMax, AccFinal} = lists:foldl(fun({_Part, Node}, {_Balanced, [{Node, Cnt} | Acc]}) + when Cnt >= TargetClaim -> + {false, [{Node, Cnt+1} | Acc]}; + ({_Part, Node}, {Balanced, [{Node, Cnt} | Acc]}) -> + {Balanced, [{Node, Cnt+1} | Acc]}; + ({_Part, NewNode}, {Balanced, Acc}) -> + {Balanced, [{NewNode, 1} | Acc]} + end, + {true, []}, + AllOwners), + BalancedMin = lists:all(fun({_Node, Cnt}) -> Cnt >= MinClaim end, AccFinal), + case BalancedMax andalso BalancedMin of + true -> + true; + false -> + {TargetClaim, MinClaim, lists:sort(AccFinal)} + end. + + +wants_counts_test() -> + {timeout, 120, + ?assert(proper:quickcheck(?QC_OUT((prop_wants_counts())), [{numtests, 5000}]))}. + +prop_wants_counts() -> + ?FORALL({S, Q}, {large_pos(100), large_pos(100000)}, + begin + Wants = riak_core_claim:wants_counts(S, Q), + conjunction([{len, S == length(Wants)}, + {sum, Q == lists:sum(Wants)}]) + end). + +wants_test() -> + {timeout, 120, + ?_assert(proper:quickcheck(?QC_OUT(prop_wants()), [{numtests, 5000}]))}. + +prop_wants() -> + ?FORALL({NodeStatus, Q}, + {?SUCHTHAT(L, non_empty(list(elements([leaving, joining]))), + lists:member(joining, L)), + ?LET(X, choose(1, 16), trunc(math:pow(2, X)))}, + begin + R0 = riak_core_ring:fresh(Q, tnode(1)), + {_, R2, Active} = + lists:foldl( + fun(S, {I, R1, A1}) -> + N = tnode(I), + case S of + joining -> + {I+1, riak_core_ring:add_member(N, R1, N), [N|A1]}; + _ -> + {I+1, riak_core_ring:leave_member(N, R1, N), A1} + end + end, {1, R0, []}, NodeStatus), + Wants = riak_core_claim:wants(R2), + + %% Check any non-claiming nodes are set to 0 + %% Check all nodes are present + {ActiveWants, InactiveWants} = + lists:partition(fun({N, _W}) -> lists:member(N, Active) end, Wants), + + ActiveSum = lists:sum([W || {_, W} <- ActiveWants]), + InactiveSum = lists:sum([W || {_, W} <- InactiveWants]), + ?WHENFAIL( + begin + io:format(user, "NodeStatus: ~p\n", [NodeStatus]), + io:format(user, "Active: ~p\n", [Active]), + io:format(user, "Q: ~p\n", [Q]), + io:format(user, "Wants: ~p\n", [Wants]), + io:format(user, "ActiveWants: ~p\n", [ActiveWants]), + io:format(user, "InactiveWants: ~p\n", [InactiveWants]) + end, + conjunction([{wants, length(Wants) == length(NodeStatus)}, + {active, Q == ActiveSum}, + {inactive, 0 == InactiveSum}])) + end). + +%% Large positive integer between 1 and Max +large_pos(Max) -> + ?LET(X, largeint(), 1 + (abs(X) rem Max)). + + +tnode(I) -> + list_to_atom("n" ++ integer_to_list(I)). + +%% Check that no node gained more than it wanted to take +%% Check that none of the nodes took more partitions than allowed +%% Check that no nodes violate target N +check_deltas(Exchanges, Before, After, Q, TN) -> + conjunction( + lists:flatten( + [begin + Gave = length(OIdxs1 -- OIdxs2), % in original and not new + Took = length(OIdxs2 -- OIdxs1), + V1 = count_violations(OIdxs1, Q, TN), + V2 = count_violations(OIdxs2, Q, TN), + [{{give, Node, Gave, Give}, Gave =< Give}, + {{take, Node, Took, Take}, Took =< Take}, + {{valid, Node, V1, V2}, + V2 == 0 orelse + V1 > 0 orelse % check no violations if there were not before + OIdxs1 == []}] % or the node held no indices so violation was impossible + end || {{Node, Give, Take, _CIdxs}, {Node, _Want1, OIdxs1}, {Node, _Want2, OIdxs2}} <- + lists:zip3(lists:sort(Exchanges), lists:sort(Before), lists:sort(After))])). + +count_violations([], _Q, _TN) -> + 0; +count_violations(Idxs, Q, TN) -> + SOIdxs = lists:sort(Idxs), + {_, Violations} = lists:foldl( + fun(This, {Last, Vs}) -> + case Last - This >= TN of + true -> + {This, Vs}; + _ -> + {This, Vs + 1} + end + end, {Q + hd(SOIdxs), 0}, lists:reverse(SOIdxs)), + Violations. + +-endif. % EQC +-endif. % TEST diff --git a/test/eqc/riak_core_claim_statem.erl b/test/pqc/riak_core_claim_statem.erl similarity index 99% rename from test/eqc/riak_core_claim_statem.erl rename to test/pqc/riak_core_claim_statem.erl index c3457671b..f683d7252 100644 --- a/test/eqc/riak_core_claim_statem.erl +++ b/test/pqc/riak_core_claim_statem.erl @@ -50,13 +50,13 @@ starting(S) -> planning(S) -> [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, - {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}, - {claiming, {call, ?MODULE, claim, claim_args(S)}} %TODO + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}%, + %{claiming, {call, ?MODULE, claim, claim_args(S)}} %TODO ]. claiming(S) -> [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, - {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}]. + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}].+ %% -- Operations ------------------------------------------------------------- diff --git a/test/pqc/riak_core_claim_util_qc.erl b/test/pqc/riak_core_claim_util_qc.erl new file mode 100644 index 000000000..d58c495bc --- /dev/null +++ b/test/pqc/riak_core_claim_util_qc.erl @@ -0,0 +1,50 @@ +-module(riak_core_claim_util_qc). +-ifdef(TEST). +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). +%-compile(export_all). + +property_adjacency_summary_test_() -> + {timeout, 120, + ?_test(proper:quickcheck(prop_adjacency_summary(), [{numtest, 5000}]))}. + +longer_list(K, G) -> + ?SIZED(Size, proper_types:resize(trunc(K*Size), list(proper_types:resize(Size, G)))). + +%% Compare directly constructing the adjacency matrix against +%% one using prepend/fixup. +prop_adjacency_summary() -> + ?FORALL({OwnersSeed, S}, + {non_empty(longer_list(40, proper_types:largeint())), + ?LET(X, proper_types:int(), 1 + abs(X))}, + begin + Owners = [list_to_atom("n" ++ integer_to_list(1 + (abs(I) rem S))) + || I <- OwnersSeed], + AM = riak_core_claim_util:adjacency_matrix(Owners), + AS = riak_core_claim_util:summarize_am(AM), + + {Owners2, _DAM2, FixDAM2} = build(Owners), + AS2 = riak_core_claim_util:summarize_am(dict:to_list(FixDAM2)), + ?WHENFAIL( + begin + io:format(user, "S=~p\nOwners =~p\n", [S, Owners]), + io:format(user, "=== AM ===\n~p\n", [AM]), + io:format(user, "=== FixAM2 ===\n~p\n", [dict:to_list(FixDAM2)]), + io:format(user, "=== AS2 ===\n~p\n", [AS2]) + end, + proper:conjunction([{owners, Owners == Owners2}, + {am2, lists:sort(AS)== lists:sort(AS2)}])) + end). + +build(Owners) -> + build(lists:usort(Owners), lists:reverse(Owners), [], dict:new()). + +build(_M, [], Owners, DAM) -> + {Owners, DAM, riak_core_claim_util:fixup_dam(Owners, DAM)}; +build(M, [N|Rest], Owners, DAM) -> + {Owners1, DAM1} = riak_core_claim_util:prepend(M, N, Owners, DAM), + build(M, Rest, Owners1, DAM1). + +-endif. +-endif. diff --git a/eqc/riak_core_ring_eqc.erl b/test/pqc/riak_core_ring_eqc.erl similarity index 95% rename from eqc/riak_core_ring_eqc.erl rename to test/pqc/riak_core_ring_eqc.erl index bf45d96d2..1187e9d8e 100644 --- a/eqc/riak_core_ring_eqc.erl +++ b/test/pqc/riak_core_ring_eqc.erl @@ -21,15 +21,15 @@ -module(riak_core_ring_eqc). --ifdef(EQC). +-ifdef(PROPER). -export([prop_future_index/0]). --include_lib("eqc/include/eqc.hrl"). +-include_lib("proper/include/proper.hrl"). -include_lib("eunit/include/eunit.hrl"). -define(TEST_ITERATIONS, 10000). -define(QC_OUT(P), - eqc:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). + proper:on_output(fun(Str, Args) -> io:format(user, Str, Args) end, P)). eqc_test_() -> @@ -43,7 +43,7 @@ eqc_test_() -> {timeout, 60000, % timeout is in msec %% Indicate the number of test iterations for each property here ?_assertEqual(true, - quickcheck(numtests(?TEST_ITERATIONS, + proper:quickcheck(numtests(?TEST_ITERATIONS, ?QC_OUT(prop_future_index())))) }]}]}]}. diff --git a/test/pqc/riak_core_ring_util_qc.erl b/test/pqc/riak_core_ring_util_qc.erl new file mode 100644 index 000000000..0a7f48220 --- /dev/null +++ b/test/pqc/riak_core_ring_util_qc.erl @@ -0,0 +1,141 @@ +-module(riak_core_ring_util_qc). +-ifdef(TEST). +-ifdef(PROPER). + +-compile(export_all). +-export([prop_ids_are_boundaries/0, + prop_reverse/0, + prop_monotonic/0, + prop_only_boundaries/0]). +-include_lib("proper/include/proper.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-define(QC_OUT(P), + proper:on_output(fun(Str, Args) -> + io:format(user, Str, Args) end, P)). +-define(TEST_TIME_SECS, 5). + +-define(HASHMAX, 1 bsl 160 - 1). +-define(RINGSIZEEXPMAX, 11). +-define(RINGSIZE(X), (1 bsl X)).%% We'll generate powers of 2 with choose() + %% and convert that to a ring size with this macro +-define(PARTITIONSIZE(X), ((1 bsl 160) div (X))). + +ids_are_boundaries_test_() -> + {timeout, ?TEST_TIME_SECS+5, [?_assert(test_ids_are_boundaries() =:= true)]}. + +test_ids_are_boundaries() -> + test_ids_are_boundaries(?TEST_TIME_SECS). +%TODO check time sec +test_ids_are_boundaries(_TestTimeSecs) -> + proper:quickcheck(?QC_OUT(prop_ids_are_boundaries()), [{numtests, 5000}]). + +reverse_test_() -> + {timeout, ?TEST_TIME_SECS+5, [?_assert(test_reverse() =:= true)]}. + +test_reverse() -> + test_reverse(?TEST_TIME_SECS). +%TODO check time sec +test_reverse(_TestTimeSecs) -> + proper:quickcheck(prop_reverse(), [{numtests, 5000}]). + + +monotonic_test_() -> + {timeout, ?TEST_TIME_SECS+5, [?_assert(test_monotonic() =:= true)]}. + +test_monotonic() -> + test_monotonic(?TEST_TIME_SECS). + +test_monotonic(_TestTimeSecs) -> + proper:quickcheck(?QC_OUT(prop_monotonic()), [{numtests, 5000}]). + + +%% `prop_only_boundaries' should run a little longer: not quite as +%% fast, need to scan a larger portion of hash space to establish +%% correctness +only_boundaries_test_() -> + {timeout, ?TEST_TIME_SECS+15, [?_assert(test_only_boundaries() =:= true)]}. + +test_only_boundaries() -> + test_only_boundaries(?TEST_TIME_SECS+10). + +test_only_boundaries(_TestTimeSecs) -> + proper:quickcheck(prop_only_boundaries(), [{numtests, 5000}]). + +%% Partition IDs should map to hash values which are partition boundaries +prop_ids_are_boundaries() -> + ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), + ?FORALL(PartitionId, choose(0, ?RINGSIZE(RingPower) - 1), + begin + RingSize = ?RINGSIZE(RingPower), + BoundaryHash = + riak_core_ring_util:partition_id_to_hash(PartitionId, + RingSize), + equals(true, + riak_core_ring_util:hash_is_partition_boundary(BoundaryHash, + RingSize)) + end + )). + +%% Partition IDs should map to hash values which map back to the same partition IDs +prop_reverse() -> + ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), + ?FORALL(PartitionId, choose(0, ?RINGSIZE(RingPower) - 1), + begin + RingSize = ?RINGSIZE(RingPower), + BoundaryHash = + riak_core_ring_util:partition_id_to_hash(PartitionId, + RingSize), + equals(PartitionId, + riak_core_ring_util:hash_to_partition_id( + BoundaryHash, RingSize)) + end + )). + +%% For any given hash value, any larger hash value maps to a partition +%% ID of greater or equal value. +prop_monotonic() -> + ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), + ?FORALL(HashValue, choose(0, ?HASHMAX - 1), + ?FORALL(GreaterHash, choose(HashValue + 1, ?HASHMAX), + begin + RingSize = ?RINGSIZE(RingPower), + LowerPartition = + riak_core_ring_util:hash_to_partition_id(HashValue, + RingSize), + GreaterPartition = + riak_core_ring_util:hash_to_partition_id(GreaterHash, + RingSize), + LowerPartition =< GreaterPartition + end + ))). + +%% Hash values which are listed in the ring structure are boundary +%% values +ring_to_set({_RingSize, PropList}) -> + ordsets:from_list(lists:map(fun({Hash, dummy}) -> Hash end, PropList)). + +find_near_boundaries(RingSize, PartitionSize) -> + ?LET({Id, Offset}, {choose(1, RingSize-1), choose(-(RingSize*2), (RingSize*2))}, + Id * PartitionSize + Offset). + +prop_only_boundaries() -> + ?FORALL(RingPower, choose(2, ?RINGSIZEEXPMAX), + ?FORALL({HashValue, BoundarySet}, + {frequency([ + {5, choose(0, ?HASHMAX)}, + {2, find_near_boundaries(?RINGSIZE(RingPower), + ?PARTITIONSIZE(?RINGSIZE(RingPower)))}]), + ring_to_set(chash:fresh(?RINGSIZE(RingPower), dummy))}, + begin + RingSize = ?RINGSIZE(RingPower), + HashIsInRing = ordsets:is_element(HashValue, BoundarySet), + HashIsPartitionBoundary = + riak_core_ring_util:hash_is_partition_boundary(HashValue, + RingSize), + equals(HashIsPartitionBoundary, HashIsInRing) + end + )). + +-endif. +-endif. \ No newline at end of file diff --git a/test/eqc/vclock_qc.erl b/test/pqc/vclock_qc.erl similarity index 100% rename from test/eqc/vclock_qc.erl rename to test/pqc/vclock_qc.erl diff --git a/test/eqc/worker_pool_pulse.erl b/test/pqc/worker_pool_pulse.erl similarity index 100% rename from test/eqc/worker_pool_pulse.erl rename to test/pqc/worker_pool_pulse.erl From ec73c8b846a8444d399e28077ee887eb7e4317b1 Mon Sep 17 00:00:00 2001 From: woelki Date: Wed, 8 Jul 2020 19:20:04 +0200 Subject: [PATCH 11/11] delete not needed sign --- test/pqc/riak_core_claim_statem.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pqc/riak_core_claim_statem.erl b/test/pqc/riak_core_claim_statem.erl index f683d7252..38d84ffa7 100644 --- a/test/pqc/riak_core_claim_statem.erl +++ b/test/pqc/riak_core_claim_statem.erl @@ -56,7 +56,7 @@ planning(S) -> claiming(S) -> [{planning, {call, ?MODULE, add_node, add_node_args(S)}}, - {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}].+ + {planning, {call, ?MODULE, leave_node, leave_node_args(S)}}]. %% -- Operations -------------------------------------------------------------