Skip to content

Commit

Permalink
add QC tests for handling linked process exits
Browse files Browse the repository at this point in the history
  • Loading branch information
argv0 committed Apr 13, 2011
1 parent f6ba8db commit 2a5ab81
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 8 deletions.
27 changes: 21 additions & 6 deletions test/core_vnode_eqc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@

-record(qcst, {started,
counters, % Dict of counters for each index
indices}).
indices,
crash_reasons}).

simple_test() ->
simple_test(100).
Expand Down Expand Up @@ -82,27 +83,35 @@ initial_state_data() ->
riak_core_ring_manager:set_ring_global(Ring),
#qcst{started=[],
counters=orddict:new(),
crash_reasons=orddict:new(),
indices=[I || {I,_N} <- riak_core_ring:all_owners(Ring)]
}.

%% Mark the vnode as started
next_state_data(_From,_To,S=#qcst{started=Started,
counters=Counters},_R,
counters=Counters,
crash_reasons=CRs},_R,
{call,?MODULE,start_vnode,[Index]}) ->
S#qcst{started=[Index|Started],
counters=orddict:store(Index, 0, Counters)};
next_state_data(_From,_To,S=#qcst{counters=Counters},_R,
counters=orddict:store(Index, 0, Counters),
crash_reasons=orddict:store(Index, undefined, CRs)};
next_state_data(_From,_To,S=#qcst{counters=Counters, crash_reasons=CRs},_R,
{call,mock_vnode,stop,[{Index,_Node}]}) ->
%% If a node is stopped, reset the counter ready for next
%% time it is called which should start it
S#qcst{counters=orddict:store(Index, 0, Counters)};
S#qcst{counters=orddict:store(Index, 0, Counters),
crash_reasons=orddict:store(Index, undefined, CRs)};
%% Update the counters for the index if a command that changes them
next_state_data(_From,_To,S=#qcst{counters=Counters},_R,
{call,_Mod,Func,[Preflist]})
when Func =:= neverreply; Func =:= returnreply; Func =:= latereply ->
S#qcst{counters=lists:foldl(fun({I, _N}, C) ->
orddict:update_counter(I, 1, C)
end, Counters, Preflist)};
%% Update the counters for the index if a command that changes them
next_state_data(_From,_To,S=#qcst{crash_reasons=CRs},_R,
{call,mock_vnode,crash,[{Index,_Node}]}) ->
S#qcst{crash_reasons=orddict:store(Index, Index, CRs)};
next_state_data(_From,_To,S,_R,_C) ->
S.
%
Expand All @@ -115,6 +124,8 @@ running(S) ->
{history, {call,?MODULE,start_vnode,[index(S)]}},
{history, {call,mock_vnode,get_index,[active_preflist1(S)]}},
{history, {call,mock_vnode,get_counter,[active_preflist1(S)]}},
{history, {call,mock_vnode,crash,[active_preflist1(S)]}},
{history, {call,mock_vnode,get_crash_reason,[active_preflist1(S)]}},
{history, {call,mock_vnode,neverreply,[active_preflist(S)]}},
{history, {call,?MODULE,returnreply,[active_preflist(S)]}},
{history, {call,?MODULE,latereply,[active_preflist(S)]}},
Expand All @@ -127,14 +138,17 @@ precondition(_From,_To,#qcst{started=Started},{call,?MODULE,start_vnode,[Index]}
not lists:member(Index, Started);
precondition(_From,_To,#qcst{started=Started},{call,_Mod,Func,[Preflist]})
when Func =:= get_index; Func =:= get_counter; Func =:= neverreply; Func =:= returnreply;
Func =:= latereply ->
Func =:= latereply; Func =:= crash; Func =:= get_crash_reason ->
preflist_is_active(Preflist, Started);
precondition(_From,_To,_S,_C) ->
true.

postcondition(_From,_To,_S,
{call,mock_vnode,get_index,[{Index,_Node}]},{ok,ReplyIndex}) ->
Index =:= ReplyIndex;
postcondition(_From,_To,#qcst{crash_reasons=CRs},
{call,mock_vnode,get_crash_reason,[{Index,_Node}]},{ok, Reason}) ->
orddict:fetch(Index, CRs) =:= Reason;
postcondition(_From,_To,#qcst{counters=Counters},
{call,mock_vnode,get_counter,[{Index,_Node}]},{ok,ReplyCount}) ->
orddict:fetch(Index, Counters) =:= ReplyCount;
Expand Down Expand Up @@ -223,3 +237,4 @@ wait_for_pid(Pid) ->
end.

-endif.

20 changes: 18 additions & 2 deletions test/mock_vnode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@
neverreply/1,
returnreply/1,
latereply/1,
crash/1,
get_crash_reason/1,
stop/1]).
-export([init/1,
handle_command/3,
terminate/2]).
terminate/2,
handle_exit/3]).

-record(state, {index, counter}).
-record(state, {index, counter, crash_reason}).

-define(MASTER, mock_vnode_master).

Expand Down Expand Up @@ -64,6 +67,12 @@ latereply(Preflist) ->
riak_core_vnode_master:command(Preflist, latereply, {raw, Ref, self()}, ?MASTER),
{ok, Ref}.

crash(Preflist) ->
riak_core_vnode_master:sync_command(Preflist, crash, ?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).

Expand All @@ -77,7 +86,12 @@ 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(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,...}
Expand All @@ -94,6 +108,8 @@ handle_command(latereply, Sender, State = #state{counter=Counter}) ->
riak_core_vnode:reply(Sender, latereply)
end),
{noreply, State#state{counter = Counter + 1}}.
handle_exit(_Pid, Reason, State) ->
{noreply, State#state{crash_reason=Reason}}.

terminate(_Reason, _State) ->
ok.

0 comments on commit 2a5ab81

Please sign in to comment.