diff --git a/test/acc_SUITE.erl b/test/acc_SUITE.erl
index 7449cdc933f..3d83b9347fd 100644
--- a/test/acc_SUITE.erl
+++ b/test/acc_SUITE.erl
@@ -30,7 +30,6 @@ groups() ->
store_retrieve_and_delete_many,
init_from_element,
produce_iq_meta_automatically,
- strip,
strip_with_params,
parse_with_cdata
]
@@ -130,38 +129,6 @@ parse_with_cdata(_C) ->
{XMLNS, _} = mongoose_iq:xmlns(Acc),
?assertEqual(<<"jabber:iq:roster">>, XMLNS).
-strip(_C) ->
- El = iq_stanza(),
- FromJID = jid:make(<<"jajid">>, <<"localhost">>, <<>>),
- ToJID = jid:make(<<"tyjid">>, <<"localhost">>, <<>>),
- Server = maps:get(lserver, ?ACC_PARAMS),
- HostType = maps:get(host_type, ?ACC_PARAMS),
- Acc = mongoose_acc:new(?ACC_PARAMS#{element => El,
- from_jid => FromJID,
- to_jid => ToJID}),
- {XMLNS1, Acc1} = mongoose_iq:xmlns(Acc),
- ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, XMLNS1),
- ?assertEqual(<<"set">>, mongoose_acc:stanza_type(Acc1)),
- ?assertEqual(undefined, mongoose_acc:get(ns, ppp, undefined, Acc1)),
- Acc2 = mongoose_acc:set_permanent(ns, ppp, 997, Acc1),
- Acc3 = mongoose_acc:set(ns2, [{a, 1}, {b, 2}], Acc2),
- ?assertMatch([_, _], mongoose_acc:get(ns2, Acc3)),
- ?assertEqual(Server, mongoose_acc:lserver(Acc3)),
- ?assertEqual(HostType, mongoose_acc:host_type(Acc3)),
- ?assertEqual({FromJID, ToJID, El}, mongoose_acc:packet(Acc3)),
- Ref = mongoose_acc:ref(Acc3),
- %% strip stanza and check that only non-permanent fields are missing
- NAcc1 = mongoose_acc:strip(Acc3),
- {XMLNS3, NAcc2} = mongoose_iq:xmlns(NAcc1),
- ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, XMLNS3),
- ?assertEqual(<<"set">>, mongoose_acc:stanza_type(NAcc2)),
- ?assertEqual(Server, mongoose_acc:lserver(NAcc2)),
- ?assertEqual(HostType, mongoose_acc:host_type(NAcc2)),
- ?assertEqual({FromJID, ToJID, El}, mongoose_acc:packet(NAcc2)),
- ?assertEqual(Ref, mongoose_acc:ref(NAcc2)),
- ?assertEqual(997, mongoose_acc:get(ns, ppp, NAcc2)),
- ?assertEqual([], mongoose_acc:get(ns2, NAcc2)).
-
strip_with_params(_Config) ->
FromJID = jid:make(<<"jajid">>, <<"localhost">>, <<>>),
ToJID = jid:make(<<"tyjid">>, <<"localhost">>, <<>>),
diff --git a/test/cowboy_SUITE.erl b/test/cowboy_SUITE.erl
index 4500c72733d..8e0f29b07f9 100644
--- a/test/cowboy_SUITE.erl
+++ b/test/cowboy_SUITE.erl
@@ -18,6 +18,7 @@
-compile([export_all, nowarn_export_all]).
-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
-define(SERVER, "http://localhost:8080").
@@ -221,9 +222,9 @@ start_cowboy_returns_error_eaddrinuse(_C) ->
handlers => [],
transport => default_config([listen, http, transport]),
protocol => default_config([listen, http, protocol])},
- {ok, _Pid} = ejabberd_cowboy:start_cowboy(a_ref, Opts, 2, 10),
+ ?assertMatch({ok, _}, ejabberd_cowboy:start_cowboy(a_ref, Opts, 2, 10)),
Result = ejabberd_cowboy:start_cowboy(a_ref_2, Opts, 2, 10),
- {error, eaddrinuse} = Result.
+ ?assertEqual({error, eaddrinuse}, Result).
%%--------------------------------------------------------------------
%% Helpers
@@ -391,4 +392,3 @@ ws_websocket_terminate(_Reason, _Req, no_ws_state) ->
assert_cowboy_handler_calls(M, F, Num) ->
Fun = fun() -> meck:num_calls(M, F, '_') end,
async_helper:wait_until(Fun, Num).
-
diff --git a/test/ejabberd_c2s_SUITE.erl b/test/ejabberd_c2s_SUITE.erl
deleted file mode 100644
index f5b01b5a338..00000000000
--- a/test/ejabberd_c2s_SUITE.erl
+++ /dev/null
@@ -1,266 +0,0 @@
--module(ejabberd_c2s_SUITE).
--include_lib("eunit/include/eunit.hrl").
--include_lib("exml/include/exml_stream.hrl").
--compile([export_all, nowarn_export_all]).
-
--define(eq(E, I), ?assertEqual(E, I)).
--define(am(E, I), ?assertMatch(E, I)).
-
--import(config_parser_helper, [config/2]).
-
-all() -> [
- c2s_start_stop_test,
- stream_error_when_invalid_domain,
- session_established,
- send_error_when_waiting,
- c2s_is_killed_when_too_many_messages_in_the_queue
- ].
-
-init_per_suite(C) ->
- {ok, _} = application:ensure_all_started(jid),
- application:start(x),
- C.
-
-end_per_suite(C) ->
- C.
-
-init_per_testcase(_TC, C) ->
- ejabberd_c2s_SUITE_mocks:setup(),
- C.
-
-end_per_testcase(_TC, C) ->
- ejabberd_c2s_SUITE_mocks:teardown(),
- C.
-
-c2s_start_stop_test(_) ->
- {ok, C2SPid} = given_c2s_started(),
-
- when_c2s_is_stopped(C2SPid),
-
- %% then
- ?eq(false, erlang:is_process_alive(C2SPid)).
-
-
-stream_error_when_invalid_domain(_) ->
- ok = meck:new(mongoose_hooks, [passthrough]),
- {ok, C2SPid} = given_c2s_started(),
-
- C2Sactions = when_stream_is_opened(C2SPid, stream_header(<<"badhost">>)),
- [StreamStart, StreamError, StreamEnd, CloseSocket] = C2Sactions,
- History = meck:history(mongoose_hooks),
- HookRes = [ok || {_, {mongoose_hooks, xmpp_send_element, [undefined, #{host_type := undefined} | _]}, _} <- History],
- ?am([], HookRes),
- ?am({send, [_P,
- <<"",
- "">>
- ]},
- StreamStart),
- ?am({send, [_P,
- <<"",
- "",
- "">>]}, StreamError),
- ?am({send, [_P, <<"">>]}, StreamEnd),
- ?am({close, [_P]}, CloseSocket),
- ok.
-
-session_established(_) ->
- {ok, C2SPid} = given_c2s_started(),
- change_state_to(session_established, C2SPid),
- ?eq(session_established, getstate(C2SPid)),
- Last = last_stanza(),
- ?eq(final_iq_response(), Last).
-
-send_error_when_waiting(_) ->
- % this is a regression test for #1252 - when c2s is in state
- % wait_for_session_or_sm and it fails to send a message
- % it should be handled properly
- {ok, C2SPid} = given_c2s_started(),
- change_state_to(wait_for_session_or_sm, C2SPid),
- % here we break things to check how c2s will handle error while sending
- % message in this state
- meck:expect(ejabberd_socket, send, fun(_, _El) -> error_error_error end),
- sync_c2s(C2SPid),
- p1_fsm:send_event(C2SPid, {xmlstreamelement, setsession_stanza()}),
- sync_c2s(C2SPid),
- [Close, StreamEnd, StreamError | _] = lists:reverse(received_stanzas()),
- ?eq(stream_error_response(),
- StreamError),
- ?eq(<<"">>, StreamEnd),
- ?eq(close, Close),
- ok.
-
-c2s_is_killed_when_too_many_messages_in_the_queue(_) ->
- meck:new(ejabberd_c2s, [passthrough]),
- MaxQueueSize = 50,
- Self = self(),
- %% We simulate a long running event during which
- %% there will be many messages put into C2S process message queue
- meck:expect(ejabberd_c2s, handle_event,
- fun(go_to_sleep, StateName, ProcState) ->
- Self ! c2s_going_to_sleep,
- ct:pal("going to sleep"),
- receive continue -> ok end,
- {next_state, StateName, ProcState};
- (Event, StateName, ProcState) ->
- meck:passthrough([Event, StateName, ProcState])
- end),
- {ok, C2SPid} = given_c2s_started(#{max_fsm_queue => MaxQueueSize}),
-
- %% We want to monitor the c2s process and not being linked to it
- Ref = erlang:monitor(process, C2SPid),
- erlang:unlink(C2SPid),
-
- p1_fsm_old:send_all_state_event(C2SPid, go_to_sleep),
-
- receive c2s_going_to_sleep -> ok end,
-
- meck:unload(ejabberd_c2s),
-
- %% We put MaxQueueSize + 1 messages to C2S Process message queue
- %% while it is asleep
- %% The process will be killed when it wakes up and tries to process
- %% next message
-
- [p1_fsm_old:send_all_state_event(C2SPid, {event, I}) ||
- I <- lists:seq(1, MaxQueueSize + 1)],
- C2SPid ! continue,
-
- receive
- {'DOWN', Ref, process, C2SPid, {process_limit,{max_queue, AllMessages}}} ->
- ct:pal("C2S dead due to message_queue_length=~p, max allowed was ~p",
- [AllMessages, MaxQueueSize]);
- Msg ->
- ct:fail("Other msg: ~p", [Msg])
- after timer:seconds(5) ->
- {message_queue_len, N} = process_info(C2SPid, message_queue_len),
- ct:fail("timeout waiting c2s exit, with message_queue_len = ~p", [N])
- end,
- ok.
-
-last_stanza() ->
- [H|_] = lists:reverse(received_stanzas()),
- H.
-
-received_stanzas() ->
- Calls = lists:filtermap(filter_calls(ejabberd_socket, [send, close]),
- meck:history(ejabberd_socket)),
-%% ct:pal("Calls: ~p", [Calls]),
- lists:map(fun extract_stanza/1, Calls).
-
-extract_stanza({_, [_, S]}) -> S;
-extract_stanza({close, _}) -> close.
-
-change_state_to(Target, C2SPid) ->
- Curr = getstate(C2SPid),
- change_state_to(Curr, Target, C2SPid).
-
-change_state_to(T, T, _) ->
- ok;
-change_state_to(wait_for_stream, T, C2SPid) ->
- p1_fsm:send_event(C2SPid, stream_header(<<"localhost">>)),
- change_state_to(getstate(C2SPid), T, C2SPid);
-change_state_to(wait_for_feature_before_auth, T, C2SPid) ->
- p1_fsm:send_event(C2SPid, {xmlstreamelement, auth_stanza()}),
- change_state_to(getstate(C2SPid), T, C2SPid);
-change_state_to(wait_for_feature_after_auth, T, C2SPid) ->
- p1_fsm:send_event(C2SPid, {xmlstreamelement, bind_stanza()}),
- change_state_to(getstate(C2SPid), T, C2SPid);
-change_state_to(wait_for_session_or_sm, T, C2SPid) ->
- p1_fsm:send_event(C2SPid, {xmlstreamelement, setsession_stanza()}),
- change_state_to(getstate(C2SPid), T, C2SPid);
-change_state_to(_, _, _) ->
- error.
-
-getstate(C2SPid) ->
- State = sync_c2s(C2SPid),
- [_, StateName | _] = State,
- StateName.
-
-when_stream_is_opened(C2SPid, Stanza) ->
- p1_fsm:send_event(C2SPid, Stanza),
- sync_c2s(C2SPid),
- lists:filtermap(filter_calls(ejabberd_socket, [send, close]),
- meck:history(ejabberd_socket)).
-
-filter_calls(_ExpectedMod, Funs) ->
- fun({_Pid, MFA, _Return}) ->
- maybe_extract_function_with_args(MFA, Funs)
- end.
-
-maybe_extract_function_with_args({_Mod, Fun, Args}, List) ->
- case lists:member(Fun, List) of
- true -> {true, {Fun, Args}};
- _ -> false
- end.
-
-sync_c2s(C2SPid) -> catch sys:get_state(C2SPid).
-
-stream_valid_header_response() ->
- R = ""
- "",
- list_to_binary(R).
-
-stream_header(Domain) ->
- #xmlstreamstart{name = <<"stream:stream">>,
- attrs = [{<<"to">>, Domain},
- {<<"xml:lang">>, <<"en">>},
- {<<"version">>, <<"1.0">>},
- {<<"xmlns">>, <<"jabber:client">>},
- {<<"xmlns:stream">>,
- <<"http://etherx.jabber.org/streams">>}]}.
-
-auth_stanza() ->
- {xmlel, <<"auth">>,
- [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-sasl">>},
- {<<"mechanism">>, <<"PLAIN">>}],
- [{xmlcdata, <<"AGFsaWNFOTkuODk3NzMzAG1hdHlncnlzYQ==">>}]}.
-
-bind_stanza() ->
- {xmlel, <<"iq">>,
- [{<<"type">>, <<"set">>}, {<<"id">>, <<"4436">>}],
- [{xmlel, <<"bind">>,
- [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-bind">>}],
- [{xmlel, <<"resource">>, [], [{xmlcdata, <<"res1">>}]}]}]}.
-
-setsession_stanza() ->
- {xmlel, <<"iq">>,
- [{<<"type">>, <<"set">>}, {<<"id">>, <<"4436">>}],
- [{xmlel, <<"session">>, [{<<"xmlns">>,
- <<"urn:ietf:params:xml:ns:xmpp-session">>}],
- []}]}.
-
-given_c2s_started() ->
- given_c2s_started(#{}).
-
-given_c2s_started(ExtraOpts) ->
- ejabberd_c2s:start_link({ejabberd_socket, self()}, c2s_opts(ExtraOpts)).
-
-when_c2s_is_stopped(Pid) ->
- stop_c2s(Pid),
- sync_c2s(Pid).
-
-c2s_opts(ExtraOpts) ->
- config([listen, c2s], ExtraOpts#{access => c2s,
- shaper => c2s_shaper,
- max_stanza_size => 65536}).
-
-stop_c2s(C2SPid) when is_pid(C2SPid) ->
- _R = ejabberd_c2s:stop(C2SPid).
-
-jid(Str) ->
- jid:from_binary(Str).
-
-final_iq_response() ->
- <<""
- ""
- "">>.
-
-stream_error_response() ->
- <<"",
- "",
- "">>.
-
diff --git a/test/ejabberd_sm_SUITE.erl b/test/ejabberd_sm_SUITE.erl
deleted file mode 100644
index aa85adcfa87..00000000000
--- a/test/ejabberd_sm_SUITE.erl
+++ /dev/null
@@ -1,615 +0,0 @@
--module(ejabberd_sm_SUITE).
--include_lib("eunit/include/eunit.hrl").
--include_lib("common_test/include/ct.hrl").
-
--include_lib("jid/include/jid.hrl").
--include_lib("session.hrl").
--compile([export_all, nowarn_export_all]).
-
--define(eq(E, I), ?assertEqual(E, I)).
-
--define(B(C), (proplists:get_value(backend, C))).
--define(MAX_USER_SESSIONS, 2).
-
--import(config_parser_helper, [default_config/1]).
-
-all() -> [{group, mnesia}, {group, redis}].
-
-init_per_suite(C) ->
- {ok, _} = application:ensure_all_started(jid),
- application:ensure_all_started(exometer_core),
- F = fun() ->
- ejabberd_sm_backend_sup:start_link(),
- receive stop -> ok end
- end,
- Pid = spawn(F),
- [{pid, Pid} | C].
-
-end_per_suite(C) ->
- Pid = ?config(pid, C),
- Pid ! stop,
- application:stop(exometer),
- application:stop(exometer_core).
-
-groups() ->
- [{mnesia, [], tests()},
- {redis, [], tests()}].
-
-tests() ->
- [open_session,
- get_full_session_list,
- get_vh_session_list,
- get_sessions_2,
- get_sessions_3,
- session_is_updated_when_created_twice,
- delete_session,
- clean_up,
- too_much_sessions,
- unique_count,
- unique_count_while_removing_entries,
- session_info_is_stored,
- session_info_is_updated_if_keys_match,
- session_info_is_extended_if_new_keys_present,
- session_info_keys_not_truncated_if_session_opened_with_empty_infolist,
- kv_can_be_stored_for_session,
- kv_can_be_updated_for_session,
- kv_can_be_removed_for_session,
- store_info_sends_message_to_the_session_owner,
- remove_info_sends_message_to_the_session_owner,
- cannot_reproduce_race_condition_in_store_info
- ].
-
-init_per_group(mnesia, Config) ->
- ok = mnesia:create_schema([node()]),
- ok = mnesia:start(),
- [{backend, ejabberd_sm_mnesia} | Config];
-init_per_group(redis, Config) ->
- init_redis_group(is_redis_running(), Config).
-
-init_redis_group(true, Config) ->
- Self = self(),
- proc_lib:spawn(fun() ->
- register(test_helper, self()),
- mongoose_wpool:ensure_started(),
- % This would be started via outgoing_pools in normal case
- Pool = default_config([outgoing_pools, redis, default]),
- mongoose_wpool:start_configured_pools([Pool], []),
- Self ! ready,
- receive stop -> ok end
- end),
- receive ready -> ok after timer:seconds(30) -> ct:fail(test_helper_not_ready) end,
- [{backend, ejabberd_sm_redis} | Config];
-init_redis_group(_, _) ->
- {skip, "redis not running"}.
-
-end_per_group(mnesia, Config) ->
- mnesia:stop(),
- mnesia:delete_schema([node()]),
- Config;
-end_per_group(_, Config) ->
- whereis(test_helper) ! stop,
- Config.
-
-init_per_testcase(too_much_sessions, Config) ->
- set_test_case_meck(?MAX_USER_SESSIONS),
- setup_sm(Config),
- Config;
-init_per_testcase(_, Config) ->
- set_test_case_meck(infinity),
- setup_sm(Config),
- Config.
-
-end_per_testcase(_, Config) ->
- clean_sessions(Config),
- terminate_sm(),
- unload_meck(),
- unset_opts(Config).
-
-open_session(C) ->
- {Sid, USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR),
- verify_session_opened(C, Sid, USR).
-
-get_full_session_list(C) ->
- ManyUsers = generate_many_random_users(5, [<<"localhost">>, <<"otherhost">>]),
- ManyUsersLen = length(ManyUsers),
- [given_session_opened(Sid, USR) || {Sid, USR} <- ManyUsers],
- AllSessions = ejabberd_sm:get_full_session_list(),
- AllSessionsLen = length(AllSessions),
- AllSessionsLen = ManyUsersLen,
- [verify_session_opened(C, Sid, USR) || {Sid, USR} <- ManyUsers].
-
-get_vh_session_list(C) ->
- ManyUsersLocal = generate_many_random_users(5, [<<"localhost">>]),
- ManyUsersOther = generate_many_random_users(5, [<<"otherhost">>]),
- ManyUsersLocalLen = length(ManyUsersLocal),
- [given_session_opened(Sid, USR) || {Sid, USR} <- ManyUsersLocal ++ ManyUsersOther],
- LocalhostSessions = ejabberd_sm:get_vh_session_list(<<"localhost">>),
- LocalhostSessionsLen = length(LocalhostSessions),
- LocalhostSessionsLen = ManyUsersLocalLen,
- ManyUsersLocalLen = ejabberd_sm:get_vh_session_number(<<"localhost">>),
- [verify_session_opened(C, Sid, USR) || {Sid, USR} <- ManyUsersLocal].
-
-get_sessions_2(C) ->
- UsersWithManyResources = generate_many_random_res(5, 3, [<<"localhost">>, <<"otherhost">>]),
- [given_session_opened(Sid, USR) || {Sid, USR} <- UsersWithManyResources],
- USDict = get_unique_us_dict(UsersWithManyResources),
- [verify_session_opened(C, U, S, dict:fetch({U, S}, USDict)) || {U, S} <- dict:fetch_keys(USDict)],
- [verify_session_opened(C, Sid, USR) || {Sid, USR} <- UsersWithManyResources].
-
-
-get_sessions_3(C) ->
- UserRes = generate_many_random_res(1, 3, [<<"localhost">>]),
- AllSessions = length(UserRes),
- {_, {User, Server, _}} = hd(UserRes),
- [given_session_opened(Sid, USR) || {Sid, USR} <- UserRes],
- Sessions_2 = ?B(C):get_sessions(User, Server),
- AllSessions = length(Sessions_2),
- F = fun({Sid, {U, S, R} = USR}) ->
- [#session{sid = Sid} = Session] = ?B(C):get_sessions(U, S, R),
- Session = lists:keyfind(Sid, #session.sid, Sessions_2),
- Session = lists:keyfind(USR, #session.usr, Sessions_2),
- true
- end,
- true = lists:all(F, UserRes).
-
-session_is_updated_when_created_twice(C) ->
- {Sid, {U, S, _} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR),
- verify_session_opened(C, Sid, USR),
-
- given_session_opened(Sid, USR, 20),
- verify_session_opened(C, Sid, USR),
-
- [#session{usr = USR, sid = Sid, priority = 20}] = ?B(C):get_sessions(),
- [#session{usr = USR, sid = Sid, priority = 20}] = ?B(C):get_sessions(S),
- [#session{priority = 20}] = ?B(C):get_sessions(U, S).
-
-session_info_is_stored(C) ->
- {Sid, {U, S, _} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- [#session{sid = Sid, info = #{key1 := val1}}]
- = ?B(C):get_sessions(U,S).
-
-session_info_is_updated_if_keys_match(C) ->
- {Sid, {U, S, _} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_opened(Sid, USR, 1, [{key1, val2}]),
-
- [#session{sid = Sid, info = #{key1 := val2}}]
- = ?B(C):get_sessions(U,S).
-
-session_info_is_extended_if_new_keys_present(C) ->
- {Sid, {U, S, _} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_opened(Sid, USR, 1, [{key1, val1}, {key2, val2}]),
-
- [#session{sid = Sid, info = #{key1 := val1, key2 := val2}}]
- = ?B(C):get_sessions(U,S).
-
-session_info_keys_not_truncated_if_session_opened_with_empty_infolist(C) ->
- {Sid, {U, S, _} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_opened(Sid, USR, 1, []),
-
- [#session{sid = Sid, info = #{key1 := val1}}]
- = ?B(C):get_sessions(U,S).
-
-
-kv_can_be_stored_for_session(C) ->
- {Sid, {U, S, R} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_info_stored(U, S, R, {key2, newval}),
-
- ?assertMatch([#session{sid = Sid, info = #{key1 := val1, key2 := newval}}],
- ?B(C):get_sessions(U,S)).
-
-kv_can_be_updated_for_session(C) ->
- {Sid, {U, S, R} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_info_stored(U, S, R, {key2, newval}),
- when_session_info_stored(U, S, R, {key2, override}),
-
- ?assertMatch([#session{sid = Sid, info = #{key1 := val1, key2 := override}}],
- ?B(C):get_sessions(U, S)).
-
-kv_can_be_removed_for_session(C) ->
- {Sid, {U, S, R} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR, 1, [{key1, val1}]),
-
- when_session_info_stored(U, S, R, {key2, newval}),
-
- [#session{sid = Sid, info = #{key1 := val1, key2 := newval}}]
- = ?B(C):get_sessions(U, S),
-
- when_session_info_removed(U, S, R, key2),
-
- [#session{sid = Sid, info = #{key1 := val1}}]
- = ?B(C):get_sessions(U, S),
-
- when_session_info_removed(U, S, R, key1),
-
- [#session{sid = Sid, info = #{}}]
- = ?B(C):get_sessions(U, S).
-
-cannot_reproduce_race_condition_in_store_info(C) ->
- ok = try_to_reproduce_race_condition(C).
-
-store_info_sends_message_to_the_session_owner(C) ->
- SID = {erlang:system_time(microsecond), self()},
- U = <<"alice2">>,
- S = <<"localhost">>,
- R = <<"res1">>,
- Session = #session{sid = SID, usr = {U, S, R}, us = {U, S}, priority = 1, info = #{}},
- %% Create session in one process
- ?B(C):create_session(U, S, R, Session),
- %% but call store_info from another process
- JID = jid:make_noprep(U, S, R),
- spawn_link(fun() -> ejabberd_sm:store_info(JID, cc, undefined) end),
- %% The original process receives a message
- receive {store_session_info,
- #jid{luser = User, lserver = Server, lresource = Resource},
- K, V, _FromPid} ->
- ?eq(U, User),
- ?eq(S, Server),
- ?eq(R, Resource),
- ?eq({cc, undefined}, {K, V}),
- ok
- after 5000 ->
- ct:fail("store_info_sends_message_to_the_session_owner=timeout")
- end.
-
-remove_info_sends_message_to_the_session_owner(C) ->
- SID = {erlang:timestamp(), self()},
- U = <<"alice2">>,
- S = <<"localhost">>,
- R = <<"res1">>,
- Session = #session{sid = SID, usr = {U, S, R}, us = {U, S}, priority = 1, info = #{}},
- %% Create session in one process
- ?B(C):create_session(U, S, R, Session),
- %% but call remove_info from another process
- JID = jid:make_noprep(U, S, R),
- spawn_link(fun() -> ejabberd_sm:remove_info(JID, cc) end),
- %% The original process receives a message
- receive {remove_session_info,
- #jid{luser = User, lserver = Server, lresource = Resource},
- Key, _FromPid} ->
- ?eq(U, User),
- ?eq(S, Server),
- ?eq(R, Resource),
- ?eq(cc, Key),
- ok
- after 5000 ->
- ct:fail("remove_info_sends_message_to_the_session_owner=timeout")
- end.
-
-delete_session(C) ->
- {Sid, {U, S, R} = USR} = generate_random_user(<<"localhost">>),
- given_session_opened(Sid, USR),
- verify_session_opened(C, Sid, USR),
-
- ?B(C):delete_session(Sid, U, S, R),
-
- [] = ?B(C):get_sessions(),
- [] = ?B(C):get_sessions(S),
- [] = ?B(C):get_sessions(U, S),
- [] = ?B(C):get_sessions(U, S, R).
-
-
-
-clean_up(C) ->
- UsersWithManyResources = generate_many_random_res(5, 3, [<<"localhost">>, <<"otherhost">>]),
- [given_session_opened(Sid, USR) || {Sid, USR} <- UsersWithManyResources],
- ?B(C):cleanup(node()),
- %% give sm backend some time to clean all sessions
- ensure_empty(C, 10, ?B(C):get_sessions()).
-
-ensure_empty(_C, 0, Sessions) ->
- [] = Sessions;
-ensure_empty(C, N, Sessions) ->
- case Sessions of
- [] ->
- ok;
- _ ->
- timer:sleep(50),
- ensure_empty(C, N-1, ?B(C):get_sessions())
- end.
-
-too_much_sessions(_C) ->
- %% Max sessions set to ?MAX_USER_SESSIONS in init_per_testcase
- UserSessions = [generate_random_user(<<"a">>, <<"localhost">>) || _ <- lists:seq(1, ?MAX_USER_SESSIONS)],
- {AddSid, AddUSR} = generate_random_user(<<"a">>, <<"localhost">>),
-
- [given_session_opened(Sid, USR) || {Sid, USR} <- UserSessions],
-
- given_session_opened(AddSid, AddUSR),
-
- receive
- replaced ->
- ok
- after 10 ->
- ct:fail("replaced message not sent")
- end.
-
-
-
-unique_count(_C) ->
- UsersWithManyResources = generate_many_random_res(5, 3, [<<"localhost">>, <<"otherhost">>]),
- [given_session_opened(Sid, USR) || {Sid, USR} <- UsersWithManyResources],
- USDict = get_unique_us_dict(UsersWithManyResources),
- UniqueCount = ejabberd_sm:get_unique_sessions_number(),
- UniqueCount = dict:size(USDict).
-
-
-unique_count_while_removing_entries(C) ->
- unique_count(C),
- UniqueCount = ejabberd_sm:get_unique_sessions_number(),
- %% Register more sessions and mock the crash
- UsersWithManyResources = generate_many_random_res(10, 3, [<<"localhost">>, <<"otherhost">>]),
- [given_session_opened(Sid, USR) || {Sid, USR} <- UsersWithManyResources],
- set_test_case_meck_unique_count_crash(?B(C)),
- USDict = get_unique_us_dict(UsersWithManyResources),
- %% Check if unique count equals prev cached value
- UniqueCount = ejabberd_sm:get_unique_sessions_number(),
- meck:unload(?B(C)),
- true = UniqueCount /= dict:size(USDict) + UniqueCount.
-
-unload_meck() ->
- meck:unload(acl),
- meck:unload(gen_hook),
- meck:unload(ejabberd_commands),
- meck:unload(mongoose_domain_api).
-
-set_test_case_meck(MaxUserSessions) ->
- meck:new(acl, []),
- meck:expect(acl, match_rule, fun(_, _, _, _) -> MaxUserSessions end),
- meck:new(gen_hook, []),
- meck:expect(gen_hook, run_fold, fun(_, _, Acc, _) -> {ok, Acc} end),
- meck:new(mongoose_domain_api, []),
- meck:expect(mongoose_domain_api, get_domain_host_type, fun(H) -> {ok, H} end).
-
-set_test_case_meck_unique_count_crash(Backend) ->
- F = get_fun_for_unique_count(Backend),
- meck:new(Backend, []),
- meck:expect(Backend, unique_count, F).
-
-get_fun_for_unique_count(ejabberd_sm_mnesia) ->
- fun() ->
- mnesia:abort({badarg,[session,{{1442,941593,580189},list_to_pid("<0.23291.6>")}]})
- end;
-get_fun_for_unique_count(ejabberd_sm_redis) ->
- fun() ->
- %% The code below is on purpose, it's to crash with badarg reason
- length({error, timeout})
- end.
-
-make_sid() ->
- {erlang:timestamp(), self()}.
-
-given_session_opened(Sid, USR) ->
- given_session_opened(Sid, USR, 1).
-
-given_session_opened(Sid, {U, S, R}, Priority) ->
- given_session_opened(Sid, {U, S, R}, Priority, []).
-
-given_session_opened(Sid, {U, S, R}, Priority, Info) ->
- JID = jid:make_noprep(U, S, R),
- ejabberd_sm:open_session(S, Sid, JID, Priority, maps:from_list(Info)).
-
-when_session_opened(Sid, {U, S, R}, Priority, Info) ->
- given_session_opened(Sid, {U, S, R}, Priority, Info).
-
-when_session_info_stored(U, S, R, {K, V}) ->
- JID = jid:make_noprep(U, S, R),
- ejabberd_sm:store_info(JID, K, V).
-
-when_session_info_removed(U, S, R, Key) ->
- JID = jid:make_noprep(U, S, R),
- ejabberd_sm:remove_info(JID, Key).
-
-verify_session_opened(C, Sid, USR) ->
- do_verify_session_opened(?B(C), Sid, USR).
-
-do_verify_session_opened(ejabberd_sm_mnesia, Sid, {U, S, R} = USR) ->
- general_session_check(ejabberd_sm_mnesia, Sid, USR, U, S, R);
-do_verify_session_opened(ejabberd_sm_redis, Sid, {U, S, R} = USR) ->
- UHash = iolist_to_binary(hash(U, S, R, Sid)),
- Hashes = mongoose_redis:cmd(["SMEMBERS", n(node())]),
- true = lists:member(UHash, Hashes),
- SessionsUSEncoded = mongoose_redis:cmd(["SMEMBERS", hash(U, S)]),
- SessionsUS = [binary_to_term(Entry) || Entry <- SessionsUSEncoded],
- true = lists:keymember(Sid, 2, SessionsUS),
- [SessionUSREncoded] = mongoose_redis:cmd(["SMEMBERS", hash(U, S, R)]),
- SessionUSR = binary_to_term(SessionUSREncoded),
- #session{sid = Sid} = SessionUSR,
- general_session_check(ejabberd_sm_redis, Sid, USR, U, S, R).
-
-verify_session_opened(C, U, S, Resources) ->
- Sessions = ?B(C):get_sessions(U, S),
- F = fun({Sid, USR}) ->
- #session{} = Session = lists:keyfind(Sid, #session.sid, Sessions),
- Session == lists:keyfind(USR, #session.usr, Sessions)
- end,
- true = lists:all(F, Resources).
-
-general_session_check(M, Sid, USR, U, S, R) ->
- [#session{sid = Sid, usr = USR, us = {U, S}}] = M:get_sessions(U, S, R).
-
-clean_sessions(C) ->
- case ?B(C) of
- ejabberd_sm_mnesia ->
- mnesia:clear_table(session);
- ejabberd_sm_redis ->
- mongoose_redis:cmd(["FLUSHALL"])
- end.
-
-generate_random_user(S) ->
- U = base16:encode(crypto:strong_rand_bytes(5)),
- generate_random_user(U, S).
-
-generate_random_user(U, S) ->
- R = base16:encode(crypto:strong_rand_bytes(5)),
- generate_user(U, S, R).
-
-generate_user(U, S, R) ->
- Sid = make_sid(),
- {Sid, {U, S, R}}.
-
-generate_many_random_users(PerServerCount, Servers) ->
- Users = [generate_random_users(PerServerCount, Server) || Server <- Servers],
- lists:flatten(Users).
-
-generate_random_users(Count, Server) ->
- [generate_random_user(Server) || _ <- lists:seq(1, Count)].
-
-generate_many_random_res(UsersPerServer, ResourcesPerUser, Servers) ->
- Usernames = [base16:encode(crypto:strong_rand_bytes(5)) || _ <- lists:seq(1, UsersPerServer)],
- [generate_random_user(U, S) || U <- Usernames, S <- Servers, _ <- lists:seq(1, ResourcesPerUser)].
-
-get_unique_us_dict(USRs) ->
- F = fun({_, {U, S, _}} = I, SetAcc) ->
- dict:append({U, S}, I, SetAcc)
- end,
- lists:foldl(F, dict:new(), USRs).
-
-%% Taken from ejabberd_sm_redis
-
--spec hash(binary()) -> iolist().
-hash(Val1) ->
- ["s3:*:", Val1, ":*"].
-
-
--spec hash(binary(), binary()) -> iolist().
-hash(Val1, Val2) ->
- ["s2:", Val1, ":", Val2].
-
-
--spec hash(binary(), binary(), binary()) -> iolist().
-hash(Val1, Val2, Val3) ->
- ["s3:", Val1, ":", Val2, ":", Val3].
-
-
--spec hash(binary(), binary(), binary(), binary()) -> iolist().
-hash(Val1, Val2, Val3, Val4) ->
- ["s4:", Val1, ":", Val2, ":", Val3, ":", term_to_binary(Val4)].
-
-
--spec n(atom()) -> iolist().
-n(Node) ->
- ["n:", atom_to_list(Node)].
-
-
-is_redis_running() ->
- case eredis:start_link() of
- {ok, Client} ->
- Result = eredis:q(Client, [<<"PING">>], 5000),
- eredis:stop(Client),
- case Result of
- {ok,<<"PONG">>} ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end.
-
-try_to_reproduce_race_condition(Config) ->
- SID = {erlang:timestamp(), self()},
- U = <<"alice">>,
- S = <<"localhost">>,
- R = <<"res1">>,
- Session = #session{sid = SID, usr = {U, S, R}, us = {U, S}, priority = 1, info = #{}},
- ?B(Config):create_session(U, S, R, Session),
- Parent = self(),
- %% Add some instrumentation to simulate race conditions
- %% The goal is to delete the session after other process reads it
- %% but before it updates it. In other words, delete a record
- %% between get_sessions and create_session in ejabberd_sm:store_info
- %% Step1 prepare concurrent processes
- DeleterPid = spawn_link(fun() ->
- receive start -> ok end,
- ?B(Config):delete_session(SID, U, S, R),
- Parent ! p1_done
- end),
- SetterPid = spawn_link(fun() ->
- receive start -> ok end,
- when_session_info_stored(U, S, R, {cc, undefined}),
- Parent ! p2_done
- end),
- %% Step2 setup mocking for some ejabbers_sm_mnesia functions
- meck:new(?B(Config), []),
- %% When the first get_sessions (run from ejabberd_sm:store_info)
- %% is executed, the start msg is sent to Deleter process
- %% Thanks to that, setter will get not empty list of sessions
- PassThrough3 = fun(A, B, C) ->
- DeleterPid ! start,
- meck:passthrough([A, B, C]) end,
- meck:expect(?B(Config), get_sessions, PassThrough3),
- %% Wait some time before setting the sessions
- %% so we are sure delete operation finishes
- meck:expect(?B(Config), create_session,
- fun(U1, S1, R1, Session1) ->
- timer:sleep(100),
- meck:passthrough([U1, S1, R1, Session1])
- end),
- PassThrough4 = fun(A, B, C, D) -> meck:passthrough([A, B, C, D]) end,
- meck:expect(?B(Config), delete_session, PassThrough4),
- %% Start the play from setter process
- SetterPid ! start,
- %% Wait for both process to finish
- receive p1_done -> ok end,
- receive p2_done -> ok end,
- meck:unload(?B(Config)),
- %% Session should not exist
- case ?B(Config):get_sessions(U, S, R) of
- [] ->
- ok;
- Other ->
- error_logger:error_msg("issue=reproduced, sid=~p, other=~1000p",
- [SID, Other]),
- {error, reproduced}
- end.
-
-setup_sm(Config) ->
- set_opts(Config),
- set_meck(),
- ejabberd_sm:start_link(),
- case ?config(backend, Config) of
- ejabberd_sm_redis ->
- mongoose_redis:cmd(["FLUSHALL"]);
- ejabberd_sm_mnesia ->
- ok
- end.
-
-terminate_sm() ->
- gen_server:stop(ejabberd_sm).
-
-set_opts(Config) ->
- [mongoose_config:set_opt(Key, Value) || {Key, Value} <- opts(Config)].
-
-unset_opts(Config) ->
- [mongoose_config:unset_opt(Key) || {Key, _Value} <- opts(Config)].
-
-opts(Config) ->
- [{hosts, [<<"localhost">>]},
- {host_types, []},
- {all_metrics_are_global, false},
- {sm_backend, sm_backend(?config(backend, Config))}].
-
-sm_backend(ejabberd_sm_redis) -> redis;
-sm_backend(ejabberd_sm_mnesia) -> mnesia.
-
-set_meck() ->
- meck:expect(gen_hook, add_handler, fun(_, _, _, _, _) -> ok end),
- meck:expect(gen_hook, add_handlers, fun(_) -> ok end),
- meck:new(ejabberd_commands, []),
- meck:expect(ejabberd_commands, register_commands, fun(_) -> ok end),
- meck:expect(ejabberd_commands, unregister_commands, fun(_) -> ok end),
- ok.
diff --git a/test/jlib_SUITE.erl b/test/jlib_SUITE.erl
index e687a9e3554..e5ab3a6bbb5 100644
--- a/test/jlib_SUITE.erl
+++ b/test/jlib_SUITE.erl
@@ -8,10 +8,6 @@
-compile([export_all, nowarn_export_all]).
all() -> [
- make_iq_reply_changes_type_to_result,
- make_iq_reply_changes_to_to_from,
- make_iq_reply_switches_from_to_to,
- make_iq_reply_switches_to_and_from_attrs,
error_reply_check
].
@@ -22,47 +18,8 @@ init_per_suite(C) ->
end_per_suite(C) ->
C.
-
-make_iq_reply_switches_to_and_from_attrs(_C) ->
- ToJid = <<"test@esl.com/res">>,
- FromJid = <<"test2@esl.com/res2">>,
- #xmlel{attrs = Attrs} = BaseIQ = base_iq(),
-
- IQWithToAndFrom = BaseIQ#xmlel{attrs = [{<<"to">>, ToJid},
- {<<"from">>, FromJid} | Attrs]},
-
- WithToFromReply = jlib:make_result_iq_reply(IQWithToAndFrom),
-
- <<"result">> = exml_query:attr(WithToFromReply, <<"type">>),
- FromJid = exml_query:attr(WithToFromReply, <<"to">>),
- ToJid = exml_query:attr(WithToFromReply, <<"from">>).
-
-make_iq_reply_switches_from_to_to(_C) ->
- FromJid = <<"test2@esl.com/res2">>,
- #xmlel{attrs = Attrs} = BaseIQ = base_iq(),
- IQWithFrom = BaseIQ#xmlel{attrs = [{<<"from">>, FromJid} | Attrs]},
-
- WithFromReply = jlib:make_result_iq_reply(IQWithFrom),
-
- <<"result">> = exml_query:attr(WithFromReply, <<"type">>),
- FromJid = exml_query:attr(WithFromReply, <<"to">>).
-
-make_iq_reply_changes_to_to_from(_C) ->
- ToJid = <<"test@esl.com/res">>,
- #xmlel{attrs = Attrs} = BaseIQ = base_iq(),
- IQWithTo = BaseIQ#xmlel{attrs = [{<<"to">>, ToJid} | Attrs]},
-
- WithToReply = jlib:make_result_iq_reply(IQWithTo),
-
- <<"result">> = exml_query:attr(WithToReply, <<"type">>),
- ToJid = exml_query:attr(WithToReply, <<"from">>).
-
-make_iq_reply_changes_type_to_result(_) ->
- BaseIQReply = jlib:make_result_iq_reply(base_iq()),
- <<"result">> = exml_query:attr(BaseIQReply, <<"type">>).
-
error_reply_check(_) ->
- BaseIQReply = jlib:make_result_iq_reply(base_iq()),
+ BaseIQReply = base_iq(),
Acc = mongoose_acc:new(#{ location => ?LOCATION,
lserver => <<"localhost">>,
host_type => <<"localhost">>,
@@ -82,7 +39,7 @@ base_iq() ->
#xmlel{name = <<"iq">>,
attrs = [{<<"id">>, base64:encode(crypto:strong_rand_bytes(4))},
{<<"xmlns">>, <<"jabber:client">>},
- {<<"type">>, <<"set">>}],
+ {<<"type">>, <<"result">>}],
children = [#xmlel{name = <<"session">>,
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-session">>}]}
]}.
@@ -95,4 +52,3 @@ run_property(Prop, NumTest, StartSize, StopSize) ->
{numtests, NumTest},
{start_size, StartSize},
{max_size, StopSize}])).
-