rabbitmq-server/test/gm_SUITE.erl

251 lines
8.1 KiB
Erlang

%% 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_SUITE).
-behaviour(gm).
-include_lib("common_test/include/ct.hrl").
-include("gm_specs.hrl").
-compile(export_all).
-define(RECEIVE_OR_THROW(Body, Bool, Error),
receive Body ->
true = Bool,
passed
after 1000 ->
throw(Error)
end).
all() ->
[
join_leave,
broadcast,
confirmed_broadcast,
member_death,
receive_in_order,
unexpected_msg,
down_in_members_change
].
init_per_suite(Config) ->
ok = application:set_env(mnesia, dir, ?config(priv_dir, Config)),
ok = application:start(mnesia),
{ok, FHC} = file_handle_cache:start_link(),
unlink(FHC),
{ok, WPS} = worker_pool_sup:start_link(),
unlink(WPS),
rabbit_ct_helpers:set_config(Config, [
{file_handle_cache_pid, FHC},
{worker_pool_sup_pid, WPS}
]).
end_per_suite(Config) ->
exit(?config(worker_pool_sup_pid, Config), shutdown),
exit(?config(file_handle_cache_pid, Config), shutdown),
ok = application:stop(mnesia),
Config.
%% ---------------------------------------------------------------------------
%% Functional tests
%% ---------------------------------------------------------------------------
join_leave(_Config) ->
passed = with_two_members(fun (_Pid, _Pid2) -> passed end).
broadcast(_Config) ->
passed = do_broadcast(fun gm:broadcast/2).
confirmed_broadcast(_Config) ->
passed = do_broadcast(fun gm:confirmed_broadcast/2).
member_death(_Config) ->
passed = 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 = (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).
receive_in_order(_Config) ->
passed = 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).
unexpected_msg(_Config) ->
passed = with_two_members(
fun(Pid, _) ->
Pid ! {make_ref(), old_gen_server_answer},
true = erlang:is_process_alive(Pid),
passed
end).
down_in_members_change(_Config) ->
%% Setup
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),
%% Test. Simulate that the gm group is deleted (forget_group) while
%% processing the 'DOWN' message from the neighbour
process_flag(trap_exit, true),
ok = meck:new(mnesia, [passthrough]),
ok = meck:expect(mnesia, read, fun({gm_group, ?MODULE}) ->
[];
(Key) ->
meck:passthrough([Key])
end),
gm:leave(Pid2),
Passed = receive
{'EXIT', Pid, shutdown} ->
passed;
{'EXIT', Pid, _} ->
crashed
after 15000 ->
timeout
end,
%% Cleanup
meck:unload(mnesia),
process_flag(trap_exit, false),
passed = Passed.
do_broadcast(Fun) ->
with_two_members(broadcast_fun(Fun)).
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).
%% -------------------------------------------------------------------
%% gm behavior callbacks.
%% -------------------------------------------------------------------
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.