Skip to content

Commit

Permalink
Merge pull request #741 from esl/parallel_bosh_SUITE
Browse files Browse the repository at this point in the history
Parallel bosh suite
  • Loading branch information
chrzaszcz committed Apr 20, 2016
2 parents bf4a8fc + 5ac6e00 commit bfa2b38
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 117 deletions.
47 changes: 1 addition & 46 deletions apps/ejabberd/src/mod_bosh.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
-export([start/2,
stop/1]).

%% ejabberd independent listener callbacks
-export([socket_type/0,
start_listener/2]).

%% cowboy_loop_handler callbacks
-export([init/3,
info/3,
Expand All @@ -40,7 +36,6 @@
-include_lib("exml/include/exml_stream.hrl").
-include("mod_bosh.hrl").

-define(LISTENER, ?MODULE).
-define(DEFAULT_BACKEND, mnesia).
-define(DEFAULT_MAX_AGE, 1728000). %% 20 days in seconds
-define(DEFAULT_INACTIVITY, 30). %% seconds
Expand Down Expand Up @@ -126,12 +121,6 @@ set_server_acks(EnableServerAcks) ->
-spec start(ejabberd:server(), [option()]) -> any().
start(_Host, Opts) ->
try
case gen_mod:get_opt(port, Opts, undefined) of
undefined ->
ok;
Port ->
ok = start_cowboy(Port, Opts)
end,
ok = start_backend(Opts),
{ok, _Pid} = mod_bosh_socket:start_supervisor(),
ejabberd_hooks:add(node_cleanup, global, ?MODULE, node_cleanup, 50)
Expand All @@ -141,25 +130,7 @@ start(_Host, Opts) ->
end.

stop(_Host) ->
%% TODO: stop backend and supervisor
cowboy:stop_listener(?LISTENER).

%%--------------------------------------------------------------------
%% ejabberd independent listener callbacks
%%--------------------------------------------------------------------

socket_type() ->
independent.

%% @doc Start the module. Called from `ejabberd_listener'.
%% If the option `ip' is undefined, then `InetAddr' is `{0,0,0,0}'.
-spec start_listener({Port :: inet:port_number(),
InetAddr :: inet:ip_address(),
tcp}, Opts :: proplists:proplist()) -> any().
start_listener({Port, InetAddr, tcp}, Opts) ->
OptsWPort = lists:keystore(port, 1, [{ip,InetAddr}|Opts], {port, Port}),
gen_mod:start_module(?MYNAME, ?MODULE, OptsWPort).

ok.
%%--------------------------------------------------------------------
%% Hooks handlers
%%--------------------------------------------------------------------
Expand Down Expand Up @@ -253,22 +224,6 @@ terminate(_Reason, _Req, _State) ->
%% Callbacks implementation
%%--------------------------------------------------------------------

-spec start_cowboy(inet:port_number(), [option()]) -> 'ok' | {'error','badarg'}.
start_cowboy(Port, Opts) ->
Host = proplists:get_value(host, Opts, '_'),
Prefix = proplists:get_value(prefix, Opts, "/http-bind"),
NumAcceptors = proplists:get_value(num_acceptors, Opts, 100),
Dispatch = cowboy_router:compile([{Host, [{Prefix, ?MODULE, Opts}] }]),
TransOpts = [{port, Port}|get_option_pair(ip, Opts)],
ProtoOpts = [{env, [{dispatch, Dispatch}]}],
case cowboy:start_http(?LISTENER, NumAcceptors, TransOpts, ProtoOpts) of
{ok, _Pid} ->
ok;
{error, Reason} ->
{error, Reason}
end.


-spec start_backend([option()]) -> 'ok'.
start_backend(Opts) ->
Backend = proplists:get_value(backend, Opts, ?DEFAULT_BACKEND),
Expand Down
105 changes: 34 additions & 71 deletions test/ejabberd_tests/tests/bosh_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
%%--------------------------------------------------------------------

-define(INACTIVITY, 2). %% seconds
-define(MAX_WAIT, 1). %% seconds
-define(MAX_WAIT, 5). %% seconds
-define(INVALID_RID_OFFSET, 999).

all() ->
Expand All @@ -38,15 +38,16 @@ all() ->
{group, acks},

{group, essential_https},
{group, chat_https}].
{group, chat_https}
].

groups() ->
[{essential, [shuffle, {repeat_until_any_fail,10}], essential_test_cases()},
{essential_https, [shuffle, {repeat_until_any_fail,10}], essential_test_cases()},
{chat, [shuffle, {repeat_until_any_fail,10}], chat_test_cases()},
{chat_https, [shuffle, {repeat_until_any_fail,10}], chat_test_cases()},
{time, [shuffle, {repeat_until_any_fail,10}], time_test_cases()},
{acks, [shuffle, {repeat_until_any_fail,5}], acks_test_cases()}].
[{essential, [shuffle], essential_test_cases()},
{essential_https, [shuffle], essential_test_cases()},
{chat, [shuffle], chat_test_cases()},
{chat_https, [shuffle], chat_test_cases()},
{time, [parallel], time_test_cases()},
{acks, [shuffle], acks_test_cases()}].

suite() ->
escalus:suite().
Expand Down Expand Up @@ -94,9 +95,12 @@ init_per_suite(Config) ->
escalus:init_per_suite([{escalus_user_db, {module, escalus_ejabberd}} | Config]).

end_per_suite(Config) ->
escalus_fresh:clean(),
escalus:end_per_suite(Config).


init_per_group(time, Config) ->
NewConfig = escalus_ejabberd:setup_option(max_wait(), Config),
escalus_ejabberd:setup_option(inactivity(), NewConfig);
init_per_group(essential, Config) ->
[{user, carol} | Config];
init_per_group(essential_https, Config) ->
Expand All @@ -108,61 +112,24 @@ init_per_group(_GroupName, Config) ->
Config1 = escalus:create_users(Config, escalus:get_users([carol, carol_s, geralt, alice])),
[{user, carol} | Config1].

end_per_group(time, Config) ->
NewConfig = escalus_ejabberd:reset_option(max_wait(), Config),
escalus_ejabberd:reset_option(inactivity(), NewConfig);
end_per_group(GroupName, Config)
when GroupName =:= essential; GroupName =:= essential_https ->
Config;
end_per_group(_GroupName, Config) ->
R = escalus:delete_users(Config, escalus:get_users([carol, carol_s, geralt, alice])),
mongoose_helper:clear_last_activity(Config, carol),
Config,
R.


init_per_testcase(disconnect_inactive = CaseName, Config) ->
NewConfig = escalus_ejabberd:setup_option(inactivity(), Config),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(connection_interrupted = CaseName, Config) ->
NewConfig = escalus_ejabberd:setup_option(inactivity(), Config),
NewerConfig = escalus_ejabberd:setup_option(max_wait(), NewConfig),
escalus:init_per_testcase(CaseName, NewerConfig);
init_per_testcase(interrupt_long_poll_is_activity = CaseName, Config) ->
InactConfig = escalus_ejabberd:setup_option(inactivity(), Config),
NewConfig = escalus_users:update_userspec(InactConfig, carol, bosh_wait, 10),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(reply_on_pause = CaseName, Config) ->
NewConfig = escalus_ejabberd:setup_option(inactivity(), Config),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(pause_request_is_activity = CaseName, Config) ->
NewConfig = escalus_ejabberd:setup_option(inactivity(5), Config),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(reply_in_time = CaseName, Config) ->
InactConfig = escalus_ejabberd:setup_option(inactivity(10), Config),
NewConfig = escalus_users:update_userspec(InactConfig, carol, bosh_wait, 3),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(server_acks = CaseName, Config) ->
NewConfig = escalus_ejabberd:setup_option(server_acks_opt(), Config),
escalus:init_per_testcase(CaseName, NewConfig);
init_per_testcase(CaseName, Config) ->
escalus:init_per_testcase(CaseName, Config).

end_per_testcase(disconnect_inactive = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(inactivity(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
end_per_testcase(connection_interrupted = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(max_wait(), Config),
NewerConfig = escalus_ejabberd:reset_option(inactivity(), NewConfig),
escalus:end_per_testcase(CaseName, NewerConfig);
end_per_testcase(interrupt_long_poll_is_activity = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(inactivity(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
end_per_testcase(reply_on_pause = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(inactivity(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
end_per_testcase(pause_request_is_activity = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(inactivity(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
end_per_testcase(reply_in_time = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(inactivity(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
end_per_testcase(server_acks = CaseName, Config) ->
NewConfig = escalus_ejabberd:reset_option(server_acks_opt(), Config),
escalus:end_per_testcase(CaseName, NewConfig);
Expand Down Expand Up @@ -432,12 +399,10 @@ namespace(Config) ->
end).

disconnect_inactive(Config) ->
escalus:story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->

%% Sanity check - there should be one BOSH session belonging
%% to Carol.
1 = length(get_bosh_sessions()),
escalus:fresh_story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->

Sid = get_bosh_sid(Carol),
{_, Sid, CarolSessionPid} = get_bosh_session(Sid),
%% Don't send new long-polling requests waiting for server push.
set_keepalive(Carol, false),

Expand All @@ -448,9 +413,7 @@ disconnect_inactive(Config) ->
escalus:assert(is_chat_message, [<<"Hello!">>],
escalus_client:wait_for_stanza(Carol)),

Sid = get_bosh_sid(Carol),
%% Ensure all connections for Carol have been closed.
{_, Sid, CarolSessionPid} = get_bosh_session(Sid),
[] = get_handlers(CarolSessionPid),

%% Wait for disconnection because of inactivity timeout.
Expand All @@ -465,12 +428,10 @@ disconnect_inactive(Config) ->
end).

connection_interrupted(Config) ->
escalus:story(Config, [{carol, 1}], fun(Carol) ->
escalus:fresh_story(Config, [{carol, 1}], fun(Carol) ->

%% Sanity check - there should be one BOSH session belonging
%% to Carol.
1 = length(get_bosh_sessions()),

%% Turn off Escalus auto-reply, so that inactivity is triggered.
set_keepalive(Carol, false),

Expand All @@ -487,7 +448,7 @@ connection_interrupted(Config) ->
%% Keep in mind this only works due to the max_wait also being lowered.
%% In other words, wait timeout must happen, so that there are
%% no requests held by the server for inactivity to cause disconnection.
timer:sleep(2 * timer:seconds(?INACTIVITY)),
timer:sleep(timer:seconds(?INACTIVITY) + timer:seconds(?MAX_WAIT)),

%% Assert Carol has been disconnected due to inactivity.
false = is_sesssion_alive(Sid)
Expand All @@ -496,8 +457,10 @@ connection_interrupted(Config) ->

%% Ensure that a new request replacing an existing long-poll does not start the
%% inactivity timer.
interrupt_long_poll_is_activity(Config) ->
escalus:story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->
interrupt_long_poll_is_activity(ConfigIn) ->
Config = escalus_users:update_userspec(ConfigIn, carol, bosh_wait, 10),

escalus:fresh_story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->

%% Sanity check - there should be one BOSH session belonging
%% to Carol and one handler for Carol.
Expand All @@ -522,8 +485,7 @@ interrupt_long_poll_is_activity(Config) ->
end).

reply_on_pause(Config) ->
escalus:story(Config, [{carol, 1}], fun(Carol) ->

escalus:fresh_story(Config, [{carol, 1}], fun(Carol) ->

Sid = get_bosh_sid(Carol),
{_, _, CarolSessionPid} = get_bosh_session(Sid),
Expand All @@ -543,7 +505,7 @@ reply_on_pause(Config) ->
end).

cant_pause_for_too_long(Config) ->
escalus:story(Config, [{carol, 1}], fun(Carol) ->
escalus:fresh_story(Config, [{carol, 1}], fun(Carol) ->

Sid = get_bosh_sid(Carol),
{_, _, CarolSessionPid} = get_bosh_session(Sid),
Expand All @@ -561,7 +523,7 @@ cant_pause_for_too_long(Config) ->

%% Ensure that a pause request causes inactivity timer cancellation.
pause_request_is_activity(Config) ->
escalus:story(Config, [{carol, 1}], fun(Carol) ->
escalus:fresh_story(Config, [{carol, 1}], fun(Carol) ->

Sid = get_bosh_sid(Carol),
{_, _, CarolSessionPid} = get_bosh_session(Sid),
Expand All @@ -571,22 +533,23 @@ pause_request_is_activity(Config) ->
1 = wait_for_handler(CarolSessionPid),

%% Wait most of the allowed inactivity interval.
timer:sleep(timer:seconds(4)),
timer:sleep(timer:seconds(?INACTIVITY - 1)),

%% This should cancel the inactivity timer.
pause(Carol, 10),

%% Wait a bit past the inactivity interval.
timer:sleep(timer:seconds(4)),
timer:sleep(timer:seconds(?INACTIVITY - 1)),

%% No disconnection should've occured.
escalus_assert:has_no_stanzas(Carol),
true = is_sesssion_alive(Sid)

end).

reply_in_time(Config) ->
escalus:story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->
reply_in_time(ConfigIn) ->
Config = escalus_users:update_userspec(ConfigIn, carol, bosh_wait, 1),
escalus:fresh_story(Config, [{carol, 1}, {geralt, 1}], fun(Carol, Geralt) ->

Wait = proplists:get_value(bosh_wait,
escalus_users:get_userspec(Config, carol)),
Expand Down

0 comments on commit bfa2b38

Please sign in to comment.