diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/rabbit_backing_queue_qc.erl | 144 |
1 files changed, 83 insertions, 61 deletions
diff --git a/src/rabbit_backing_queue_qc.erl b/src/rabbit_backing_queue_qc.erl index 95832842dd..f5cd7f95b1 100644 --- a/src/rabbit_backing_queue_qc.erl +++ b/src/rabbit_backing_queue_qc.erl @@ -15,9 +15,14 @@ %% -module(rabbit_backing_queue_qc). +-include("rabbit.hrl"). +-include("rabbit_framing.hrl"). +-include_lib("proper/include/proper.hrl"). -behaviour(proper_statem). +-define(BQMOD, rabbit_variable_queue). + -export([initial_state/0, command/1, precondition/2, postcondition/3, next_state/3]). @@ -28,109 +33,125 @@ messages, acks}). --include("rabbit.hrl"). --include("rabbit_framing.hrl"). --include_lib("proper/include/proper.hrl"). initial_state() -> VQ = qc_variable_queue_init(qc_test_queue()), - #state{bqstate=VQ, messages = [], acks = []}. + #state{bqstate=VQ, messages = queue:new(), acks = []}. prop_backing_queue_test() -> ?FORALL(Cmds, commands(?MODULE, initial_state()), begin - {_H, _S, Res} = run_commands(?MODULE, Cmds), + {_H, #state{bqstate = VQ}, Res} = run_commands(?MODULE, Cmds), + rabbit_variable_queue:delete_and_terminate(shutdown, VQ), ?WHENFAIL( - io:format("Result: ~p~n", [Res]), - aggregate(command_names(Cmds), Res =:= ok)) + io:format("Result: ~p~n", [Res]), + aggregate(command_names(Cmds), Res =:= ok)) end). %% Commands -command(S) -> - frequency([{5, qc_publish(S)}, - {3, qc_fetch(S)}, - {2, qc_ack(S)}, - {2, qc_requeue(S)}, - {1, qc_ram(S)}]). +command(#state{bqstate = VQ} = S) -> + ?SIZED(Size, + frequency([{Size, qc_publish(S)}, + {Size, qc_fetch(S)}, + {Size, qc_ack(S)}, + {Size, qc_requeue(S)}, + {Size, qc_ram(S)}, + {1, {call, ?BQMOD, purge, [VQ]}}])). qc_publish(#state{bqstate = VQ}) -> - {call, rabbit_variable_queue, publish, - [qc_message(), #message_properties{}, self(), VQ]}. + {call, ?BQMOD, publish, + [qc_message(), #message_properties{}, self(), VQ]}. qc_fetch(#state{bqstate = VQ}) -> - {call, rabbit_variable_queue, fetch, [true, VQ]}. + {call, ?BQMOD, fetch, [boolean(), VQ]}. qc_ack(#state{bqstate = VQ, acks = Acks}) -> - {call, rabbit_variable_queue, ack, [sublist(Acks), VQ]}. + {call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), VQ]}. qc_requeue(#state{bqstate = VQ, acks = Acks}) -> - {call, rabbit_variable_queue, requeue, - [sublist(Acks), fun(MsgOpts) -> MsgOpts end, VQ]}. + {call, ?BQMOD, requeue, + [rand_choice(proplists:get_keys(Acks)), fun(MsgOpts) -> MsgOpts end, VQ]}. qc_ram(#state{bqstate = VQ}) -> - {call, rabbit_variable_queue, set_ram_duration_target, - [oneof([0, infinity]), VQ]}. + {call, ?BQMOD, set_ram_duration_target, + [oneof([0, infinity]), VQ]}. %% Preconditions -precondition(_S, {call, rabbit_variable_queue, Fun, _Arg}) - when Fun =:= publish; Fun =:= fetch; Fun =:= set_ram_duration_target -> - true; -precondition(#state{acks = Acks}, {call, rabbit_variable_queue, Fun, _Arg}) +precondition(#state{acks = Acks}, {call, ?BQMOD, Fun, _Arg}) when Fun =:= ack; Fun =:= requeue -> - length(Acks) > 0. + length(Acks) > 0; +precondition(_S, {call, ?BQMOD, _Fun, _Arg}) -> + true. %% Next state -next_state(S, VQ, {call, rabbit_variable_queue, publish, - [Msg, _MsgProps, _Pid, _VQ]}) -> +next_state(S, VQ, {call, ?BQMOD, publish, [Msg, _MsgProps, _Pid, _VQ]}) -> #state{messages = Messages} = S, - S#state{bqstate=VQ, messages= [Msg | Messages]}; - -next_state(S, Res, {call, rabbit_variable_queue, fetch, [AckReq, _VQ]}) -> - #state{messages = M, acks = Acks} = S, - ResultDetails = {call, erlang, element, [1, Res]}, - AckTag = {call, erlang, element, [3, ResultDetails]}, - VQ1 = {call, erlang, element, [2, Res]}, - S1 = S#state{bqstate = VQ1}, - case M of - [] -> S1; - [_|_] -> Msg = lists:last(M), - case AckReq of - true -> S1#state{messages = M -- [Msg], - acks = Acks ++ [AckTag]}; - false -> throw(non_ack_not_supported) - end + S#state{bqstate = VQ, messages = queue:in(Msg, Messages)}; + +next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _VQ]}) -> + #state{messages = Messages, acks = Acks} = S, + ResultInfo = {call, erlang, element, [1, Res]}, + VQ1 = {call, erlang, element, [2, Res]}, + AckTag = {call, erlang, element, [3, ResultInfo]}, + S1 = S#state{bqstate = VQ1}, + case queue:out(Messages) of + {empty, _M2} -> + S1; + {{value, Msg}, M2} -> + S2 = S1#state{messages = M2}, + case AckReq of + true -> S2#state{acks = Acks ++ [{AckTag, Msg}]}; + false -> S2 + end end; -next_state(S, Res, {call, rabbit_variable_queue, ack, [AcksArg, _VQ]}) -> +next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _VQ]}) -> #state{acks = AcksState} = S, VQ1 = {call, erlang, element, [2, Res]}, S#state{bqstate = VQ1, - acks = AcksState -- AcksArg}; + acks = propvals_by_keys(AcksState, AcksArg)}; -next_state(S, Res, {call, rabbit_variable_queue, requeue, [AcksArg, _F, _V]}) -> +next_state(S, Res, {call, ?BQMOD, requeue, [AcksArg, _F, _V]}) -> #state{messages = Messages, acks = AcksState} = S, VQ1 = {call, erlang, element, [2, Res]}, + RequeueMsgs = [proplists:get_value(Key, AcksState) || Key <- AcksArg ], S#state{bqstate = VQ1, - messages = AcksArg ++ Messages, - acks = AcksState -- AcksArg}; + messages = queue:join(Messages, queue:from_list(RequeueMsgs)), + acks = propvals_by_keys(AcksState, AcksArg)}; + +next_state(S, VQ, {call, ?BQMOD, set_ram_duration_target, _A}) -> + S#state{bqstate = VQ}; -next_state(S, VQ, {call, rabbit_variable_queue, set_ram_duration_target, _A}) -> - S#state{bqstate = VQ}. +next_state(S, Res, {call, ?BQMOD, purge, _A}) -> + VQ1 = {call, erlang, element, [2, Res]}, + S#state{bqstate = VQ1, messages = queue:new()}. %% Postconditions +postcondition(#state{messages = Messages}, {call, ?BQMOD, fetch, _Args}, Res) -> + case Res of + {{MsgFetched, _IsDelivered, _AckTag, _Remaining_Len}, _VQ} -> + MsgFetched =:= queue:head(Messages); + {empty, _VQ} -> + queue:len(Messages) =:= 0 + end; + +postcondition(#state{messages = Messages}, {call, ?BQMOD, purge, _Args}, Res) -> + {PurgeCount, _VQ} = Res, + queue:len(Messages) =:= PurgeCount; + postcondition(#state{bqstate = VQ, messages = Messages}, - {call, _Mod, _Fun, _Args}, _Res) -> - rabbit_variable_queue:len(VQ) =:= length(Messages). + {call, ?BQMOD, _Fun, _Args}, _Res) -> + ?BQMOD:len(VQ) =:= queue:len(Messages). %% Helpers qc_message_payload() -> - binary(). + ?SIZED(Size, resize(Size * Size, binary())). qc_routing_key() -> noshrink(binary(10)). @@ -152,7 +173,7 @@ qc_default_exchange() -> {call, rabbit_misc, r, [<<>>, exchange, <<>>]}. qc_variable_queue_init(Q) -> - {call, rabbit_variable_queue, init, + {call, ?BQMOD, init, [Q, false, nop(2), nop(2), nop(2), nop(1)]}. qc_test_q() -> @@ -170,12 +191,13 @@ qc_test_queue(Durable) -> nop(N) -> function(N, ok). -sublist(List) -> +propvals_by_keys(Props, Keys) -> + lists:filter(fun ({Key, _Msg}) -> + not lists:member(Key, Keys) + end, Props). + +rand_choice(List) -> case List of [] -> []; - _ -> Item = lists:nth(random:uniform(length(List)), List), - case random:uniform(3) of - 1 -> [Item]; - _ -> [Item | sublist(List -- [Item])] - end + _ -> [lists:nth(random:uniform(length(List)), List)] end. |
