Tests are moved to rabbitmq-test
This commit is contained in:
parent
93d830ee5a
commit
acab9f2f00
|
|
@ -1,50 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(credit_flow_test).
|
||||
|
||||
-export([test_credit_flow_settings/0]).
|
||||
|
||||
test_credit_flow_settings() ->
|
||||
%% default values
|
||||
passed = test_proc(200, 50),
|
||||
|
||||
application:set_env(rabbit, credit_flow_default_credit, {100, 20}),
|
||||
passed = test_proc(100, 20),
|
||||
|
||||
application:unset_env(rabbit, credit_flow_default_credit),
|
||||
|
||||
% back to defaults
|
||||
passed = test_proc(200, 50),
|
||||
passed.
|
||||
|
||||
test_proc(InitialCredit, MoreCreditAfter) ->
|
||||
Pid = spawn(fun dummy/0),
|
||||
Pid ! {credit, self()},
|
||||
{InitialCredit, MoreCreditAfter} =
|
||||
receive
|
||||
{credit, Val} -> Val
|
||||
end,
|
||||
passed.
|
||||
|
||||
dummy() ->
|
||||
credit_flow:send(self()),
|
||||
receive
|
||||
{credit, From} ->
|
||||
From ! {credit, get(credit_flow_default_credit)};
|
||||
_ ->
|
||||
dummy()
|
||||
end.
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%% the License for the specific language governing rights and
|
||||
%% limitations under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(gm_qc).
|
||||
-ifdef(use_proper_qc).
|
||||
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-define(GROUP, test_group).
|
||||
-define(MAX_SIZE, 5).
|
||||
-define(MSG_TIMEOUT, 1000000). %% micros
|
||||
|
||||
-export([prop_gm_test/0]).
|
||||
|
||||
-behaviour(proper_statem).
|
||||
-export([initial_state/0, command/1, precondition/2, postcondition/3,
|
||||
next_state/3]).
|
||||
|
||||
-behaviour(gm).
|
||||
-export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]).
|
||||
|
||||
%% Helpers
|
||||
-export([do_join/0, do_leave/1, do_send/1, do_proceed1/1, do_proceed2/2]).
|
||||
|
||||
%% For insertion into gm
|
||||
-export([call/3, cast/2, monitor/1, demonitor/1, execute_mnesia_transaction/1]).
|
||||
|
||||
-record(state, {seq, %% symbolic and dynamic
|
||||
instrumented, %% dynamic only
|
||||
outstanding, %% dynamic only
|
||||
monitors, %% dynamic only
|
||||
all_join, %% for symbolic
|
||||
to_join, %% dynamic only
|
||||
to_leave %% for symbolic
|
||||
}).
|
||||
|
||||
prop_gm_test() ->
|
||||
case ?INSTR_MOD of
|
||||
?MODULE -> ok;
|
||||
_ -> exit(compile_with_INSTRUMENT_FOR_QC)
|
||||
end,
|
||||
process_flag(trap_exit, true),
|
||||
erlang:register(?MODULE, self()),
|
||||
?FORALL(Cmds, commands(?MODULE), gm_test(Cmds)).
|
||||
|
||||
gm_test(Cmds) ->
|
||||
{_H, State, Res} = run_commands(?MODULE, Cmds),
|
||||
cleanup(State),
|
||||
?WHENFAIL(
|
||||
io:format("Result: ~p~n", [Res]),
|
||||
aggregate(command_names(Cmds), Res =:= ok)).
|
||||
|
||||
cleanup(S) ->
|
||||
S2 = ensure_joiners_joined_and_msgs_received(S),
|
||||
All = gms_joined(S2),
|
||||
All = gms(S2), %% assertion - none to join
|
||||
check_stale_members(All),
|
||||
[gm:leave(GM) || GM <- All],
|
||||
drain_and_proceed_gms(S2),
|
||||
[await_death(GM) || GM <- All],
|
||||
gm:forget_group(?GROUP),
|
||||
ok.
|
||||
|
||||
check_stale_members(All) ->
|
||||
GMs = [P || P <- processes(), is_gm_process(?GROUP, P)],
|
||||
case GMs -- All of
|
||||
[] -> ok;
|
||||
Rest -> exit({forgot, Rest})
|
||||
end.
|
||||
|
||||
is_gm_process(Group, P) ->
|
||||
case process_info(P, dictionary) of
|
||||
undefined -> false;
|
||||
{dictionary, D} -> {gm, Group} =:= proplists:get_value(process_name, D)
|
||||
end.
|
||||
|
||||
await_death(P) ->
|
||||
MRef = erlang:monitor(process, P),
|
||||
await_death(MRef, P).
|
||||
|
||||
await_death(MRef, P) ->
|
||||
receive
|
||||
{'DOWN', MRef, process, P, _} -> ok;
|
||||
{'DOWN', _, _, _, _} -> await_death(MRef, P);
|
||||
{'EXIT', _, normal} -> await_death(MRef, P);
|
||||
{'EXIT', _, Reason} -> exit(Reason);
|
||||
{joined, _GM} -> await_death(MRef, P);
|
||||
{left, _GM} -> await_death(MRef, P);
|
||||
Anything -> exit({stray_msg, Anything})
|
||||
end.
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% proper_statem
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
initial_state() -> #state{seq = 1,
|
||||
outstanding = dict:new(),
|
||||
instrumented = dict:new(),
|
||||
monitors = dict:new(),
|
||||
all_join = sets:new(),
|
||||
to_join = sets:new(),
|
||||
to_leave = sets:new()}.
|
||||
|
||||
command(S) ->
|
||||
case {length(gms_symb_not_left(S)), length(gms_symb(S))} of
|
||||
{0, 0} -> qc_join(S);
|
||||
{0, _} -> frequency([{1, qc_join(S)},
|
||||
{3, qc_proceed1(S)},
|
||||
{5, qc_proceed2(S)}]);
|
||||
_ -> frequency([{1, qc_join(S)},
|
||||
{1, qc_leave(S)},
|
||||
{10, qc_send(S)},
|
||||
{5, qc_proceed1(S)},
|
||||
{15, qc_proceed2(S)}])
|
||||
end.
|
||||
|
||||
qc_join(_S) -> {call,?MODULE,do_join, []}.
|
||||
qc_leave(S) -> {call,?MODULE,do_leave,[oneof(gms_symb_not_left(S))]}.
|
||||
qc_send(S) -> {call,?MODULE,do_send, [oneof(gms_symb_not_left(S))]}.
|
||||
qc_proceed1(S) -> {call,?MODULE,do_proceed1, [oneof(gms_symb(S))]}.
|
||||
qc_proceed2(S) -> {call,?MODULE,do_proceed2, [oneof(gms_symb(S)),
|
||||
oneof(gms_symb(S))]}.
|
||||
|
||||
precondition(S, {call, ?MODULE, do_join, []}) ->
|
||||
length(gms_symb(S)) < ?MAX_SIZE;
|
||||
|
||||
precondition(_S, {call, ?MODULE, do_leave, [_GM]}) ->
|
||||
true;
|
||||
|
||||
precondition(_S, {call, ?MODULE, do_send, [_GM]}) ->
|
||||
true;
|
||||
|
||||
precondition(_S, {call, ?MODULE, do_proceed1, [_GM]}) ->
|
||||
true;
|
||||
|
||||
precondition(_S, {call, ?MODULE, do_proceed2, [GM1, GM2]}) ->
|
||||
GM1 =/= GM2.
|
||||
|
||||
postcondition(_S, {call, _M, _F, _A}, _Res) ->
|
||||
true.
|
||||
|
||||
next_state(S = #state{to_join = ToSet,
|
||||
all_join = AllSet}, GM, {call, ?MODULE, do_join, []}) ->
|
||||
S#state{to_join = sets:add_element(GM, ToSet),
|
||||
all_join = sets:add_element(GM, AllSet)};
|
||||
|
||||
next_state(S = #state{to_leave = Set}, _Res, {call, ?MODULE, do_leave, [GM]}) ->
|
||||
S#state{to_leave = sets:add_element(GM, Set)};
|
||||
|
||||
next_state(S = #state{seq = Seq,
|
||||
outstanding = Outstanding}, _Res,
|
||||
{call, ?MODULE, do_send, [GM]}) ->
|
||||
case is_pid(GM) andalso lists:member(GM, gms_joined(S)) of
|
||||
true ->
|
||||
%% Dynamic state, i.e. runtime
|
||||
Msg = [{sequence, Seq},
|
||||
{sent_to, GM},
|
||||
{dests, gms_joined(S)}],
|
||||
gm:broadcast(GM, Msg),
|
||||
Outstanding1 = dict:map(
|
||||
fun (_GM, Set) ->
|
||||
gb_sets:add_element(Msg, Set)
|
||||
end, Outstanding),
|
||||
drain(S#state{seq = Seq + 1,
|
||||
outstanding = Outstanding1});
|
||||
false ->
|
||||
S
|
||||
end;
|
||||
|
||||
next_state(S, _Res, {call, ?MODULE, do_proceed1, [Pid]}) ->
|
||||
proceed(Pid, S);
|
||||
|
||||
next_state(S, _Res, {call, ?MODULE, do_proceed2, [From, To]}) ->
|
||||
proceed({From, To}, S).
|
||||
|
||||
proceed(K, S = #state{instrumented = Msgs}) ->
|
||||
case dict:find(K, Msgs) of
|
||||
{ok, Q} -> case queue:out(Q) of
|
||||
{{value, Thing}, Q2} ->
|
||||
S2 = proceed(K, Thing, S),
|
||||
S2#state{instrumented = dict:store(K, Q2, Msgs)};
|
||||
{empty, _} ->
|
||||
S
|
||||
end;
|
||||
error -> S
|
||||
end.
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% GM
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
joined(Pid, _Members) -> Pid ! {joined, self()},
|
||||
ok.
|
||||
members_changed(_Pid, _Bs, _Ds) -> ok.
|
||||
handle_msg(Pid, _From, Msg) -> Pid ! {gm, self(), Msg}, ok.
|
||||
handle_terminate(Pid, _Reason) -> Pid ! {left, self()}.
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Helpers
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
do_join() ->
|
||||
{ok, GM} = gm:start_link(?GROUP, ?MODULE, self(),
|
||||
fun execute_mnesia_transaction/1),
|
||||
GM.
|
||||
|
||||
do_leave(GM) ->
|
||||
gm:leave(GM),
|
||||
GM.
|
||||
|
||||
%% We need to update the state, so do the work in next_state
|
||||
do_send( _GM) -> ok.
|
||||
do_proceed1(_Pid) -> ok.
|
||||
do_proceed2(_From, _To) -> ok.
|
||||
|
||||
%% All GMs, joined and to join
|
||||
gms(#state{outstanding = Outstanding,
|
||||
to_join = ToJoin}) ->
|
||||
dict:fetch_keys(Outstanding) ++ sets:to_list(ToJoin).
|
||||
|
||||
%% All GMs, joined and to join
|
||||
gms_joined(#state{outstanding = Outstanding}) ->
|
||||
dict:fetch_keys(Outstanding).
|
||||
|
||||
%% All GMs including those that have left (symbolic)
|
||||
gms_symb(#state{all_join = AllJoin}) ->
|
||||
sets:to_list(AllJoin).
|
||||
|
||||
%% All GMs not including those that have left (symbolic)
|
||||
gms_symb_not_left(#state{all_join = AllJoin,
|
||||
to_leave = ToLeave}) ->
|
||||
sets:to_list(sets:subtract(AllJoin, ToLeave)).
|
||||
|
||||
drain(S) ->
|
||||
receive
|
||||
Msg -> drain(handle_msg(Msg, S))
|
||||
after 10 -> S
|
||||
end.
|
||||
|
||||
drain_and_proceed_gms(S0) ->
|
||||
S = #state{instrumented = Msgs} = drain(S0),
|
||||
case dict:size(Msgs) of
|
||||
0 -> S;
|
||||
_ -> S1 = dict:fold(
|
||||
fun (Key, Q, Si) ->
|
||||
lists:foldl(
|
||||
fun (Msg, Sij) ->
|
||||
proceed(Key, Msg, Sij)
|
||||
end, Si, queue:to_list(Q))
|
||||
end, S, Msgs),
|
||||
drain_and_proceed_gms(S1#state{instrumented = dict:new()})
|
||||
end.
|
||||
|
||||
handle_msg({gm, GM, Msg}, S = #state{outstanding = Outstanding}) ->
|
||||
case dict:find(GM, Outstanding) of
|
||||
{ok, Set} ->
|
||||
Set2 = gb_sets:del_element(Msg, Set),
|
||||
S#state{outstanding = dict:store(GM, Set2, Outstanding)};
|
||||
error ->
|
||||
%% Message from GM that has already died. OK.
|
||||
S
|
||||
end;
|
||||
handle_msg({instrumented, Key, Thing}, S = #state{instrumented = Msgs}) ->
|
||||
Q1 = case dict:find(Key, Msgs) of
|
||||
{ok, Q} -> queue:in(Thing, Q);
|
||||
error -> queue:from_list([Thing])
|
||||
end,
|
||||
S#state{instrumented = dict:store(Key, Q1, Msgs)};
|
||||
handle_msg({joined, GM}, S = #state{outstanding = Outstanding,
|
||||
to_join = ToJoin}) ->
|
||||
S#state{outstanding = dict:store(GM, gb_sets:empty(), Outstanding),
|
||||
to_join = sets:del_element(GM, ToJoin)};
|
||||
handle_msg({left, GM}, S = #state{outstanding = Outstanding,
|
||||
to_join = ToJoin}) ->
|
||||
true = dict:is_key(GM, Outstanding) orelse sets:is_element(GM, ToJoin),
|
||||
S#state{outstanding = dict:erase(GM, Outstanding),
|
||||
to_join = sets:del_element(GM, ToJoin)};
|
||||
handle_msg({'DOWN', MRef, _, From, _} = Msg, S = #state{monitors = Mons}) ->
|
||||
To = dict:fetch(MRef, Mons),
|
||||
handle_msg({instrumented, {From, To}, {info, Msg}},
|
||||
S#state{monitors = dict:erase(MRef, Mons)});
|
||||
handle_msg({'EXIT', _From, normal}, S) ->
|
||||
S;
|
||||
handle_msg({'EXIT', _From, Reason}, _S) ->
|
||||
%% We just trapped exits to get nicer SASL logging.
|
||||
exit(Reason).
|
||||
|
||||
proceed({_From, To}, {cast, Msg}, S) -> gen_server2:cast(To, Msg), S;
|
||||
proceed({_From, To}, {info, Msg}, S) -> To ! Msg, S;
|
||||
proceed({From, _To}, {wait, Ref}, S) -> From ! {proceed, Ref}, S;
|
||||
proceed({From, To}, {mon, Ref}, S) -> add_monitor(From, To, Ref, S);
|
||||
proceed(_Pid, {demon, MRef}, S) -> erlang:demonitor(MRef), S;
|
||||
proceed(Pid, {wait, Ref}, S) -> Pid ! {proceed, Ref}, S.
|
||||
|
||||
%% NB From here is To in handle_msg/DOWN above, since the msg is going
|
||||
%% the other way
|
||||
add_monitor(From, To, Ref, S = #state{monitors = Mons}) ->
|
||||
MRef = erlang:monitor(process, To),
|
||||
From ! {mref, Ref, MRef},
|
||||
S#state{monitors = dict:store(MRef, From, Mons)}.
|
||||
|
||||
%% ----------------------------------------------------------------------------
|
||||
%% Assertions
|
||||
%% ----------------------------------------------------------------------------
|
||||
|
||||
ensure_joiners_joined_and_msgs_received(S0) ->
|
||||
S = drain_and_proceed_gms(S0),
|
||||
case outstanding_joiners(S) of
|
||||
true -> ensure_joiners_joined_and_msgs_received(S);
|
||||
false -> case outstanding_msgs(S) of
|
||||
[] -> S;
|
||||
Out -> exit({outstanding_msgs, Out})
|
||||
end
|
||||
end.
|
||||
|
||||
outstanding_joiners(#state{to_join = ToJoin}) ->
|
||||
sets:size(ToJoin) > 0.
|
||||
|
||||
outstanding_msgs(#state{outstanding = Outstanding}) ->
|
||||
dict:fold(fun (GM, Set, OS) ->
|
||||
case gb_sets:is_empty(Set) of
|
||||
true -> OS;
|
||||
false -> [{GM, gb_sets:to_list(Set)} | OS]
|
||||
end
|
||||
end, [], Outstanding).
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% For insertion into GM
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
call(Pid, Msg, infinity) ->
|
||||
Ref = make_ref(),
|
||||
whereis(?MODULE) ! {instrumented, {self(), Pid}, {wait, Ref}},
|
||||
receive
|
||||
{proceed, Ref} -> ok
|
||||
end,
|
||||
gen_server2:call(Pid, Msg, infinity).
|
||||
|
||||
cast(Pid, Msg) ->
|
||||
whereis(?MODULE) ! {instrumented, {self(), Pid}, {cast, Msg}},
|
||||
ok.
|
||||
|
||||
monitor(Pid) ->
|
||||
Ref = make_ref(),
|
||||
whereis(?MODULE) ! {instrumented, {self(), Pid}, {mon, Ref}},
|
||||
receive
|
||||
{mref, Ref, MRef} -> MRef
|
||||
end.
|
||||
|
||||
demonitor(MRef) ->
|
||||
whereis(?MODULE) ! {instrumented, self(), {demon, MRef}},
|
||||
true.
|
||||
|
||||
execute_mnesia_transaction(Fun) ->
|
||||
Ref = make_ref(),
|
||||
whereis(?MODULE) ! {instrumented, self(), {wait, Ref}},
|
||||
receive
|
||||
{proceed, Ref} -> ok
|
||||
end,
|
||||
rabbit_misc:execute_mnesia_transaction(Fun).
|
||||
|
||||
-else.
|
||||
|
||||
-export([prop_disabled/0]).
|
||||
|
||||
prop_disabled() ->
|
||||
exit({compiled_without_proper,
|
||||
"PropEr was not present during compilation of the test module. "
|
||||
"Hence all tests are disabled."}).
|
||||
|
||||
-endif.
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(gm_soak_test).
|
||||
|
||||
-export([test/0]).
|
||||
-export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]).
|
||||
|
||||
-behaviour(gm).
|
||||
|
||||
-include("gm_specs.hrl").
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Soak test
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
get_state() ->
|
||||
get(state).
|
||||
|
||||
with_state(Fun) ->
|
||||
put(state, Fun(get_state())).
|
||||
|
||||
inc() ->
|
||||
case 1 + get(count) of
|
||||
100000 -> Now = time_compat:monotonic_time(),
|
||||
Start = put(ts, Now),
|
||||
Diff = time_compat:convert_time_unit(Now - Start,
|
||||
native,
|
||||
micro_seconds),
|
||||
Rate = 100000 / (Diff / 1000000),
|
||||
io:format("~p seeing ~p msgs/sec~n", [self(), Rate]),
|
||||
put(count, 0);
|
||||
N -> put(count, N)
|
||||
end.
|
||||
|
||||
joined([], Members) ->
|
||||
io:format("Joined ~p (~p members)~n", [self(), length(Members)]),
|
||||
put(state, dict:from_list([{Member, empty} || Member <- Members])),
|
||||
put(count, 0),
|
||||
put(ts, time_compat:monotonic_time()),
|
||||
ok.
|
||||
|
||||
members_changed([], Births, Deaths) ->
|
||||
with_state(
|
||||
fun (State) ->
|
||||
State1 =
|
||||
lists:foldl(
|
||||
fun (Born, StateN) ->
|
||||
false = dict:is_key(Born, StateN),
|
||||
dict:store(Born, empty, StateN)
|
||||
end, State, Births),
|
||||
lists:foldl(
|
||||
fun (Died, StateN) ->
|
||||
true = dict:is_key(Died, StateN),
|
||||
dict:store(Died, died, StateN)
|
||||
end, State1, Deaths)
|
||||
end),
|
||||
ok.
|
||||
|
||||
handle_msg([], From, {test_msg, Num}) ->
|
||||
inc(),
|
||||
with_state(
|
||||
fun (State) ->
|
||||
ok = case dict:find(From, State) of
|
||||
{ok, died} ->
|
||||
exit({{from, From},
|
||||
{received_posthumous_delivery, Num}});
|
||||
{ok, empty} -> ok;
|
||||
{ok, Num} -> ok;
|
||||
{ok, Num1} when Num < Num1 ->
|
||||
exit({{from, From},
|
||||
{duplicate_delivery_of, Num},
|
||||
{expecting, Num1}});
|
||||
{ok, Num1} ->
|
||||
exit({{from, From},
|
||||
{received_early, Num},
|
||||
{expecting, Num1}});
|
||||
error ->
|
||||
exit({{from, From},
|
||||
{received_premature_delivery, Num}})
|
||||
end,
|
||||
dict:store(From, Num + 1, State)
|
||||
end),
|
||||
ok.
|
||||
|
||||
handle_terminate([], Reason) ->
|
||||
io:format("Left ~p (~p)~n", [self(), Reason]),
|
||||
ok.
|
||||
|
||||
spawn_member() ->
|
||||
spawn_link(
|
||||
fun () ->
|
||||
random:seed(erlang:phash2([node()]),
|
||||
time_compat:monotonic_time(),
|
||||
time_compat:unique_integer()),
|
||||
%% start up delay of no more than 10 seconds
|
||||
timer:sleep(random:uniform(10000)),
|
||||
{ok, Pid} = gm:start_link(
|
||||
?MODULE, ?MODULE, [],
|
||||
fun rabbit_misc:execute_mnesia_transaction/1),
|
||||
Start = random:uniform(10000),
|
||||
send_loop(Pid, Start, Start + random:uniform(10000)),
|
||||
gm:leave(Pid),
|
||||
spawn_more()
|
||||
end).
|
||||
|
||||
spawn_more() ->
|
||||
[spawn_member() || _ <- lists:seq(1, 4 - random:uniform(4))].
|
||||
|
||||
send_loop(_Pid, Target, Target) ->
|
||||
ok;
|
||||
send_loop(Pid, Count, Target) when Target > Count ->
|
||||
case random:uniform(3) of
|
||||
3 -> gm:confirmed_broadcast(Pid, {test_msg, Count});
|
||||
_ -> gm:broadcast(Pid, {test_msg, Count})
|
||||
end,
|
||||
timer:sleep(random:uniform(5) - 1), %% sleep up to 4 ms
|
||||
send_loop(Pid, Count + 1, Target).
|
||||
|
||||
test() ->
|
||||
ok = gm:create_tables(),
|
||||
spawn_member(),
|
||||
spawn_member().
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(gm_speed_test).
|
||||
|
||||
-export([test/3]).
|
||||
-export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]).
|
||||
-export([wile_e_coyote/2]).
|
||||
|
||||
-behaviour(gm).
|
||||
|
||||
-include("gm_specs.hrl").
|
||||
|
||||
%% callbacks
|
||||
|
||||
joined(Owner, _Members) ->
|
||||
Owner ! joined,
|
||||
ok.
|
||||
|
||||
members_changed(_Owner, _Births, _Deaths) ->
|
||||
ok.
|
||||
|
||||
handle_msg(Owner, _From, ping) ->
|
||||
Owner ! ping,
|
||||
ok.
|
||||
|
||||
handle_terminate(Owner, _Reason) ->
|
||||
Owner ! terminated,
|
||||
ok.
|
||||
|
||||
%% other
|
||||
|
||||
wile_e_coyote(Time, WriteUnit) ->
|
||||
{ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(),
|
||||
fun rabbit_misc:execute_mnesia_transaction/1),
|
||||
receive joined -> ok end,
|
||||
timer:sleep(1000), %% wait for all to join
|
||||
timer:send_after(Time, stop),
|
||||
Start = time_compat:monotonic_time(),
|
||||
{Sent, Received} = loop(Pid, WriteUnit, 0, 0),
|
||||
End = time_compat:monotonic_time(),
|
||||
ok = gm:leave(Pid),
|
||||
receive terminated -> ok end,
|
||||
Elapsed = time_compat:convert_time_unit(End - Start,
|
||||
native,
|
||||
micro_seconds) / 1000000,
|
||||
io:format("Sending rate: ~p msgs/sec~nReceiving rate: ~p msgs/sec~n~n",
|
||||
[Sent/Elapsed, Received/Elapsed]),
|
||||
ok.
|
||||
|
||||
loop(Pid, WriteUnit, Sent, Received) ->
|
||||
case read(Received) of
|
||||
{stop, Received1} -> {Sent, Received1};
|
||||
{ok, Received1} -> ok = write(Pid, WriteUnit),
|
||||
loop(Pid, WriteUnit, Sent + WriteUnit, Received1)
|
||||
end.
|
||||
|
||||
read(Count) ->
|
||||
receive
|
||||
ping -> read(Count + 1);
|
||||
stop -> {stop, Count}
|
||||
after 5 ->
|
||||
{ok, Count}
|
||||
end.
|
||||
|
||||
write(_Pid, 0) -> ok;
|
||||
write(Pid, N) -> ok = gm:broadcast(Pid, ping),
|
||||
write(Pid, N - 1).
|
||||
|
||||
test(Time, WriteUnit, Nodes) ->
|
||||
ok = gm:create_tables(),
|
||||
[spawn(Node, ?MODULE, wile_e_coyote, [Time, WriteUnit]) || Node <- Nodes].
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(gm_tests).
|
||||
|
||||
-export([test_join_leave/0,
|
||||
test_broadcast/0,
|
||||
test_confirmed_broadcast/0,
|
||||
test_member_death/0,
|
||||
test_receive_in_order/0,
|
||||
all_tests/0]).
|
||||
-export([joined/2, members_changed/3, handle_msg/3, handle_terminate/2]).
|
||||
|
||||
-behaviour(gm).
|
||||
|
||||
-include("gm_specs.hrl").
|
||||
|
||||
-define(RECEIVE_OR_THROW(Body, Bool, Error),
|
||||
receive Body ->
|
||||
true = Bool,
|
||||
passed
|
||||
after 1000 ->
|
||||
throw(Error)
|
||||
end).
|
||||
|
||||
joined(Pid, Members) ->
|
||||
Pid ! {joined, self(), Members},
|
||||
ok.
|
||||
|
||||
members_changed(Pid, Births, Deaths) ->
|
||||
Pid ! {members_changed, self(), Births, Deaths},
|
||||
ok.
|
||||
|
||||
handle_msg(Pid, From, Msg) ->
|
||||
Pid ! {msg, self(), From, Msg},
|
||||
ok.
|
||||
|
||||
handle_terminate(Pid, Reason) ->
|
||||
Pid ! {termination, self(), Reason},
|
||||
ok.
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Functional tests
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
all_tests() ->
|
||||
passed = test_join_leave(),
|
||||
passed = test_broadcast(),
|
||||
passed = test_confirmed_broadcast(),
|
||||
passed = test_member_death(),
|
||||
passed = test_receive_in_order(),
|
||||
passed.
|
||||
|
||||
test_join_leave() ->
|
||||
with_two_members(fun (_Pid, _Pid2) -> passed end).
|
||||
|
||||
test_broadcast() ->
|
||||
test_broadcast(fun gm:broadcast/2).
|
||||
|
||||
test_confirmed_broadcast() ->
|
||||
test_broadcast(fun gm:confirmed_broadcast/2).
|
||||
|
||||
test_member_death() ->
|
||||
with_two_members(
|
||||
fun (Pid, Pid2) ->
|
||||
{ok, Pid3} = gm:start_link(
|
||||
?MODULE, ?MODULE, self(),
|
||||
fun rabbit_misc:execute_mnesia_transaction/1),
|
||||
passed = receive_joined(Pid3, [Pid, Pid2, Pid3],
|
||||
timeout_joining_gm_group_3),
|
||||
passed = receive_birth(Pid, Pid3, timeout_waiting_for_birth_3_1),
|
||||
passed = receive_birth(Pid2, Pid3, timeout_waiting_for_birth_3_2),
|
||||
|
||||
unlink(Pid3),
|
||||
exit(Pid3, kill),
|
||||
|
||||
%% Have to do some broadcasts to ensure that all members
|
||||
%% find out about the death.
|
||||
passed = (test_broadcast_fun(fun gm:confirmed_broadcast/2))(
|
||||
Pid, Pid2),
|
||||
|
||||
passed = receive_death(Pid, Pid3, timeout_waiting_for_death_3_1),
|
||||
passed = receive_death(Pid2, Pid3, timeout_waiting_for_death_3_2),
|
||||
|
||||
passed
|
||||
end).
|
||||
|
||||
test_receive_in_order() ->
|
||||
with_two_members(
|
||||
fun (Pid, Pid2) ->
|
||||
Numbers = lists:seq(1,1000),
|
||||
[begin ok = gm:broadcast(Pid, N), ok = gm:broadcast(Pid2, N) end
|
||||
|| N <- Numbers],
|
||||
passed = receive_numbers(
|
||||
Pid, Pid, {timeout_for_msgs, Pid, Pid}, Numbers),
|
||||
passed = receive_numbers(
|
||||
Pid, Pid2, {timeout_for_msgs, Pid, Pid2}, Numbers),
|
||||
passed = receive_numbers(
|
||||
Pid2, Pid, {timeout_for_msgs, Pid2, Pid}, Numbers),
|
||||
passed = receive_numbers(
|
||||
Pid2, Pid2, {timeout_for_msgs, Pid2, Pid2}, Numbers),
|
||||
passed
|
||||
end).
|
||||
|
||||
test_broadcast(Fun) ->
|
||||
with_two_members(test_broadcast_fun(Fun)).
|
||||
|
||||
test_broadcast_fun(Fun) ->
|
||||
fun (Pid, Pid2) ->
|
||||
ok = Fun(Pid, magic_message),
|
||||
passed = receive_or_throw({msg, Pid, Pid, magic_message},
|
||||
timeout_waiting_for_msg),
|
||||
passed = receive_or_throw({msg, Pid2, Pid, magic_message},
|
||||
timeout_waiting_for_msg)
|
||||
end.
|
||||
|
||||
with_two_members(Fun) ->
|
||||
ok = gm:create_tables(),
|
||||
|
||||
{ok, Pid} = gm:start_link(?MODULE, ?MODULE, self(),
|
||||
fun rabbit_misc:execute_mnesia_transaction/1),
|
||||
passed = receive_joined(Pid, [Pid], timeout_joining_gm_group_1),
|
||||
|
||||
{ok, Pid2} = gm:start_link(?MODULE, ?MODULE, self(),
|
||||
fun rabbit_misc:execute_mnesia_transaction/1),
|
||||
passed = receive_joined(Pid2, [Pid, Pid2], timeout_joining_gm_group_2),
|
||||
passed = receive_birth(Pid, Pid2, timeout_waiting_for_birth_2),
|
||||
|
||||
passed = Fun(Pid, Pid2),
|
||||
|
||||
ok = gm:leave(Pid),
|
||||
passed = receive_death(Pid2, Pid, timeout_waiting_for_death_1),
|
||||
passed =
|
||||
receive_termination(Pid, normal, timeout_waiting_for_termination_1),
|
||||
|
||||
ok = gm:leave(Pid2),
|
||||
passed =
|
||||
receive_termination(Pid2, normal, timeout_waiting_for_termination_2),
|
||||
|
||||
receive X -> throw({unexpected_message, X})
|
||||
after 0 -> passed
|
||||
end.
|
||||
|
||||
receive_or_throw(Pattern, Error) ->
|
||||
?RECEIVE_OR_THROW(Pattern, true, Error).
|
||||
|
||||
receive_birth(From, Born, Error) ->
|
||||
?RECEIVE_OR_THROW({members_changed, From, Birth, Death},
|
||||
([Born] == Birth) andalso ([] == Death),
|
||||
Error).
|
||||
|
||||
receive_death(From, Died, Error) ->
|
||||
?RECEIVE_OR_THROW({members_changed, From, Birth, Death},
|
||||
([] == Birth) andalso ([Died] == Death),
|
||||
Error).
|
||||
|
||||
receive_joined(From, Members, Error) ->
|
||||
?RECEIVE_OR_THROW({joined, From, Members1},
|
||||
lists:usort(Members) == lists:usort(Members1),
|
||||
Error).
|
||||
|
||||
receive_termination(From, Reason, Error) ->
|
||||
?RECEIVE_OR_THROW({termination, From, Reason1},
|
||||
Reason == Reason1,
|
||||
Error).
|
||||
|
||||
receive_numbers(_Pid, _Sender, _Error, []) ->
|
||||
passed;
|
||||
receive_numbers(Pid, Sender, Error, [N | Numbers]) ->
|
||||
?RECEIVE_OR_THROW({msg, Pid, Sender, M},
|
||||
M == N,
|
||||
Error),
|
||||
receive_numbers(Pid, Sender, Error, Numbers).
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(mirrored_supervisor_tests).
|
||||
|
||||
-export([all_tests/0]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
-behaviour(mirrored_supervisor).
|
||||
|
||||
-define(MS, mirrored_supervisor).
|
||||
-define(SERVER, mirrored_supervisor_tests_gs).
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Functional tests
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
all_tests() ->
|
||||
passed = test_migrate(),
|
||||
passed = test_migrate_twice(),
|
||||
passed = test_already_there(),
|
||||
passed = test_delete_restart(),
|
||||
passed = test_which_children(),
|
||||
passed = test_large_group(),
|
||||
passed = test_childspecs_at_init(),
|
||||
passed = test_anonymous_supervisors(),
|
||||
passed = test_no_migration_on_shutdown(),
|
||||
passed = test_start_idempotence(),
|
||||
passed = test_unsupported(),
|
||||
passed = test_ignore(),
|
||||
passed = test_startup_failure(),
|
||||
passed.
|
||||
|
||||
%% Simplest test
|
||||
test_migrate() ->
|
||||
with_sups(fun([A, _]) ->
|
||||
?MS:start_child(a, childspec(worker)),
|
||||
Pid1 = pid_of(worker),
|
||||
kill_registered(A, Pid1),
|
||||
Pid2 = pid_of(worker),
|
||||
false = (Pid1 =:= Pid2)
|
||||
end, [a, b]).
|
||||
|
||||
%% Is migration transitive?
|
||||
test_migrate_twice() ->
|
||||
with_sups(fun([A, B]) ->
|
||||
?MS:start_child(a, childspec(worker)),
|
||||
Pid1 = pid_of(worker),
|
||||
kill_registered(A, Pid1),
|
||||
{ok, C} = start_sup(c),
|
||||
Pid2 = pid_of(worker),
|
||||
kill_registered(B, Pid2),
|
||||
Pid3 = pid_of(worker),
|
||||
false = (Pid1 =:= Pid3),
|
||||
kill(C)
|
||||
end, [a, b]).
|
||||
|
||||
%% Can't start the same child twice
|
||||
test_already_there() ->
|
||||
with_sups(fun([_, _]) ->
|
||||
S = childspec(worker),
|
||||
{ok, Pid} = ?MS:start_child(a, S),
|
||||
{error, {already_started, Pid}} = ?MS:start_child(b, S)
|
||||
end, [a, b]).
|
||||
|
||||
%% Deleting and restarting should work as per a normal supervisor
|
||||
test_delete_restart() ->
|
||||
with_sups(fun([_, _]) ->
|
||||
S = childspec(worker),
|
||||
{ok, Pid1} = ?MS:start_child(a, S),
|
||||
{error, running} = ?MS:delete_child(a, worker),
|
||||
ok = ?MS:terminate_child(a, worker),
|
||||
ok = ?MS:delete_child(a, worker),
|
||||
{ok, Pid2} = ?MS:start_child(b, S),
|
||||
false = (Pid1 =:= Pid2),
|
||||
ok = ?MS:terminate_child(b, worker),
|
||||
{ok, Pid3} = ?MS:restart_child(b, worker),
|
||||
Pid3 = pid_of(worker),
|
||||
false = (Pid2 =:= Pid3),
|
||||
%% Not the same supervisor as the worker is on
|
||||
ok = ?MS:terminate_child(a, worker),
|
||||
ok = ?MS:delete_child(a, worker),
|
||||
{ok, Pid4} = ?MS:start_child(a, S),
|
||||
false = (Pid3 =:= Pid4)
|
||||
end, [a, b]).
|
||||
|
||||
test_which_children() ->
|
||||
with_sups(
|
||||
fun([A, B] = Both) ->
|
||||
?MS:start_child(A, childspec(worker)),
|
||||
assert_wc(Both, fun ([C]) -> true = is_pid(wc_pid(C)) end),
|
||||
ok = ?MS:terminate_child(a, worker),
|
||||
assert_wc(Both, fun ([C]) -> undefined = wc_pid(C) end),
|
||||
{ok, _} = ?MS:restart_child(a, worker),
|
||||
assert_wc(Both, fun ([C]) -> true = is_pid(wc_pid(C)) end),
|
||||
?MS:start_child(B, childspec(worker2)),
|
||||
assert_wc(Both, fun (C) -> 2 = length(C) end)
|
||||
end, [a, b]).
|
||||
|
||||
assert_wc(Sups, Fun) ->
|
||||
[Fun(?MS:which_children(Sup)) || Sup <- Sups].
|
||||
|
||||
wc_pid(Child) ->
|
||||
{worker, Pid, worker, [mirrored_supervisor_tests]} = Child,
|
||||
Pid.
|
||||
|
||||
%% Not all the members of the group should actually do the failover
|
||||
test_large_group() ->
|
||||
with_sups(fun([A, _, _, _]) ->
|
||||
?MS:start_child(a, childspec(worker)),
|
||||
Pid1 = pid_of(worker),
|
||||
kill_registered(A, Pid1),
|
||||
Pid2 = pid_of(worker),
|
||||
false = (Pid1 =:= Pid2)
|
||||
end, [a, b, c, d]).
|
||||
|
||||
%% Do childspecs work when returned from init?
|
||||
test_childspecs_at_init() ->
|
||||
S = childspec(worker),
|
||||
with_sups(fun([A, _]) ->
|
||||
Pid1 = pid_of(worker),
|
||||
kill_registered(A, Pid1),
|
||||
Pid2 = pid_of(worker),
|
||||
false = (Pid1 =:= Pid2)
|
||||
end, [{a, [S]}, {b, [S]}]).
|
||||
|
||||
test_anonymous_supervisors() ->
|
||||
with_sups(fun([A, _B]) ->
|
||||
?MS:start_child(A, childspec(worker)),
|
||||
Pid1 = pid_of(worker),
|
||||
kill_registered(A, Pid1),
|
||||
Pid2 = pid_of(worker),
|
||||
false = (Pid1 =:= Pid2)
|
||||
end, [anon, anon]).
|
||||
|
||||
%% When a mirrored_supervisor terminates, we should not migrate, but
|
||||
%% the whole supervisor group should shut down. To test this we set up
|
||||
%% a situation where the gen_server will only fail if it's running
|
||||
%% under the supervisor called 'evil'. It should not migrate to
|
||||
%% 'good' and survive, rather the whole group should go away.
|
||||
test_no_migration_on_shutdown() ->
|
||||
with_sups(fun([Evil, _]) ->
|
||||
?MS:start_child(Evil, childspec(worker)),
|
||||
try
|
||||
call(worker, ping, 1000, 100),
|
||||
exit(worker_should_not_have_migrated)
|
||||
catch exit:{timeout_waiting_for_server, _, _} ->
|
||||
ok
|
||||
end
|
||||
end, [evil, good]).
|
||||
|
||||
test_start_idempotence() ->
|
||||
with_sups(fun([_]) ->
|
||||
CS = childspec(worker),
|
||||
{ok, Pid} = ?MS:start_child(a, CS),
|
||||
{error, {already_started, Pid}} = ?MS:start_child(a, CS),
|
||||
?MS:terminate_child(a, worker),
|
||||
{error, already_present} = ?MS:start_child(a, CS)
|
||||
end, [a]).
|
||||
|
||||
test_unsupported() ->
|
||||
try
|
||||
?MS:start_link({global, foo}, get_group(group), fun tx_fun/1, ?MODULE,
|
||||
{one_for_one, []}),
|
||||
exit(no_global)
|
||||
catch error:badarg ->
|
||||
ok
|
||||
end,
|
||||
try
|
||||
?MS:start_link({local, foo}, get_group(group), fun tx_fun/1, ?MODULE,
|
||||
{simple_one_for_one, []}),
|
||||
exit(no_sofo)
|
||||
catch error:badarg ->
|
||||
ok
|
||||
end,
|
||||
passed.
|
||||
|
||||
%% Just test we don't blow up
|
||||
test_ignore() ->
|
||||
?MS:start_link({local, foo}, get_group(group), fun tx_fun/1, ?MODULE,
|
||||
{fake_strategy_for_ignore, []}),
|
||||
passed.
|
||||
|
||||
test_startup_failure() ->
|
||||
[test_startup_failure(F) || F <- [want_error, want_exit]],
|
||||
passed.
|
||||
|
||||
test_startup_failure(Fail) ->
|
||||
process_flag(trap_exit, true),
|
||||
?MS:start_link(get_group(group), fun tx_fun/1, ?MODULE,
|
||||
{one_for_one, [childspec(Fail)]}),
|
||||
receive
|
||||
{'EXIT', _, shutdown} ->
|
||||
ok
|
||||
after 1000 ->
|
||||
exit({did_not_exit, Fail})
|
||||
end,
|
||||
process_flag(trap_exit, false).
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
with_sups(Fun, Sups) ->
|
||||
inc_group(),
|
||||
Pids = [begin {ok, Pid} = start_sup(Sup), Pid end || Sup <- Sups],
|
||||
Fun(Pids),
|
||||
[kill(Pid) || Pid <- Pids, is_process_alive(Pid)],
|
||||
timer:sleep(500),
|
||||
passed.
|
||||
|
||||
start_sup(Spec) ->
|
||||
start_sup(Spec, group).
|
||||
|
||||
start_sup({Name, ChildSpecs}, Group) ->
|
||||
{ok, Pid} = start_sup0(Name, get_group(Group), ChildSpecs),
|
||||
%% We are not a supervisor, when we kill the supervisor we do not
|
||||
%% want to die!
|
||||
unlink(Pid),
|
||||
{ok, Pid};
|
||||
|
||||
start_sup(Name, Group) ->
|
||||
start_sup({Name, []}, Group).
|
||||
|
||||
start_sup0(anon, Group, ChildSpecs) ->
|
||||
?MS:start_link(Group, fun tx_fun/1, ?MODULE,
|
||||
{one_for_one, ChildSpecs});
|
||||
|
||||
start_sup0(Name, Group, ChildSpecs) ->
|
||||
?MS:start_link({local, Name}, Group, fun tx_fun/1, ?MODULE,
|
||||
{one_for_one, ChildSpecs}).
|
||||
|
||||
childspec(Id) ->
|
||||
{Id,{?SERVER, start_link, [Id]}, transient, 16#ffffffff, worker, [?MODULE]}.
|
||||
|
||||
pid_of(Id) ->
|
||||
{received, Pid, ping} = call(Id, ping),
|
||||
Pid.
|
||||
|
||||
tx_fun(Fun) ->
|
||||
case mnesia:sync_transaction(Fun) of
|
||||
{atomic, Result} -> Result;
|
||||
{aborted, Reason} -> throw({error, Reason})
|
||||
end.
|
||||
|
||||
inc_group() ->
|
||||
Count = case get(counter) of
|
||||
undefined -> 0;
|
||||
C -> C
|
||||
end + 1,
|
||||
put(counter, Count).
|
||||
|
||||
get_group(Group) ->
|
||||
{Group, get(counter)}.
|
||||
|
||||
call(Id, Msg) -> call(Id, Msg, 10*1000, 100).
|
||||
|
||||
call(Id, Msg, 0, _Decr) ->
|
||||
exit({timeout_waiting_for_server, {Id, Msg}, erlang:get_stacktrace()});
|
||||
|
||||
call(Id, Msg, MaxDelay, Decr) ->
|
||||
try
|
||||
gen_server:call(Id, Msg, infinity)
|
||||
catch exit:_ -> timer:sleep(Decr),
|
||||
call(Id, Msg, MaxDelay - Decr, Decr)
|
||||
end.
|
||||
|
||||
kill(Pid) -> kill(Pid, []).
|
||||
kill(Pid, Wait) when is_pid(Wait) -> kill(Pid, [Wait]);
|
||||
kill(Pid, Waits) ->
|
||||
erlang:monitor(process, Pid),
|
||||
[erlang:monitor(process, P) || P <- Waits],
|
||||
exit(Pid, bang),
|
||||
kill_wait(Pid),
|
||||
[kill_wait(P) || P <- Waits].
|
||||
|
||||
kill_registered(Pid, Child) ->
|
||||
{registered_name, Name} = erlang:process_info(Child, registered_name),
|
||||
kill(Pid, Child),
|
||||
false = (Child =:= whereis(Name)),
|
||||
ok.
|
||||
|
||||
kill_wait(Pid) ->
|
||||
receive
|
||||
{'DOWN', _Ref, process, Pid, _Reason} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
init({fake_strategy_for_ignore, _ChildSpecs}) ->
|
||||
ignore;
|
||||
|
||||
init({Strategy, ChildSpecs}) ->
|
||||
{ok, {{Strategy, 0, 1}, ChildSpecs}}.
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(mirrored_supervisor_tests_gs).
|
||||
|
||||
%% Dumb gen_server we can supervise
|
||||
|
||||
-export([start_link/1]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3,
|
||||
handle_cast/2]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-define(MS, mirrored_supervisor).
|
||||
|
||||
start_link(want_error) ->
|
||||
{error, foo};
|
||||
|
||||
start_link(want_exit) ->
|
||||
exit(foo);
|
||||
|
||||
start_link(Id) ->
|
||||
gen_server:start_link({local, Id}, ?MODULE, [], []).
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
{ok, state}.
|
||||
|
||||
handle_call(Msg, _From, State) ->
|
||||
die_if_my_supervisor_is_evil(),
|
||||
{reply, {received, self(), Msg}, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
die_if_my_supervisor_is_evil() ->
|
||||
try lists:keysearch(self(), 2, ?MS:which_children(evil)) of
|
||||
false -> ok;
|
||||
_ -> exit(doooom)
|
||||
catch
|
||||
exit:{noproc, _} -> ok
|
||||
end.
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(on_disk_store_tunable_parameter_validation_test).
|
||||
|
||||
-include("rabbit.hrl").
|
||||
|
||||
-export([test_msg_store_parameter_validation/0]).
|
||||
|
||||
-define(T(Fun, Args), (catch apply(rabbit, Fun, Args))).
|
||||
|
||||
test_msg_store_parameter_validation() ->
|
||||
%% make sure it works with default values
|
||||
ok = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [?CREDIT_DISC_BOUND, ?IO_BATCH_SIZE]),
|
||||
|
||||
%% IO_BATCH_SIZE must be greater than CREDIT_DISC_BOUND initial credit
|
||||
ok = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 3000]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 1500]),
|
||||
|
||||
%% All values must be integers
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, "1500"]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{"2000", 500}, abc]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, "500"}, 2048]),
|
||||
|
||||
%% CREDIT_DISC_BOUND must be a tuple
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [[2000, 500], 1500]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [2000, 1500]),
|
||||
|
||||
%% config values can't be smaller than default values
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{1999, 500}, 2048]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 499}, 2048]),
|
||||
{error, _} = ?T(validate_msg_store_io_batch_size_and_credit_disc_bound, [{2000, 500}, 2047]),
|
||||
|
||||
passed.
|
||||
|
|
@ -1,473 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%% the License for the specific language governing rights and
|
||||
%% limitations under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_backing_queue_qc).
|
||||
-ifdef(use_proper_qc).
|
||||
-include("rabbit.hrl").
|
||||
-include("rabbit_framing.hrl").
|
||||
-include_lib("proper/include/proper.hrl").
|
||||
|
||||
-behaviour(proper_statem).
|
||||
|
||||
-define(BQMOD, rabbit_variable_queue).
|
||||
-define(QUEUE_MAXLEN, 10000).
|
||||
-define(TIMEOUT_LIMIT, 100).
|
||||
|
||||
-define(RECORD_INDEX(Key, Record),
|
||||
proplists:get_value(
|
||||
Key, lists:zip(record_info(fields, Record),
|
||||
lists:seq(2, record_info(size, Record))))).
|
||||
|
||||
-export([initial_state/0, command/1, precondition/2, postcondition/3,
|
||||
next_state/3]).
|
||||
|
||||
-export([prop_backing_queue_test/0, publish_multiple/1,
|
||||
timeout/2, bump_credit/1]).
|
||||
|
||||
-record(state, {bqstate,
|
||||
len, %% int
|
||||
next_seq_id, %% int
|
||||
messages, %% gb_trees of seqid => {msg_props, basic_msg}
|
||||
acks, %% [{acktag, {seqid, {msg_props, basic_msg}}}]
|
||||
confirms, %% set of msgid
|
||||
publishing}).%% int
|
||||
|
||||
%% Initialise model
|
||||
|
||||
initial_state() ->
|
||||
#state{bqstate = qc_variable_queue_init(qc_test_queue()),
|
||||
len = 0,
|
||||
next_seq_id = 0,
|
||||
messages = gb_trees:empty(),
|
||||
acks = [],
|
||||
confirms = gb_sets:new(),
|
||||
publishing = 0}.
|
||||
|
||||
%% Property
|
||||
|
||||
prop_backing_queue_test() ->
|
||||
?FORALL(Cmds, commands(?MODULE, initial_state()),
|
||||
backing_queue_test(Cmds)).
|
||||
|
||||
backing_queue_test(Cmds) ->
|
||||
{ok, FileSizeLimit} =
|
||||
application:get_env(rabbit, msg_store_file_size_limit),
|
||||
application:set_env(rabbit, msg_store_file_size_limit, 512,
|
||||
infinity),
|
||||
{ok, MaxJournal} =
|
||||
application:get_env(rabbit, queue_index_max_journal_entries),
|
||||
application:set_env(rabbit, queue_index_max_journal_entries, 128,
|
||||
infinity),
|
||||
|
||||
{_H, #state{bqstate = BQ}, Res} = run_commands(?MODULE, Cmds),
|
||||
|
||||
application:set_env(rabbit, msg_store_file_size_limit,
|
||||
FileSizeLimit, infinity),
|
||||
application:set_env(rabbit, queue_index_max_journal_entries,
|
||||
MaxJournal, infinity),
|
||||
|
||||
?BQMOD:delete_and_terminate(shutdown, BQ),
|
||||
?WHENFAIL(
|
||||
io:format("Result: ~p~n", [Res]),
|
||||
aggregate(command_names(Cmds), Res =:= ok)).
|
||||
|
||||
%% Commands
|
||||
|
||||
%% Command frequencies are tuned so that queues are normally
|
||||
%% reasonably short, but they may sometimes exceed
|
||||
%% ?QUEUE_MAXLEN. Publish-multiple and purging cause extreme queue
|
||||
%% lengths, so these have lower probabilities. Fetches/drops are
|
||||
%% sufficiently frequent so that commands that need acktags get decent
|
||||
%% coverage.
|
||||
|
||||
command(S) ->
|
||||
frequency([{10, qc_publish(S)},
|
||||
{1, qc_publish_delivered(S)},
|
||||
{1, qc_publish_multiple(S)}, %% very slow
|
||||
{9, qc_fetch(S)}, %% needed for ack and requeue
|
||||
{6, qc_drop(S)}, %%
|
||||
{15, qc_ack(S)},
|
||||
{15, qc_requeue(S)},
|
||||
{3, qc_set_ram_duration_target(S)},
|
||||
{1, qc_ram_duration(S)},
|
||||
{1, qc_drain_confirmed(S)},
|
||||
{1, qc_dropwhile(S)},
|
||||
{1, qc_is_empty(S)},
|
||||
{1, qc_timeout(S)},
|
||||
{1, qc_bump_credit(S)},
|
||||
{1, qc_purge(S)},
|
||||
{1, qc_fold(S)}]).
|
||||
|
||||
qc_publish(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, publish,
|
||||
[qc_message(),
|
||||
#message_properties{needs_confirming = frequency([{1, true},
|
||||
{20, false}]),
|
||||
expiry = oneof([undefined | lists:seq(1, 10)]),
|
||||
size = 10},
|
||||
false, self(), noflow, BQ]}.
|
||||
|
||||
qc_publish_multiple(#state{}) ->
|
||||
{call, ?MODULE, publish_multiple, [resize(?QUEUE_MAXLEN, pos_integer())]}.
|
||||
|
||||
qc_publish_delivered(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, publish_delivered,
|
||||
[qc_message(), #message_properties{size = 10}, self(), noflow, BQ]}.
|
||||
|
||||
qc_fetch(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, fetch, [boolean(), BQ]}.
|
||||
|
||||
qc_drop(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, drop, [boolean(), BQ]}.
|
||||
|
||||
qc_ack(#state{bqstate = BQ, acks = Acks}) ->
|
||||
{call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), BQ]}.
|
||||
|
||||
qc_requeue(#state{bqstate = BQ, acks = Acks}) ->
|
||||
{call, ?BQMOD, requeue, [rand_choice(proplists:get_keys(Acks)), BQ]}.
|
||||
|
||||
qc_set_ram_duration_target(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, set_ram_duration_target,
|
||||
[oneof([0, 1, 2, resize(1000, pos_integer()), infinity]), BQ]}.
|
||||
|
||||
qc_ram_duration(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, ram_duration, [BQ]}.
|
||||
|
||||
qc_drain_confirmed(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, drain_confirmed, [BQ]}.
|
||||
|
||||
qc_dropwhile(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, dropwhile, [fun dropfun/1, BQ]}.
|
||||
|
||||
qc_is_empty(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, is_empty, [BQ]}.
|
||||
|
||||
qc_timeout(#state{bqstate = BQ}) ->
|
||||
{call, ?MODULE, timeout, [BQ, ?TIMEOUT_LIMIT]}.
|
||||
|
||||
qc_bump_credit(#state{bqstate = BQ}) ->
|
||||
{call, ?MODULE, bump_credit, [BQ]}.
|
||||
|
||||
qc_purge(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, purge, [BQ]}.
|
||||
|
||||
qc_fold(#state{bqstate = BQ}) ->
|
||||
{call, ?BQMOD, fold, [makefoldfun(pos_integer()), foldacc(), BQ]}.
|
||||
|
||||
%% Preconditions
|
||||
|
||||
%% Create long queues by only allowing publishing
|
||||
precondition(#state{publishing = Count}, {call, _Mod, Fun, _Arg})
|
||||
when Count > 0, Fun /= publish ->
|
||||
false;
|
||||
precondition(#state{acks = Acks}, {call, ?BQMOD, Fun, _Arg})
|
||||
when Fun =:= ack; Fun =:= requeue ->
|
||||
length(Acks) > 0;
|
||||
precondition(#state{messages = Messages},
|
||||
{call, ?BQMOD, publish_delivered, _Arg}) ->
|
||||
gb_trees:is_empty(Messages);
|
||||
precondition(_S, {call, ?BQMOD, _Fun, _Arg}) ->
|
||||
true;
|
||||
precondition(_S, {call, ?MODULE, timeout, _Arg}) ->
|
||||
true;
|
||||
precondition(_S, {call, ?MODULE, bump_credit, _Arg}) ->
|
||||
true;
|
||||
precondition(#state{len = Len}, {call, ?MODULE, publish_multiple, _Arg}) ->
|
||||
Len < ?QUEUE_MAXLEN.
|
||||
|
||||
%% Model updates
|
||||
|
||||
next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Del, _Pid, _Flow, _BQ]}) ->
|
||||
#state{len = Len,
|
||||
messages = Messages,
|
||||
confirms = Confirms,
|
||||
publishing = PublishCount,
|
||||
next_seq_id = NextSeq} = S,
|
||||
MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]},
|
||||
NeedsConfirm =
|
||||
{call, erlang, element,
|
||||
[?RECORD_INDEX(needs_confirming, message_properties), MsgProps]},
|
||||
S#state{bqstate = BQ,
|
||||
len = Len + 1,
|
||||
next_seq_id = NextSeq + 1,
|
||||
messages = gb_trees:insert(NextSeq, {MsgProps, Msg}, Messages),
|
||||
publishing = {call, erlang, max, [0, {call, erlang, '-',
|
||||
[PublishCount, 1]}]},
|
||||
confirms = case eval(NeedsConfirm) of
|
||||
true -> gb_sets:add(MsgId, Confirms);
|
||||
_ -> Confirms
|
||||
end};
|
||||
|
||||
next_state(S, _BQ, {call, ?MODULE, publish_multiple, [PublishCount]}) ->
|
||||
S#state{publishing = PublishCount};
|
||||
|
||||
next_state(S, Res,
|
||||
{call, ?BQMOD, publish_delivered,
|
||||
[Msg, MsgProps, _Pid, _Flow, _BQ]}) ->
|
||||
#state{confirms = Confirms, acks = Acks, next_seq_id = NextSeq} = S,
|
||||
AckTag = {call, erlang, element, [1, Res]},
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
MsgId = {call, erlang, element, [?RECORD_INDEX(id, basic_message), Msg]},
|
||||
NeedsConfirm =
|
||||
{call, erlang, element,
|
||||
[?RECORD_INDEX(needs_confirming, message_properties), MsgProps]},
|
||||
S#state{bqstate = BQ1,
|
||||
next_seq_id = NextSeq + 1,
|
||||
confirms = case eval(NeedsConfirm) of
|
||||
true -> gb_sets:add(MsgId, Confirms);
|
||||
_ -> Confirms
|
||||
end,
|
||||
acks = [{AckTag, {NextSeq, {MsgProps, Msg}}}|Acks]
|
||||
};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) ->
|
||||
next_state_fetch_and_drop(S, Res, AckReq, 3);
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, drop, [AckReq, _BQ]}) ->
|
||||
next_state_fetch_and_drop(S, Res, AckReq, 2);
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _BQ]}) ->
|
||||
#state{acks = AcksState} = S,
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
S#state{bqstate = BQ1,
|
||||
acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, requeue, [AcksArg, _V]}) ->
|
||||
#state{messages = Messages, acks = AcksState} = S,
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
Messages1 = lists:foldl(fun (AckTag, Msgs) ->
|
||||
{SeqId, MsgPropsMsg} =
|
||||
proplists:get_value(AckTag, AcksState),
|
||||
gb_trees:insert(SeqId, MsgPropsMsg, Msgs)
|
||||
end, Messages, AcksArg),
|
||||
S#state{bqstate = BQ1,
|
||||
len = gb_trees:size(Messages1),
|
||||
messages = Messages1,
|
||||
acks = lists:foldl(fun proplists:delete/2, AcksState, AcksArg)};
|
||||
|
||||
next_state(S, BQ, {call, ?BQMOD, set_ram_duration_target, _Args}) ->
|
||||
S#state{bqstate = BQ};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, ram_duration, _Args}) ->
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
S#state{bqstate = BQ1};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, drain_confirmed, _Args}) ->
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
S#state{bqstate = BQ1};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) ->
|
||||
BQ = {call, erlang, element, [2, Res]},
|
||||
#state{messages = Messages} = S,
|
||||
Msgs1 = drop_messages(Messages),
|
||||
S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1};
|
||||
|
||||
next_state(S, _Res, {call, ?BQMOD, is_empty, _Args}) ->
|
||||
S;
|
||||
|
||||
next_state(S, BQ, {call, ?MODULE, timeout, _Args}) ->
|
||||
S#state{bqstate = BQ};
|
||||
next_state(S, BQ, {call, ?MODULE, bump_credit, _Args}) ->
|
||||
S#state{bqstate = BQ};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, purge, _Args}) ->
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()};
|
||||
|
||||
next_state(S, Res, {call, ?BQMOD, fold, _Args}) ->
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
S#state{bqstate = BQ1}.
|
||||
|
||||
%% Postconditions
|
||||
|
||||
postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) ->
|
||||
#state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S,
|
||||
case Res of
|
||||
{{MsgFetched, _IsDelivered, AckTag}, _BQ} ->
|
||||
{_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages),
|
||||
MsgFetched =:= Msg andalso
|
||||
not proplists:is_defined(AckTag, Acks) andalso
|
||||
not gb_sets:is_element(AckTag, Confrms) andalso
|
||||
Len =/= 0;
|
||||
{empty, _BQ} ->
|
||||
Len =:= 0
|
||||
end;
|
||||
|
||||
postcondition(S, {call, ?BQMOD, drop, _Args}, Res) ->
|
||||
#state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S,
|
||||
case Res of
|
||||
{{MsgIdFetched, AckTag}, _BQ} ->
|
||||
{_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages),
|
||||
MsgId = eval({call, erlang, element,
|
||||
[?RECORD_INDEX(id, basic_message), Msg]}),
|
||||
MsgIdFetched =:= MsgId andalso
|
||||
not proplists:is_defined(AckTag, Acks) andalso
|
||||
not gb_sets:is_element(AckTag, Confrms) andalso
|
||||
Len =/= 0;
|
||||
{empty, _BQ} ->
|
||||
Len =:= 0
|
||||
end;
|
||||
|
||||
postcondition(S, {call, ?BQMOD, publish_delivered, _Args}, {AckTag, _BQ}) ->
|
||||
#state{acks = Acks, confirms = Confrms} = S,
|
||||
not proplists:is_defined(AckTag, Acks) andalso
|
||||
not gb_sets:is_element(AckTag, Confrms);
|
||||
|
||||
postcondition(#state{len = Len}, {call, ?BQMOD, purge, _Args}, Res) ->
|
||||
{PurgeCount, _BQ} = Res,
|
||||
Len =:= PurgeCount;
|
||||
|
||||
postcondition(#state{len = Len}, {call, ?BQMOD, is_empty, _Args}, Res) ->
|
||||
(Len =:= 0) =:= Res;
|
||||
|
||||
postcondition(S, {call, ?BQMOD, drain_confirmed, _Args}, Res) ->
|
||||
#state{confirms = Confirms} = S,
|
||||
{ReportedConfirmed, _BQ} = Res,
|
||||
lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end,
|
||||
ReportedConfirmed);
|
||||
|
||||
postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) ->
|
||||
#state{messages = Messages} = S,
|
||||
{_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) ->
|
||||
{stop, Acc};
|
||||
({_SeqId, {MsgProps, Msg}}, {cont, Acc}) ->
|
||||
FoldFun(Msg, MsgProps, false, Acc)
|
||||
end, {cont, Acc0}, gb_trees:to_list(Messages)),
|
||||
true = Model =:= Res;
|
||||
|
||||
postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) ->
|
||||
?BQMOD:len(BQ) =:= Len.
|
||||
|
||||
%% Helpers
|
||||
|
||||
publish_multiple(_C) ->
|
||||
ok.
|
||||
|
||||
timeout(BQ, 0) ->
|
||||
BQ;
|
||||
timeout(BQ, AtMost) ->
|
||||
case ?BQMOD:needs_timeout(BQ) of
|
||||
false -> BQ;
|
||||
_ -> timeout(?BQMOD:timeout(BQ), AtMost - 1)
|
||||
end.
|
||||
|
||||
bump_credit(BQ) ->
|
||||
case credit_flow:blocked() of
|
||||
false -> BQ;
|
||||
true -> receive
|
||||
{bump_credit, Msg} ->
|
||||
credit_flow:handle_bump_msg(Msg),
|
||||
?BQMOD:resume(BQ)
|
||||
end
|
||||
end.
|
||||
|
||||
qc_message_payload() -> ?SIZED(Size, resize(Size * Size, binary())).
|
||||
|
||||
qc_routing_key() -> noshrink(binary(10)).
|
||||
|
||||
qc_delivery_mode() -> oneof([1, 2]).
|
||||
|
||||
qc_message() -> qc_message(qc_delivery_mode()).
|
||||
|
||||
qc_message(DeliveryMode) ->
|
||||
{call, rabbit_basic, message, [qc_default_exchange(),
|
||||
qc_routing_key(),
|
||||
#'P_basic'{delivery_mode = DeliveryMode},
|
||||
qc_message_payload()]}.
|
||||
|
||||
qc_default_exchange() ->
|
||||
{call, rabbit_misc, r, [<<>>, exchange, <<>>]}.
|
||||
|
||||
qc_variable_queue_init(Q) ->
|
||||
{call, ?BQMOD, init,
|
||||
[Q, new, function(2, {ok, []})]}.
|
||||
|
||||
qc_test_q() -> {call, rabbit_misc, r, [<<"/">>, queue, noshrink(binary(16))]}.
|
||||
|
||||
qc_test_queue() -> qc_test_queue(boolean()).
|
||||
|
||||
qc_test_queue(Durable) ->
|
||||
#amqqueue{name = qc_test_q(),
|
||||
durable = Durable,
|
||||
auto_delete = false,
|
||||
arguments = [],
|
||||
pid = self()}.
|
||||
|
||||
rand_choice([]) -> [];
|
||||
rand_choice(List) -> rand_choice(List, [], random:uniform(length(List))).
|
||||
|
||||
rand_choice(_List, Selection, 0) ->
|
||||
Selection;
|
||||
rand_choice(List, Selection, N) ->
|
||||
Picked = lists:nth(random:uniform(length(List)), List),
|
||||
rand_choice(List -- [Picked], [Picked | Selection],
|
||||
N - 1).
|
||||
|
||||
makefoldfun(Size) ->
|
||||
fun (Msg, _MsgProps, Unacked, Acc) ->
|
||||
case {length(Acc) > Size, Unacked} of
|
||||
{false, false} -> {cont, [Msg | Acc]};
|
||||
{false, true} -> {cont, Acc};
|
||||
{true, _} -> {stop, Acc}
|
||||
end
|
||||
end.
|
||||
foldacc() -> [].
|
||||
|
||||
dropfun(Props) ->
|
||||
Expiry = eval({call, erlang, element,
|
||||
[?RECORD_INDEX(expiry, message_properties), Props]}),
|
||||
Expiry =/= 1.
|
||||
|
||||
drop_messages(Messages) ->
|
||||
case gb_trees:is_empty(Messages) of
|
||||
true ->
|
||||
Messages;
|
||||
false -> {_Seq, MsgProps_Msg, M2} = gb_trees:take_smallest(Messages),
|
||||
MsgProps = {call, erlang, element, [1, MsgProps_Msg]},
|
||||
case dropfun(MsgProps) of
|
||||
true -> drop_messages(M2);
|
||||
false -> Messages
|
||||
end
|
||||
end.
|
||||
|
||||
next_state_fetch_and_drop(S, Res, AckReq, AckTagIdx) ->
|
||||
#state{len = Len, messages = Messages, acks = Acks} = S,
|
||||
ResultInfo = {call, erlang, element, [1, Res]},
|
||||
BQ1 = {call, erlang, element, [2, Res]},
|
||||
AckTag = {call, erlang, element, [AckTagIdx, ResultInfo]},
|
||||
S1 = S#state{bqstate = BQ1},
|
||||
case gb_trees:is_empty(Messages) of
|
||||
true -> S1;
|
||||
false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages),
|
||||
S2 = S1#state{len = Len - 1, messages = M2},
|
||||
case AckReq of
|
||||
true ->
|
||||
S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]};
|
||||
false ->
|
||||
S2
|
||||
end
|
||||
end.
|
||||
|
||||
-else.
|
||||
|
||||
-export([prop_disabled/0]).
|
||||
|
||||
prop_disabled() ->
|
||||
exit({compiled_without_proper,
|
||||
"PropEr was not present during compilation of the test module. "
|
||||
"Hence all tests are disabled."}).
|
||||
|
||||
-endif.
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%% the License for the specific language governing rights and
|
||||
%% limitations under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_runtime_parameters_test).
|
||||
-behaviour(rabbit_runtime_parameter).
|
||||
-behaviour(rabbit_policy_validator).
|
||||
|
||||
-include("rabbit.hrl").
|
||||
|
||||
-export([validate/5, notify/4, notify_clear/3]).
|
||||
-export([register/0, unregister/0]).
|
||||
-export([validate_policy/1]).
|
||||
-export([register_policy_validator/0, unregister_policy_validator/0]).
|
||||
|
||||
%----------------------------------------------------------------------------
|
||||
|
||||
register() ->
|
||||
rabbit_registry:register(runtime_parameter, <<"test">>, ?MODULE).
|
||||
|
||||
unregister() ->
|
||||
rabbit_registry:unregister(runtime_parameter, <<"test">>).
|
||||
|
||||
validate(_, <<"test">>, <<"good">>, _Term, _User) -> ok;
|
||||
validate(_, <<"test">>, <<"maybe">>, <<"good">>, _User) -> ok;
|
||||
validate(_, <<"test">>, <<"admin">>, _Term, none) -> ok;
|
||||
validate(_, <<"test">>, <<"admin">>, _Term, User) ->
|
||||
case lists:member(administrator, User#user.tags) of
|
||||
true -> ok;
|
||||
false -> {error, "meh", []}
|
||||
end;
|
||||
validate(_, <<"test">>, _, _, _) -> {error, "meh", []}.
|
||||
|
||||
notify(_, _, _, _) -> ok.
|
||||
notify_clear(_, _, _) -> ok.
|
||||
|
||||
%----------------------------------------------------------------------------
|
||||
|
||||
register_policy_validator() ->
|
||||
rabbit_registry:register(policy_validator, <<"testeven">>, ?MODULE),
|
||||
rabbit_registry:register(policy_validator, <<"testpos">>, ?MODULE).
|
||||
|
||||
unregister_policy_validator() ->
|
||||
rabbit_registry:unregister(policy_validator, <<"testeven">>),
|
||||
rabbit_registry:unregister(policy_validator, <<"testpos">>).
|
||||
|
||||
validate_policy([{<<"testeven">>, Terms}]) when is_list(Terms) ->
|
||||
case length(Terms) rem 2 =:= 0 of
|
||||
true -> ok;
|
||||
false -> {error, "meh", []}
|
||||
end;
|
||||
|
||||
validate_policy([{<<"testpos">>, Terms}]) when is_list(Terms) ->
|
||||
case lists:all(fun (N) -> is_integer(N) andalso N > 0 end, Terms) of
|
||||
true -> ok;
|
||||
false -> {error, "meh", []}
|
||||
end;
|
||||
|
||||
validate_policy(_) ->
|
||||
{error, "meh", []}.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,58 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%% the License for the specific language governing rights and
|
||||
%% limitations under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_tests_event_receiver).
|
||||
|
||||
-export([start/3, stop/0]).
|
||||
|
||||
-export([init/1, handle_call/2, handle_event/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("rabbit.hrl").
|
||||
|
||||
start(Pid, Nodes, Types) ->
|
||||
Oks = [ok || _ <- Nodes],
|
||||
{Oks, _} = rpc:multicall(Nodes, gen_event, add_handler,
|
||||
[rabbit_event, ?MODULE, [Pid, Types]]).
|
||||
|
||||
stop() ->
|
||||
gen_event:delete_handler(rabbit_event, ?MODULE, []).
|
||||
|
||||
%%----------------------------------------------------------------------------
|
||||
|
||||
init([Pid, Types]) ->
|
||||
{ok, {Pid, Types}}.
|
||||
|
||||
handle_call(_Request, State) ->
|
||||
{ok, not_understood, State}.
|
||||
|
||||
handle_event(Event = #event{type = Type}, State = {Pid, Types}) ->
|
||||
case lists:member(Type, Types) of
|
||||
true -> Pid ! Event;
|
||||
false -> ok
|
||||
end,
|
||||
{ok, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{ok, State}.
|
||||
|
||||
terminate(_Arg, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%----------------------------------------------------------------------------
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(supervisor2_tests).
|
||||
-behaviour(supervisor2).
|
||||
|
||||
-export([test_all/0, start_link/0]).
|
||||
-export([init/1]).
|
||||
|
||||
test_all() ->
|
||||
ok = check_shutdown(stop, 200, 200, 2000),
|
||||
ok = check_shutdown(ignored, 1, 2, 2000).
|
||||
|
||||
check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) ->
|
||||
{ok, Sup} = supervisor2:start_link(?MODULE, [SupTimeout]),
|
||||
Res = lists:foldl(
|
||||
fun (I, ok) ->
|
||||
TestSupPid = erlang:whereis(?MODULE),
|
||||
ChildPids =
|
||||
[begin
|
||||
{ok, ChildPid} =
|
||||
supervisor2:start_child(TestSupPid, []),
|
||||
ChildPid
|
||||
end || _ <- lists:seq(1, ChildCount)],
|
||||
MRef = erlang:monitor(process, TestSupPid),
|
||||
[P ! SigStop || P <- ChildPids],
|
||||
ok = supervisor2:terminate_child(Sup, test_sup),
|
||||
{ok, _} = supervisor2:restart_child(Sup, test_sup),
|
||||
receive
|
||||
{'DOWN', MRef, process, TestSupPid, shutdown} ->
|
||||
ok;
|
||||
{'DOWN', MRef, process, TestSupPid, Reason} ->
|
||||
{error, {I, Reason}}
|
||||
end;
|
||||
(_, R) ->
|
||||
R
|
||||
end, ok, lists:seq(1, Iterations)),
|
||||
unlink(Sup),
|
||||
MSupRef = erlang:monitor(process, Sup),
|
||||
exit(Sup, shutdown),
|
||||
receive
|
||||
{'DOWN', MSupRef, process, Sup, _Reason} ->
|
||||
ok
|
||||
end,
|
||||
Res.
|
||||
|
||||
start_link() ->
|
||||
Pid = spawn_link(fun () ->
|
||||
process_flag(trap_exit, true),
|
||||
receive stop -> ok end
|
||||
end),
|
||||
{ok, Pid}.
|
||||
|
||||
init([Timeout]) ->
|
||||
{ok, {{one_for_one, 0, 1},
|
||||
[{test_sup, {supervisor2, start_link,
|
||||
[{local, ?MODULE}, ?MODULE, []]},
|
||||
transient, Timeout, supervisor, [?MODULE]}]}};
|
||||
init([]) ->
|
||||
{ok, {{simple_one_for_one, 0, 1},
|
||||
[{test_worker, {?MODULE, start_link, []},
|
||||
temporary, 1000, worker, [?MODULE]}]}}.
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
%% the License for the specific language governing rights and
|
||||
%% limitations under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(test_sup).
|
||||
|
||||
-behaviour(supervisor2).
|
||||
|
||||
-export([test_supervisor_delayed_restart/0,
|
||||
init/1, start_child/0]).
|
||||
|
||||
%%----------------------------------------------------------------------------
|
||||
|
||||
-ifdef(use_specs).
|
||||
|
||||
-spec(test_supervisor_delayed_restart/0 :: () -> 'passed').
|
||||
|
||||
-endif.
|
||||
|
||||
%%----------------------------------------------------------------------------
|
||||
%% Public API
|
||||
%%----------------------------------------------------------------------------
|
||||
|
||||
test_supervisor_delayed_restart() ->
|
||||
passed = with_sup(simple_one_for_one,
|
||||
fun (SupPid) ->
|
||||
{ok, _ChildPid} =
|
||||
supervisor2:start_child(SupPid, []),
|
||||
test_supervisor_delayed_restart(SupPid)
|
||||
end),
|
||||
passed = with_sup(one_for_one, fun test_supervisor_delayed_restart/1).
|
||||
|
||||
test_supervisor_delayed_restart(SupPid) ->
|
||||
ok = ping_child(SupPid),
|
||||
ok = exit_child(SupPid),
|
||||
timer:sleep(100),
|
||||
ok = ping_child(SupPid),
|
||||
ok = exit_child(SupPid),
|
||||
timer:sleep(100),
|
||||
timeout = ping_child(SupPid),
|
||||
timer:sleep(1010),
|
||||
ok = ping_child(SupPid),
|
||||
passed.
|
||||
|
||||
with_sup(RestartStrategy, Fun) ->
|
||||
{ok, SupPid} = supervisor2:start_link(?MODULE, [RestartStrategy]),
|
||||
Res = Fun(SupPid),
|
||||
unlink(SupPid),
|
||||
exit(SupPid, shutdown),
|
||||
Res.
|
||||
|
||||
init([RestartStrategy]) ->
|
||||
{ok, {{RestartStrategy, 1, 1},
|
||||
[{test, {test_sup, start_child, []}, {permanent, 1},
|
||||
16#ffffffff, worker, [test_sup]}]}}.
|
||||
|
||||
start_child() ->
|
||||
{ok, proc_lib:spawn_link(fun run_child/0)}.
|
||||
|
||||
ping_child(SupPid) ->
|
||||
Ref = make_ref(),
|
||||
with_child_pid(SupPid, fun(ChildPid) -> ChildPid ! {ping, Ref, self()} end),
|
||||
receive {pong, Ref} -> ok
|
||||
after 1000 -> timeout
|
||||
end.
|
||||
|
||||
exit_child(SupPid) ->
|
||||
with_child_pid(SupPid, fun(ChildPid) -> exit(ChildPid, abnormal) end),
|
||||
ok.
|
||||
|
||||
with_child_pid(SupPid, Fun) ->
|
||||
case supervisor2:which_children(SupPid) of
|
||||
[{_Id, undefined, worker, [test_sup]}] -> ok;
|
||||
[{_Id, ChildPid, worker, [test_sup]}] -> Fun(ChildPid);
|
||||
[] -> ok
|
||||
end.
|
||||
|
||||
run_child() ->
|
||||
receive {ping, Ref, Pid} -> Pid ! {pong, Ref},
|
||||
run_child()
|
||||
end.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
%% The contents of this file are subject to the Mozilla Public License
|
||||
%% Version 1.1 (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.mozilla.org/MPL/
|
||||
%%
|
||||
%% Software distributed under the License is distributed on an "AS IS"
|
||||
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
||||
%% License for the specific language governing rights and limitations
|
||||
%% under the License.
|
||||
%%
|
||||
%% The Original Code is RabbitMQ.
|
||||
%%
|
||||
%% The Initial Developer of the Original Code is GoPivotal, Inc.
|
||||
%% Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(vm_memory_monitor_tests).
|
||||
|
||||
-export([all_tests/0]).
|
||||
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Tests
|
||||
%% ---------------------------------------------------------------------------
|
||||
|
||||
all_tests() ->
|
||||
lists:foreach(fun ({S, {K, V}}) ->
|
||||
{K, V} = vm_memory_monitor:parse_line_linux(S)
|
||||
end,
|
||||
[{"MemTotal: 0 kB", {'MemTotal', 0}},
|
||||
{"MemTotal: 502968 kB ", {'MemTotal', 515039232}},
|
||||
{"MemFree: 178232 kB", {'MemFree', 182509568}},
|
||||
{"MemTotal: 50296888", {'MemTotal', 50296888}},
|
||||
{"MemTotal 502968 kB", {'MemTotal', 515039232}},
|
||||
{"MemTotal 50296866 ", {'MemTotal', 50296866}}]),
|
||||
passed.
|
||||
Loading…
Reference in New Issue