From 649638170fc40f96748d114af5dc0d383e5de992 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 13 Dec 2021 12:39:03 +0100 Subject: [PATCH 01/26] Make some common functions --- big_tests/tests/sm_SUITE.erl | 285 +++++++++++++++-------------------- 1 file changed, 119 insertions(+), 166 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index dbae7a591d..b8d5c4cc22 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -276,11 +276,10 @@ client_enables_sm_twice_fails_with_correct_error_stanza(Config) -> session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza(Config) -> %% GIVEN USER WITH STREAM RESUMPTION ENABLED - {Alice, AliceSpec, SMID, SMH} = - get_stream_resumption_enabled_fresh_user_smid_and_h(Config, alice), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + SMH = escalus_connection:get_sm_h(Alice), %% WHEN USER RESUMES SESSION FROM NEW CLIENT - Steps2 = connection_steps_to_stream_resumption(SMID, SMH), - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps2), + Alice2 = connect_with_resume_session(Alice, SMH), process_initial_stanza(Alice2), %% THEN: Old session is gracefully closed with the correct error stanza escalus:assert(is_stream_error, [<<"conflict">>, <<>>], @@ -293,12 +292,9 @@ session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza( session_resumed_and_old_session_dead_doesnt_route_error_to_new_session(Config) -> %% GIVEN USER WITH STREAM RESUMPTION ENABLED - {Alice, AliceSpec, SMID, SMH} = - get_stream_resumption_enabled_fresh_user_smid_and_h(Config, alice), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), %% WHEN FIRST SESSION DIES AND USER RESUMES FROM NEW CLIENT - escalus_connection:kill(Alice), - Steps2 = connection_steps_to_stream_resumption(SMID, SMH), - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps2), + Alice2 = kill_and_connect_with_resume_session(Alice), process_initial_stanza(Alice2), %% THEN new session does not have any message rerouted false = escalus_client:has_stanzas(Alice2), @@ -460,10 +456,8 @@ resend_more_offline_messages_than_buffer_size(Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), + {Bob, _} = given_fresh_user(Config, bob), + AliceSpec = escalus_fresh:create_fresh_user(Config, alice), % sent some messages - more than unacked buffer size @@ -523,36 +517,29 @@ preserve_order(Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), - - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps++[stream_resumption]), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Alice, presence), + {Bob, _} = given_fresh_user(Config, bob), + {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"1">>)), %% kill alice connection escalus_connection:kill(Alice), wait_until_disconnected(AliceSpec), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"2">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"3">>)), {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), escalus_connection:send(NewAlice, escalus_stanza:enable_sm([resume])), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"4">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"5">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"4">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"5">>)), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"6">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"6">>)), - receive_all_ordered(NewAlice,1), + receive_all_ordered(NewAlice, 1), % replace connection {ok, NewAlice2, _} = escalus_connection:start(AliceSpec, ConnSteps), @@ -587,25 +574,17 @@ resend_unacked_after_resume_timeout(Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), - - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps++[stream_resumption]), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Alice, presence), + {Bob, _} = given_fresh_user(Config, bob), + {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), %% kill alice connection escalus_connection:kill(Alice), %% ensure there is no session wait_until_disconnected(AliceSpec), - 0 = length(get_user_alive_resources(AliceSpec)), %% alice come back and receives unacked message {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), @@ -623,22 +602,15 @@ resend_unacked_after_resume_timeout(Config) -> resume_expired_session_returns_correct_h(Config) -> %% connect bob and alice - {Bob, _, _, _} = - get_stream_resumption_enabled_fresh_user_smid_and_h(Config, bob), - {Alice, AliceSpec, SMID, SMH} = - get_stream_resumption_enabled_fresh_user_smid_and_h(Config, alice), + Bob = connect_fresh_with_stream_resumption_enabled(Config, bob), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), %% Bob sends a message to Alice, and Alice receives it but doesn't acknowledge - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus:wait_for_stanza(Alice), - %% kill alice connection - escalus_connection:kill(Alice), - %% ensure there is no session - wait_until_disconnected(AliceSpec), %% alice comes back, but too late, so resumption doesn't work, %% but she receives the previous h = 1 anyway - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_authenticate()), - escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), + NewAlice = kill_and_connect_with_resume_session_without_waiting_for_result(Alice), FailedResumption = escalus_connection:get_stanza(NewAlice, failed_resumption), <<"1">> = exml_query:attr(FailedResumption, <<"h">>), %% And we can continue with bind and session @@ -668,31 +640,23 @@ resume_session_state_send_message(Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), - - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps++[stream_resumption]), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Alice, presence), + {Bob, _} = given_fresh_user(Config, bob), + {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), %% Ack the presence stanza escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), escalus:send(Alice, escalus_stanza:sm_ack(1)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), %% kill alice connection - {ok, C2SPid} = get_session_pid(AliceSpec, escalus_client:resource(Alice)), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), 1 = length(get_user_alive_resources(AliceSpec)), %% send some messages and check if c2s can handle it - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-2">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-3">>)), %% suspend the process to ensure that Alice has enough time to reconnect, %% before resumption timeout occurs. ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -720,25 +684,18 @@ resume_session_state_stop_c2s(Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec, ConnSteps), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), - - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps++[stream_resumption]), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Alice, presence), + {Bob, _} = given_fresh_user(Config, bob), + {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), %% Ack presence escalus_connection:send(Alice, escalus_stanza:sm_ack(1)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), %% get pid of c2s - {ok, C2SPid} = get_session_pid(AliceSpec, escalus_client:resource(Alice)), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), %% Wait c2s process to process our presence ack. %% Otherwise, we can receive two initial presences sometimes. wait_for_c2s_unacked_count(C2SPid, 1), @@ -771,7 +728,7 @@ session_established(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], {Alice, _} = given_fresh_user_with_spec(AliceSpec), - {ok, C2SPid} = get_session_pid(AliceSpec, <<"escalus-default-resource">>), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), assert_no_offline_msgs(AliceSpec), assert_c2s_state(C2SPid, session_established), escalus_connection:stop(Alice). @@ -832,27 +789,23 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> ConnSteps = connection_steps_to_session(), %% connect bob and alice - BobSpec = escalus_fresh:create_fresh_user(Config, bob), - {ok, Bob, _} = escalus_connection:start(BobSpec), - escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Bob, presence), + {Bob, _} = given_fresh_user(Config, bob), AliceSpec0 = escalus_fresh:create_fresh_user(Config, alice), Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], HookHandlerExtra = start_hook_listener(Resource), {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps ++ [stream_resumption]), - SMID = proplists:get_value(smid, Alice#client.props), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus_connection:get_stanza(Alice, presence), + SMID = client_to_smid(Alice), + process_initial_stanza(Alice), %% Ack the presence stanza escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), escalus:send(Alice, escalus_stanza:sm_ack(1)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-1">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), %% kill alice connection - {ok, C2SPid} = get_session_pid(AliceSpec, Resource), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), 1 = length(get_user_alive_resources(AliceSpec)), @@ -862,8 +815,8 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> ?assertEqual(timeout, wait_for_unacked_msg_hook(0, Resource, 100)), %% send some messages and check if c2s can handle it - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-3">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(common_helper:get_bjid(AliceSpec), <<"msg-4">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-4">>)), escalus:assert(is_chat_message, [<<"msg-3">>], wait_for_unacked_msg_hook(0, Resource, 100)), escalus:assert(is_chat_message, [<<"msg-4">>], wait_for_unacked_msg_hook(0, Resource, 100)), ?assertEqual(timeout, wait_for_unacked_msg_hook(0, Resource, 100)), @@ -878,7 +831,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> ok end, ok), - {ok, NewC2SPid} = get_session_pid(AliceSpec, NewResource), + NewC2SPid = mongoose_helper:get_session_pid(NewAlice, mim()), escalus_connection:kill(NewAlice), wait_for_c2s_state_change(NewC2SPid, resume_session), @@ -963,8 +916,8 @@ resume_session_kills_old_C2S_gracefully(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), - {ok, C2SPid} = get_session_pid(AliceSpec, escalus_client:resource(Alice)), + {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), InitialPresence = setattr(escalus_stanza:presence(<<"available">>), <<"id">>, <<"presence1">>), @@ -981,9 +934,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> wait_for_c2s_state_change(C2SPid, resume_session), %% Resume the session. - SMID = proplists:get_value(smid, Props), - NewSteps = connection_steps_to_stream_resumption(SMID, 1), - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, NewSteps), + NewAlice = connect_with_resume_session(Alice, 1), %% C2S process should die gracefully with Reason=normal. receive @@ -1032,19 +983,16 @@ try_to_resume_stream(Conn, SMID, PrevH) -> buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages) -> Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), - JID = common_helper:get_bjid(Props), + {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), InitialPresence = setattr(escalus_stanza:presence(<<"available">>), <<"id">>, <<"presence1">>), escalus_connection:send(Alice, InitialPresence), Presence = escalus_connection:get_stanza(Alice, presence1), escalus:assert(is_presence, Presence), - Res = <<"escalus-default-resource">>, - {ok, C2SPid} = get_session_pid(AliceSpec, Res), escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), _Presence = escalus_connection:get_stanza(Alice, presence2), %% Bobs sends some messages to Alice. - [escalus:send(Bob, escalus_stanza:chat_to(JID, Msg)) + [escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg)) || Msg <- Messages], %% Alice receives them, but doesn't ack. Stanzas = [escalus_connection:get_stanza(Alice, {msg, I}) @@ -1053,7 +1001,9 @@ buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages) -> || {Msg, Stanza} <- lists:zip(Messages, Stanzas)], %% Alice's connection is violently terminated. escalus_client:kill_connection(Config, Alice), - {C2SPid, proplists:get_value(smid, Props)}. + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + SMID = client_to_smid(Alice), + {C2SPid, SMID}. aggressively_pipelined_resume(Config) -> AliceSpec = [{manual_ack, true}, {parser_opts, [{start_tag, <<"stream:stream">>}]} @@ -1089,20 +1039,14 @@ replies_are_processed_by_resumed_session(Config) -> %% GIVEN a session and registered special IQ handler (added in init_per_testcase), %% that waits for old session process to terminate (at this point new process %% has fully taken over) and then actually sends the reply. - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), - SMID = proplists:get_value(smid, Props), - SMH = escalus_connection:get_sm_h(Alice), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), %% WHEN a client sends IQ request to the special handler... IQReq = escalus_stanza:iq_get(regression_ns(), []), escalus:send(Alice, IQReq), %% ... goes down and session is resumed. - escalus_client:kill_connection(Config, Alice), - Steps2 = connection_steps_to_stream_resumption(SMID, SMH), - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps2), + Alice2 = kill_and_connect_with_resume_session(Alice), %% THEN the client receives the reply properly. IQReply = escalus:wait_for_stanza(Alice2), @@ -1180,22 +1124,16 @@ subscription_requests_are_buffered_properly(Config) -> %% 7. Bob's message is appended to SM buffer in "flush" step %% 8. With bug fixed, the message is retransmitted properly messages_are_properly_flushed_during_resumption(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> % GIVEN (online Bob) and (Alice in resume state); Alice's session is suspended - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), - SMID = proplists:get_value(smid, Props), - InitialPresence = escalus_stanza:presence(<<"available">>), - escalus_connection:send(Alice, InitialPresence), - Presence = escalus:wait_for_stanza(Alice), - escalus:assert(is_presence, Presence), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + process_initial_stanza(Alice), SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), %% The receiver process would stop now wait_for_c2s_state(Alice, resume_session), - {ok, C2SPid} = get_session_pid(AliceSpec, <<"escalus-default-resource">>), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), wait_for_queue_length(C2SPid, 0), ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -1206,7 +1144,7 @@ messages_are_properly_flushed_during_resumption(Config) -> % because new client start will block until old process is resumed MsgBody = <<"flush-regression">>, - spawn(fun() -> + spawn_link(fun() -> wait_for_queue_length(C2SPid, 1), % Bob sends a message... @@ -1219,10 +1157,7 @@ messages_are_properly_flushed_during_resumption(Config) -> % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) end), - - Steps2 = connection_steps_to_stream_resumption(SMID, SMH), - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps2), - + Alice2 = connect_with_resume_session(Alice, SMH), % THEN Alice's new session receives Bob's message RecvMsg = escalus:wait_for_stanza(Alice2), escalus:assert(is_chat_message, [MsgBody], RecvMsg) @@ -1231,19 +1166,13 @@ messages_are_properly_flushed_during_resumption(Config) -> messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> %% the same as messages_are_properly_flushed_during_resumption, %% but tests that buffered by p1_fsm_old messages are delivered - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), - SMID = proplists:get_value(smid, Props), - InitialPresence = escalus_stanza:presence(<<"available">>), - escalus_connection:send(Alice, InitialPresence), - Presence = escalus:wait_for_stanza(Alice), - escalus:assert(is_presence, Presence), + Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + process_initial_stanza(Alice), SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), wait_for_c2s_state(Alice, resume_session), - {ok, C2SPid} = get_session_pid(AliceSpec, <<"escalus-default-resource">>), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), ok = rpc(mim(), sys, suspend, [C2SPid]), %% send some dummy event. ignored by c2s but ensures that @@ -1251,7 +1180,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> rpc(mim(), p1_fsm_old, send_all_state_event, [C2SPid, dummy_event]), MsgBody = <<"flush-regression">>, - spawn(fun() -> + spawn_link(fun() -> wait_for_queue_length(C2SPid, 2), % Bob sends a message... @@ -1264,10 +1193,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) end), - - Steps2 = connection_steps_to_stream_resumption(SMID, SMH), - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps2), - + Alice2 = connect_with_resume_session(Alice, SMH), % THEN Alice's new session receives Bob's message RecvMsg = escalus:wait_for_stanza(Alice2), escalus:assert(is_chat_message, [MsgBody], RecvMsg) @@ -1339,15 +1265,6 @@ wait_for_unacked_msg_hook(Counter, Res, Timeout) -> timeout end. -get_stream_resumption_enabled_fresh_user_smid_and_h(Config, UserAtom) -> - UserSpec = escalus_fresh:create_fresh_user(Config, UserAtom), - Steps = connection_steps_to_enable_stream_resumption(), - {ok, User = #client{props = Props}, _} = escalus_connection:start(UserSpec, Steps), - process_initial_stanza(User), - SMID = proplists:get_value(smid, Props), - SMH = escalus_connection:get_sm_h(User), - {User, UserSpec, SMID, SMH}. - process_initial_stanza(User) -> escalus:send(User, escalus_stanza:presence(<<"available">>)), escalus:assert(is_presence, escalus:wait_for_stanza(User)). @@ -1407,9 +1324,7 @@ wait_until_disconnected(UserSpec) -> #{name => get_user_alive_resources}). monitor_session(Client) -> - UserSpec = Client#client.props, - {resource, Res} = lists:keyfind(resource, 1, UserSpec), - {ok, C2SPid} = get_session_pid(UserSpec, Res), + C2SPid = mongoose_helper:get_session_pid(Client, mim()), erlang:monitor(process, C2SPid). -spec wait_for_process_termination(MRef :: reference()) -> ok. @@ -1417,21 +1332,11 @@ wait_for_process_termination(MRef) -> receive {'DOWN', MRef, _Type, _C2SPid, _Info} -> ok - after timer:seconds(1) -> - ok + after 5000 -> + ct:fail(wait_for_process_termination_timeout) end, ok. -get_session_pid(UserSpec, Resource) -> - {U, S} = get_us_from_spec(UserSpec), - JID = mongoose_helper:make_jid(U, S, Resource), - case rpc(mim(), ejabberd_sm, get_session_pid, [JID]) of - none -> - {error, no_found}; - C2SPid -> - {ok, C2SPid} - end. - get_user_alive_resources(UserSpec) -> {U, S} = get_us_from_spec(UserSpec), JID = mongoose_helper:make_jid(U, S, <<>>), @@ -1466,11 +1371,9 @@ given_fresh_user(Config, UserName) -> given_fresh_user_with_spec(Spec). given_fresh_user_with_spec(Spec) -> - {ok, User = #client{props = Props}, _} = escalus_connection:start(Spec), - escalus:send(User, escalus_stanza:presence(<<"available">>)), - escalus:wait_for_stanza(User), - JID = common_helper:get_bjid(Props), - {User#client{jid = JID}, Spec}. + {ok, User, _} = escalus_connection:start(Spec), + process_initial_stanza(User), + {User, Spec}. wait_for_queue_length(Pid, Length) -> F = fun() -> @@ -1540,3 +1443,53 @@ stop_client_and_wait_for_termination(Alice) -> wait_for_c2s_state(Alice, StateName) -> C2SPid = mongoose_helper:get_session_pid(Alice, mim()), mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, StateName). + +connect_fresh_with_stream_resumption_enabled(Config, UserAtom) -> + UserSpec = escalus_fresh:create_fresh_user(Config, UserAtom), + Steps = connection_steps_to_enable_stream_resumption(), + {ok, User, _} = escalus_connection:start(UserSpec, Steps), + process_initial_stanza(User), + User. + +connect_with_resume_session(Alice, SMH) -> + SMID = client_to_smid(Alice), + C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + Steps = connection_steps_to_stream_resumption(SMID, SMH), + AliceSpec = client_to_spec(Alice), + try + {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps), + Alice2 + catch Class:Error:Stacktrace -> + ct:pal("C2S info ~p", [rpc:pinfo(C2SPid, [messages, current_stacktrace])]), + erlang:raise(Class, Error, Stacktrace) + end. + +kill_and_connect_with_resume_session(Alice) -> + %% Get SMH before killing the old connection + SMH = escalus_connection:get_sm_h(Alice), + escalus_connection:kill(Alice), + connect_with_resume_session(Alice, SMH). + +kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> + SMH = escalus_connection:get_sm_h(Alice), + AliceSpec = client_to_spec(Alice), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_authenticate()), + SMID = client_to_smid(Alice), + %% kill alice connection + escalus_connection:kill(Alice), + %% ensure there is no session + wait_until_disconnected(AliceSpec), + escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), + NewAlice. + +client_to_smid(#client{props = Props}) -> + proplists:get_value(smid, Props). + +client_to_spec(#client{props = Props}) -> + Props. + +given_fresh_user_with_stream_resumption_enabled(Config, Name) -> + Spec = escalus_fresh:create_fresh_user(Config, Name), + {ok, Client, _} = escalus_connection:start([{stream_resumption, true}|proplists:delete(stream_management, Spec)]), + process_initial_stanza(Client), + {Client, Spec}. From 61c294cf47ae821fb9cb3e07f9d31a8b8627182d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 13 Dec 2021 21:29:07 +0100 Subject: [PATCH 02/26] Use connect_fresh function --- big_tests/tests/sm_SUITE.erl | 288 +++++++++++++++++------------------ 1 file changed, 143 insertions(+), 145 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index b8d5c4cc22..2fcb090630 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -276,10 +276,10 @@ client_enables_sm_twice_fails_with_correct_error_stanza(Config) -> session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza(Config) -> %% GIVEN USER WITH STREAM RESUMPTION ENABLED - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + Alice = connect_fresh(Config, alice, sr_presence), SMH = escalus_connection:get_sm_h(Alice), %% WHEN USER RESUMES SESSION FROM NEW CLIENT - Alice2 = connect_with_resume_session(Alice, SMH), + Alice2 = connect_resume(Alice, SMH), process_initial_stanza(Alice2), %% THEN: Old session is gracefully closed with the correct error stanza escalus:assert(is_stream_error, [<<"conflict">>, <<>>], @@ -292,9 +292,9 @@ session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza( session_resumed_and_old_session_dead_doesnt_route_error_to_new_session(Config) -> %% GIVEN USER WITH STREAM RESUMPTION ENABLED - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + Alice = connect_fresh(Config, alice, sr_presence), %% WHEN FIRST SESSION DIES AND USER RESUMES FROM NEW CLIENT - Alice2 = kill_and_connect_with_resume_session(Alice), + Alice2 = kill_and_connect_resume(Alice), process_initial_stanza(Alice2), %% THEN new session does not have any message rerouted false = escalus_client:has_stanzas(Alice2), @@ -406,8 +406,9 @@ client_acks_more_than_sent(Config) -> true = escalus_connection:wait_for_close(Alice,timer:seconds(5)). too_many_unacked_stanzas(Config) -> - {Bob, _} = given_fresh_user(Config, bob), - {Alice, _} = given_fresh_user(Config, alice), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sm_presence), + escalus:wait_for_stanza(Alice), %% wait for ack request [escalus:send(Bob, escalus_stanza:chat_to(Alice, <<(integer_to_binary(N))/binary, ": Hi, Alice!">>)) @@ -422,8 +423,8 @@ server_requests_ack(Config) -> server_requests_ack(Config, 1). server_requests_ack(Config, N) -> - {Bob, _} = given_fresh_user(Config, bob), - {Alice, _} = given_fresh_user(Config, alice), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sm_presence), %% ack request after initial presence maybe_assert_ack_request(1, N, Alice), escalus:send(Bob, escalus_stanza:chat_to(Alice, <<"Hi, Alice!">>)), @@ -453,10 +454,8 @@ server_requests_ack_after_session(Config) -> resend_more_offline_messages_than_buffer_size(Config) -> - ConnSteps = connection_steps_to_session(), - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), + Bob = connect_fresh(Config, bob, session), AliceSpec = escalus_fresh:create_fresh_user(Config, alice), @@ -467,7 +466,7 @@ resend_more_offline_messages_than_buffer_size(Config) -> || I <- lists:seq(1, MessagesToSend)], % connect alice who wants to receive all messages from offline storage - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps++[stream_management]), + {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_mgmt(after_session)), escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), escalus:wait_for_stanzas(Alice, MessagesToSend * 2), %messages and ack requests @@ -488,39 +487,31 @@ resend_more_offline_messages_than_buffer_size(Config) -> resend_unacked_on_reconnection(Config) -> Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], - {Bob, _} = given_fresh_user(Config, bob), - {Alice, AliceSpec0} = given_fresh_user(Config, alice), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sm_presence), + AliceSpec0 = client_to_spec0(Alice), %% Bob sends some messages to Alice. [escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg)) || Msg <- Messages], %% Alice receives the messages. - Stanzas = escalus:wait_for_stanzas(Alice, length(Messages)), - [escalus:assert(is_chat_message, [Msg], Stanza) - || {Msg, Stanza} <- lists:zip(Messages, Stanzas)], + wait_for_messages(Alice, Messages), %% Alice disconnects without acking the messages. stop_client_and_wait_for_termination(Alice), - escalus_connection:stop(Bob), %% Messages go to the offline store. %% Alice receives the messages from the offline store. AliceSpec = [{manual_ack, true} | AliceSpec0], {ok, NewAlice, _} = escalus_connection:start(AliceSpec), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - OfflineMsgs = [escalus_connection:get_stanza(NewAlice, {msg, I}) - || I <- lists:seq(1, length(Messages))], - [escalus:assert(is_chat_message, [Msg], Stanza) - || {Msg, Stanza} <- lists:zip(Messages, OfflineMsgs)], + wait_for_messages(NewAlice, Messages), %% Alice acks the delayed messages so they won't go again %% to the offline store. escalus_connection:send(NewAlice, escalus_stanza:sm_ack(3)). preserve_order(Config) -> - ConnSteps = connection_steps_to_session(), - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), - {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), - - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sr_presence), + AliceSpec = client_to_spec(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"1">>)), %% kill alice connection @@ -530,7 +521,7 @@ preserve_order(Config) -> escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"2">>)), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"3">>)), - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), escalus_connection:send(NewAlice, escalus_stanza:enable_sm([resume])), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"4">>)), @@ -542,7 +533,7 @@ preserve_order(Config) -> receive_all_ordered(NewAlice, 1), % replace connection - {ok, NewAlice2, _} = escalus_connection:start(AliceSpec, ConnSteps), + {ok, NewAlice2, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), % allow messages to go to the offline storage ct:sleep(1000), @@ -571,13 +562,10 @@ receive_all_ordered(Conn, N) -> end. resend_unacked_after_resume_timeout(Config) -> - ConnSteps = connection_steps_to_session(), - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), - {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), - - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sr_presence), + AliceSpec = client_to_spec(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), %% kill alice connection @@ -587,7 +575,7 @@ resend_unacked_after_resume_timeout(Config) -> wait_until_disconnected(AliceSpec), %% alice come back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), Stanzas =[escalus_connection:get_stanza(NewAlice, msg), @@ -602,9 +590,8 @@ resend_unacked_after_resume_timeout(Config) -> resume_expired_session_returns_correct_h(Config) -> %% connect bob and alice - Bob = connect_fresh_with_stream_resumption_enabled(Config, bob), - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), + Bob = connect_fresh(Config, bob, sr_presence), + Alice = connect_fresh(Config, alice, sr_presence), %% Bob sends a message to Alice, and Alice receives it but doesn't acknowledge escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus:wait_for_stanza(Alice), @@ -637,14 +624,11 @@ gc_repeat_after_timeout_does_clean(Config) -> #{name => smid_garbage_collected}). resume_session_state_send_message(Config) -> - ConnSteps = connection_steps_to_session(), - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), - {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), - %% Ack the presence stanza - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), - escalus:send(Alice, escalus_stanza:sm_ack(1)), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sr_presence), + AliceSpec = client_to_spec(Alice), + ack_initial_presence(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), %% kill alice connection @@ -662,7 +646,7 @@ resume_session_state_send_message(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), escalus:assert(is_presence, escalus_connection:get_stanza(NewAlice, presence)), %% now we can resume c2s process of the old connection @@ -681,15 +665,12 @@ resume_session_state_send_message(Config) -> %%for instance it can be done by mod ping resume_session_state_stop_c2s(Config) -> - ConnSteps = connection_steps_to_session(), - - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), - {Alice, AliceSpec} = given_fresh_user_with_stream_resumption_enabled(Config, alice), + Bob = connect_fresh(Config, bob, presence), + Alice = connect_fresh(Config, alice, sr_presence), + AliceSpec = client_to_spec(Alice), - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), - %% Ack presence - escalus_connection:send(Alice, escalus_stanza:sm_ack(1)), + get_ack(Alice), + ack_initial_presence(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), @@ -711,23 +692,22 @@ resume_session_state_stop_c2s(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - escalus:assert(is_presence, escalus_connection:get_stanza(NewAlice, presence)), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec), + process_initial_stanza(NewAlice), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), - escalus_connection:stop(NewAlice), - escalus_connection:stop(Bob). + escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), + escalus_connection:stop(Bob), + escalus_connection:stop(NewAlice). %% This test only verifies the validity of helpers (get_session_pid, %% assert_no_offline_msgs, assert_c2s_state) written for wait_for_resumption %% testcase. session_established(Config) -> - AliceSpec = [{manual_ack, true} - | escalus_fresh:create_fresh_user(Config, alice)], - {Alice, _} = given_fresh_user_with_spec(AliceSpec), + Alice = connect_fresh(Config, alice, presence_with_manual_ack), + AliceSpec = client_to_spec(Alice), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), assert_no_offline_msgs(AliceSpec), assert_c2s_state(C2SPid, session_established), @@ -738,7 +718,7 @@ session_established(Config) -> wait_for_resumption(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], - {Bob, _} = given_fresh_user(Config, bob), + Bob = connect_fresh(Config, bob, session), Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), %% Ensure the c2s process is waiting for resumption. @@ -757,10 +737,9 @@ unacknowledged_message_hook_bounce(Config) -> unacknowledged_message_hook_common(fun unacknowledged_message_hook_bounce/4, Config). unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> - ConnSteps = connection_steps_to_session() ++ [stream_resumption], NewResource = <<"new_", Resource/binary>>, NewSpec = lists:keystore(resource, 1, AliceSpec, {resource, NewResource}), - {ok, NewAlice, _} = escalus_connection:start(NewSpec, ConnSteps), + {ok, NewAlice, _} = escalus_connection:start(NewSpec, connection_steps_to_enable_stream_resumption()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), %% ensure second C2S is registered so all the messages are bounced properly mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(AliceSpec)) end, 2), @@ -771,10 +750,9 @@ unacknowledged_message_hook_offline(Config) -> unacknowledged_message_hook_common(fun unacknowledged_message_hook_offline/4, Config). unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> - ConnSteps = connection_steps_to_session() ++ [stream_resumption], C2SRef = erlang:monitor(process, C2SPid), %%reset the session, so old C2S process is stopped - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, ConnSteps), + {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), %% wait for old C2S termination before send presence. other way %% some of the latest unacknowledged messages can be bounced to %% the new C2S process instead of going to the mod_offline storage. @@ -786,21 +764,20 @@ unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> {Resource, NewAlice}. unacknowledged_message_hook_common(RestartConnectionFN, Config) -> - ConnSteps = connection_steps_to_session(), - %% connect bob and alice - {Bob, _} = given_fresh_user(Config, bob), + Bob = connect_fresh(Config, bob, presence), AliceSpec0 = escalus_fresh:create_fresh_user(Config, alice), Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], HookHandlerExtra = start_hook_listener(Resource), - {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps ++ [stream_resumption]), - SMID = client_to_smid(Alice), + {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), process_initial_stanza(Alice), %% Ack the presence stanza - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, ack)), - escalus:send(Alice, escalus_stanza:sm_ack(1)), + get_ack(Alice), + ack_initial_presence(Alice), + + SMID = client_to_smid(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), @@ -918,12 +895,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> Steps = connection_steps_to_enable_stream_resumption(), {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), - - InitialPresence = setattr(escalus_stanza:presence(<<"available">>), - <<"id">>, <<"presence1">>), - escalus_connection:send(Alice, InitialPresence), - Presence = escalus_connection:get_stanza(Alice, presence1), - escalus:assert(is_presence, Presence), + process_initial_stanza(Alice), %% Monitor the C2S process and disconnect Alice. MonitorRef = erlang:monitor(process, C2SPid), @@ -934,7 +906,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> wait_for_c2s_state_change(C2SPid, resume_session), %% Resume the session. - NewAlice = connect_with_resume_session(Alice, 1), + NewAlice = connect_resume(Alice, 1), %% C2S process should die gracefully with Reason=normal. receive @@ -947,6 +919,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> end, escalus_connection:stop(NewAlice). +%% Connection steps connection_steps_to_authenticate() -> [start_stream, stream_features, @@ -965,7 +938,16 @@ connection_steps_to_enable_stream_mgmt(after_bind) -> connection_steps_to_bind() ++ [stream_management]. connection_steps_to_enable_stream_resumption() -> - connection_steps_to_bind() ++ [session, stream_resumption]. + connection_steps_to_session() ++ [stream_resumption]. + +connection_steps_to_enable_stream_resumption_and_presence() -> + connection_steps_to_enable_stream_resumption() ++ [fun initial_presence_step/2]. + +connection_steps_to_presence() -> + connection_steps_to_session() ++ [fun initial_presence_step/2]. + +connection_steps_to_enable_stream_mgmt_and_presene() -> + connection_steps_to_enable_stream_mgmt(after_session) ++ [fun initial_presence_step/2]. connection_steps_to_stream_resumption(SMID, H) -> connection_steps_to_authenticate() ++ [mk_resume_stream(SMID, H)]. @@ -977,6 +959,10 @@ mk_resume_stream(SMID, PrevH) -> {Conn#client{props = [{smid, SMID} | Props]}, Features} end. +initial_presence_step(Conn, Features) -> + process_initial_stanza(Conn), + {Conn, Features}. + try_to_resume_stream(Conn, SMID, PrevH) -> escalus_connection:send(Conn, escalus_stanza:resume(SMID, PrevH)), escalus_connection:get_stanza(Conn, get_resumed). @@ -1039,14 +1025,14 @@ replies_are_processed_by_resumed_session(Config) -> %% GIVEN a session and registered special IQ handler (added in init_per_testcase), %% that waits for old session process to terminate (at this point new process %% has fully taken over) and then actually sends the reply. - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), + Alice = connect_fresh(Config, alice, sr_presence), %% WHEN a client sends IQ request to the special handler... IQReq = escalus_stanza:iq_get(regression_ns(), []), escalus:send(Alice, IQReq), %% ... goes down and session is resumed. - Alice2 = kill_and_connect_with_resume_session(Alice), + Alice2 = kill_and_connect_resume(Alice), %% THEN the client receives the reply properly. IQReply = escalus:wait_for_stanza(Alice2), @@ -1066,25 +1052,19 @@ replies_are_processed_by_resumed_session(Config) -> subscription_requests_are_buffered_properly(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], MsgBody = <<"buffered">>, - SubPredFun = fun(S) -> escalus_pred:is_presence_with_type(<<"subscribe">>, S) end, - AvailablePredFun = fun(S) -> escalus_pred:is_presence_with_type(<<"available">>, S) end, - MsgPredFun = fun(S) -> escalus_pred:is_chat_message(MsgBody, S) end, - escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> % GIVEN Bob's pending subscription to Alice's presence - AliceUser = proplists:get_value(username, AliceSpec), - AliceServer = proplists:get_value(server, AliceSpec), - AliceJid = <>, + AliceJid = common_helper:get_bjid(AliceSpec), escalus:send(Bob, escalus_stanza:presence_direct(AliceJid, <<"subscribe">>)), _RosterPushReq = escalus:wait_for_stanza(Bob), % WHEN Alice becomes online... - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), InitialPresence = escalus_stanza:presence(<<"available">>), escalus_connection:send(Alice, InitialPresence), - AvailableAndSubPresences = escalus:wait_for_stanzas(Alice, 2), - escalus:assert_many([SubPredFun, AvailablePredFun], AvailableAndSubPresences), + %% subscribe could come before the initial presence + escalus:assert_many([is_presence(<<"available">>), is_presence(<<"subscribe">>)], + escalus:wait_for_stanzas(Alice, 2)), % ...and Bob sends a message to Alice... escalus:send(Bob, escalus_stanza:chat_to(Alice, MsgBody)), @@ -1104,8 +1084,8 @@ subscription_requests_are_buffered_properly(Config) -> % * buffered subscription request because it is dropped by ejabberd_sm % because it's treated like repeated sub request to bare JID, so it's not % processed by any sub req handler (like mod_roster) - SubReqAndInitialPresence = escalus:wait_for_stanzas(Alice2, 2), - escalus:assert_many([AvailablePredFun, MsgPredFun], SubReqAndInitialPresence), + escalus:assert_many([is_presence(<<"available">>), is_chat(MsgBody)], + escalus:wait_for_stanzas(Alice2, 2)), escalus_connection:stop(Alice2) end). @@ -1126,8 +1106,7 @@ subscription_requests_are_buffered_properly(Config) -> messages_are_properly_flushed_during_resumption(Config) -> escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> % GIVEN (online Bob) and (Alice in resume state); Alice's session is suspended - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), - process_initial_stanza(Alice), + Alice = connect_fresh(Config, alice, sr_presence), SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), %% The receiver process would stop now @@ -1157,7 +1136,7 @@ messages_are_properly_flushed_during_resumption(Config) -> % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) end), - Alice2 = connect_with_resume_session(Alice, SMH), + Alice2 = connect_resume(Alice, SMH), % THEN Alice's new session receives Bob's message RecvMsg = escalus:wait_for_stanza(Alice2), escalus:assert(is_chat_message, [MsgBody], RecvMsg) @@ -1167,8 +1146,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> %% the same as messages_are_properly_flushed_during_resumption, %% but tests that buffered by p1_fsm_old messages are delivered escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> - Alice = connect_fresh_with_stream_resumption_enabled(Config, alice), - process_initial_stanza(Alice), + Alice = connect_fresh(Config, alice, sr_presence), SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), wait_for_c2s_state(Alice, resume_session), @@ -1193,16 +1171,14 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) end), - Alice2 = connect_with_resume_session(Alice, SMH), + Alice2 = connect_resume(Alice, SMH), % THEN Alice's new session receives Bob's message RecvMsg = escalus:wait_for_stanza(Alice2), escalus:assert(is_chat_message, [MsgBody], RecvMsg) end). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_session(), - {ok, Alice, _Features} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, session), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm()), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1210,9 +1186,7 @@ no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> escalus_connection:stop(Alice). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_session(), - {ok, Alice, _Features} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, session), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm([resume])), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1366,14 +1340,13 @@ clear_sm_session_table() -> is_chat(Content) -> fun(Stanza) -> escalus_pred:is_chat_message(Content, Stanza) end. -given_fresh_user(Config, UserName) -> - Spec = escalus_fresh:create_fresh_user(Config, UserName), - given_fresh_user_with_spec(Spec). +is_presence(Type) -> + fun(Stanza) -> escalus_pred:is_presence_with_type(Type, Stanza) end. given_fresh_user_with_spec(Spec) -> {ok, User, _} = escalus_connection:start(Spec), process_initial_stanza(User), - {User, Spec}. + User. wait_for_queue_length(Pid, Length) -> F = fun() -> @@ -1444,32 +1417,6 @@ wait_for_c2s_state(Alice, StateName) -> C2SPid = mongoose_helper:get_session_pid(Alice, mim()), mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, StateName). -connect_fresh_with_stream_resumption_enabled(Config, UserAtom) -> - UserSpec = escalus_fresh:create_fresh_user(Config, UserAtom), - Steps = connection_steps_to_enable_stream_resumption(), - {ok, User, _} = escalus_connection:start(UserSpec, Steps), - process_initial_stanza(User), - User. - -connect_with_resume_session(Alice, SMH) -> - SMID = client_to_smid(Alice), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), - Steps = connection_steps_to_stream_resumption(SMID, SMH), - AliceSpec = client_to_spec(Alice), - try - {ok, Alice2, _} = escalus_connection:start(AliceSpec, Steps), - Alice2 - catch Class:Error:Stacktrace -> - ct:pal("C2S info ~p", [rpc:pinfo(C2SPid, [messages, current_stacktrace])]), - erlang:raise(Class, Error, Stacktrace) - end. - -kill_and_connect_with_resume_session(Alice) -> - %% Get SMH before killing the old connection - SMH = escalus_connection:get_sm_h(Alice), - escalus_connection:kill(Alice), - connect_with_resume_session(Alice, SMH). - kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> SMH = escalus_connection:get_sm_h(Alice), AliceSpec = client_to_spec(Alice), @@ -1488,8 +1435,59 @@ client_to_smid(#client{props = Props}) -> client_to_spec(#client{props = Props}) -> Props. -given_fresh_user_with_stream_resumption_enabled(Config, Name) -> +client_to_spec0(#client{props = Props}) -> + lists:foldl(fun proplists:delete/2, Props, [host, stream_id, resource]). + +get_ack(Client) -> + escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). + +ack_initial_presence(Client) -> + escalus_connection:send(Client, escalus_stanza:sm_ack(1)). + +connect_fresh(Config, Name, FinalStep) -> Spec = escalus_fresh:create_fresh_user(Config, Name), - {ok, Client, _} = escalus_connection:start([{stream_resumption, true}|proplists:delete(stream_management, Spec)]), - process_initial_stanza(Client), - {Client, Spec}. + Steps = final_step_to_steps(FinalStep), + ExtraOpts = final_step_to_extra_opts(FinalStep), + {ok, Client, _Features} = escalus_connection:start(ExtraOpts ++ Spec, Steps), + Client. + +final_step_to_steps(session) -> + connection_steps_to_session(); +final_step_to_steps(presence) -> + connection_steps_to_presence(); +final_step_to_steps(presence_with_manual_ack) -> + connection_steps_to_presence(); +final_step_to_steps(sr_presence) -> + connection_steps_to_enable_stream_resumption_and_presence(); +final_step_to_steps(sm_presence) -> + connection_steps_to_enable_stream_mgmt_and_presene(). + +final_step_to_extra_opts(presence_with_manual_ack) -> + [{manual_ack, true}]; +final_step_to_extra_opts(_) -> + []. + +kill_and_connect_resume(Client) -> + %% Get SMH before killing the old connection + SMH = escalus_connection:get_sm_h(Client), + escalus_connection:kill(Client), + connect_resume(Client, SMH). + +connect_resume(Client, SMH) -> + SMID = client_to_smid(Client), + Spec = client_to_spec(Client), + C2SPid = mongoose_helper:get_session_pid(Client, mim()), + Steps = connection_steps_to_stream_resumption(SMID, SMH), + try + {ok, Client2, _} = escalus_connection:start(Spec, Steps), + Client2 + catch Class:Error:Stacktrace -> + ct:pal("C2S info ~p", [rpc:pinfo(C2SPid, [messages, current_stacktrace])]), + erlang:raise(Class, Error, Stacktrace) + end. + +wait_for_messages(Alice, Texts) -> + Stanzas = escalus:wait_for_stanzas(Alice, length(Texts)), + [escalus:assert(is_chat_message, [Text], Stanza) + || {Text, Stanza} <- lists:zip(Texts, Stanzas)], + ok. From 2e9b40a0ecdd76d0b6e7f8dc1a878369aaa6e0cb Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 13 Dec 2021 22:12:45 +0100 Subject: [PATCH 03/26] Cleaner short cases with connect_fresh --- big_tests/tests/sm_SUITE.erl | 52 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 2fcb090630..3219123261 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -302,9 +302,7 @@ session_resumed_and_old_session_dead_doesnt_route_error_to_new_session(Config) - escalus_connection:stop(Alice2). basic_ack(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_session), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sm_after_session), escalus_connection:send(Alice, escalus_stanza:roster_get()), escalus:assert(is_roster_result, escalus_connection:get_stanza(Alice, roster_result)), @@ -316,9 +314,7 @@ basic_ack(Config) -> %% - SM is enabled *before* the session is established %% - is sent *before* the session is established h_ok_before_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_bind), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sm_after_bind), escalus_connection:send(Alice, escalus_stanza:sm_request()), escalus:assert(is_sm_ack, [0], escalus_connection:get_stanza(Alice, stream_mgmt_ack)). @@ -327,9 +323,7 @@ h_ok_before_session(Config) -> %% - SM is enabled *before* the session is established %% - is sent *after* the session is established h_ok_after_session_enabled_before_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_bind) ++ [session], - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sm_before_session), escalus_connection:send(Alice, escalus_stanza:sm_request()), escalus:assert(is_sm_ack, [1], escalus_connection:get_stanza(Alice, stream_mgmt_ack)). @@ -338,9 +332,7 @@ h_ok_after_session_enabled_before_session(Config) -> %% - SM is enabled *after* the session is established %% - is sent *after* the session is established h_ok_after_session_enabled_after_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_session), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sm_after_session), escalus_connection:send(Alice, escalus_stanza:roster_get()), escalus:assert(is_roster_result, escalus_connection:get_stanza(Alice, roster_result)), @@ -388,8 +380,7 @@ h_non_given_closes_stream_gracefully(ConfigIn) -> end). client_acks_more_than_sent(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec), + Alice = connect_fresh(Config, alice, sm_after_session), escalus:send(Alice, escalus_stanza:sm_ack(5)), StreamErrorStanza = escalus:wait_for_stanza(Alice), %% Assert "undefined-condition" children @@ -403,13 +394,12 @@ client_acks_more_than_sent(Config) -> <<"0">> = exml_query:attr(HandledCountSubElement, <<"send-count">>), %% Assert graceful stream end escalus:assert(is_stream_end, escalus_connection:get_stanza(Alice, stream_end)), - true = escalus_connection:wait_for_close(Alice,timer:seconds(5)). + true = escalus_connection:wait_for_close(Alice, timer:seconds(5)). too_many_unacked_stanzas(Config) -> Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sm_presence), - - escalus:wait_for_stanza(Alice), %% wait for ack request + get_ack(Alice), [escalus:send(Bob, escalus_stanza:chat_to(Alice, <<(integer_to_binary(N))/binary, ": Hi, Alice!">>)) || N <- lists:seq(1,?SMALL_SM_BUFFER)], @@ -447,9 +437,7 @@ server_requests_ack_freq_2(Config) -> server_requests_ack(Config1, 2). server_requests_ack_after_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_bind) ++ [session], - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sm_before_session), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, stream_mgmt_req)). @@ -550,7 +538,6 @@ receive_all_ordered(Conn, N) -> #xmlel{} = Stanza -> NN = case Stanza#xmlel.name of <<"message">> -> - %ct:pal("~p~n", [Stanza]), escalus:assert(is_chat_message, [integer_to_binary(N)], Stanza), N + 1; _ -> @@ -578,12 +565,8 @@ resend_unacked_after_resume_timeout(Config) -> {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - Stanzas =[escalus_connection:get_stanza(NewAlice, msg), - escalus_connection:get_stanza(NewAlice, msg)], - - escalus_new_assert:mix_match([is_presence, - is_chat(<<"msg-1">>)], - Stanzas), + escalus_new_assert:mix_match([is_presence, is_chat(<<"msg-1">>)], + escalus:wait_for_stanzas(NewAlice, 2)), escalus_connection:stop(Bob), escalus_connection:stop(NewAlice). @@ -831,10 +814,7 @@ resume_session(Config) -> {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), %% Alice receives the unacked messages from the previous %% interrupted session. - Stanzas = [escalus_connection:get_stanza(Alice, {msg, I}) - || I <- lists:seq(1, 3)], - [escalus:assert(is_chat_message, [Msg], Stanza) - || {Msg, Stanza} <- lists:zip(Messages, Stanzas)], + wait_for_messages(Alice, Messages), %% Alice acks the received messages. escalus_connection:send(Alice, escalus_stanza:sm_ack(5)), escalus_connection:stop(Alice) @@ -946,7 +926,7 @@ connection_steps_to_enable_stream_resumption_and_presence() -> connection_steps_to_presence() -> connection_steps_to_session() ++ [fun initial_presence_step/2]. -connection_steps_to_enable_stream_mgmt_and_presene() -> +connection_steps_to_enable_stream_mgmt_and_presence() -> connection_steps_to_enable_stream_mgmt(after_session) ++ [fun initial_presence_step/2]. connection_steps_to_stream_resumption(SMID, H) -> @@ -1460,7 +1440,13 @@ final_step_to_steps(presence_with_manual_ack) -> final_step_to_steps(sr_presence) -> connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sm_presence) -> - connection_steps_to_enable_stream_mgmt_and_presene(). + connection_steps_to_enable_stream_mgmt_and_presence(); +final_step_to_steps(sm_after_session) -> + connection_steps_to_enable_stream_mgmt(after_session); +final_step_to_steps(sm_after_bind) -> + connection_steps_to_enable_stream_mgmt(after_bind); +final_step_to_steps(sm_before_session) -> + connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]. final_step_to_extra_opts(presence_with_manual_ack) -> [{manual_ack, true}]; From 7cafc3092940882579cf44ff5d921aed030a0762 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 12:21:53 +0100 Subject: [PATCH 04/26] Use connect_fresh for simple cases --- big_tests/tests/sm_SUITE.erl | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 3219123261..333b1c1632 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -229,14 +229,10 @@ server_announces_sm(Config) -> server_enables_sm_before_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_bind), - {ok, _, _} = escalus_connection:start(AliceSpec, Steps). + connect_fresh(Config, alice, sm_after_bind). server_enables_sm_after_session(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_enable_stream_mgmt(after_session), - {ok, _, _} = escalus_connection:start(AliceSpec, Steps). + connect_fresh(Config, alice, sm_after_session). server_returns_failed_after_start(Config) -> server_returns_failed(Config, []). @@ -245,10 +241,7 @@ server_returns_failed_after_auth(Config) -> server_returns_failed(Config, [authenticate]). server_enables_resumption(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - %% Assert matches {ok, _, _, _} - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, sr_presence), escalus_connection:stop(Alice). server_returns_failed(Config, ConnActions) -> From e3c0fe94653b979207d34c6cf9cebda71ef03c48 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 12:33:48 +0100 Subject: [PATCH 05/26] Remove sleep in preserve_order --- big_tests/tests/mongoose_helper.erl | 7 +++++++ big_tests/tests/offline_SUITE.erl | 7 +------ big_tests/tests/sm_SUITE.erl | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 8e2234c078..1a675763f2 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -48,6 +48,7 @@ -export([print_debug_info_for_module/1]). -export([backup_and_set_config/2, backup_and_set_config_option/3, change_config_option/3]). -export([restore_config/1, restore_config_option/2]). +-export([wait_for_n_offline_messages/2]). -import(distributed_helper, [mim/0, rpc/4]). @@ -562,3 +563,9 @@ do_restore_config_option(Option, {ok, Value}) -> rpc(mim(), mongoose_config, set_opt, [Option, Value]); do_restore_config_option(Option, {error, not_found}) -> rpc(mim(), mongoose_config, unset_opt, [Option]). + +wait_for_n_offline_messages(Client, N) -> + LUser = escalus_utils:jid_to_lower(escalus_client:username(Client)), + LServer = escalus_utils:jid_to_lower(escalus_client:server(Client)), + WaitFn = fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, + mongoose_helper:wait_until(WaitFn, N). diff --git a/big_tests/tests/offline_SUITE.erl b/big_tests/tests/offline_SUITE.erl index 6b7e5bf7d3..d3acfd310e 100644 --- a/big_tests/tests/offline_SUITE.erl +++ b/big_tests/tests/offline_SUITE.erl @@ -18,6 +18,7 @@ -define(NS_FEATURE_MSGOFFLINE, <<"msgoffline">>). -import(domain_helper, [host_type/0]). +-import(mongoose_helper, [wait_for_n_offline_messages/2]). %%%=================================================================== %%% Suite configuration @@ -370,12 +371,6 @@ has_element_with_ns(Stanza, Element, NS) -> %%%=================================================================== %%% Helpers %%%=================================================================== -wait_for_n_offline_messages(Client, N) -> - LUser = escalus_utils:jid_to_lower(escalus_client:username(Client)), - LServer = escalus_utils:jid_to_lower(escalus_client:server(Client)), - WaitFn = fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, - mongoose_helper:wait_until(WaitFn, N). - logout(Config, User) -> mongoose_helper:logout_user(Config, User). diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 333b1c1632..52f4d1241b 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -511,22 +511,22 @@ preserve_order(Config) -> escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"6">>)), - receive_all_ordered(NewAlice, 1), + receive_all_ordered(NewAlice, 1, 6), % replace connection {ok, NewAlice2, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), % allow messages to go to the offline storage - ct:sleep(1000), + mongoose_helper:wait_for_n_offline_messages(NewAlice, 6), escalus_connection:send(NewAlice2, escalus_stanza:presence(<<"available">>)), % receves messages in correct order - receive_all_ordered(NewAlice2, 1), + receive_all_ordered(NewAlice2, 1, 6), escalus_connection:stop(Bob), escalus_connection:stop(NewAlice2). -receive_all_ordered(Conn, N) -> +receive_all_ordered(Conn, N, Total) -> case catch escalus_connection:get_stanza(Conn, msg) of #xmlel{} = Stanza -> NN = case Stanza#xmlel.name of @@ -536,8 +536,8 @@ receive_all_ordered(Conn, N) -> _ -> N end, - receive_all_ordered(Conn, NN); - _Error -> + receive_all_ordered(Conn, NN, Total); + _Error when N =:= Total -> ok end. From fff5c6dd855f4be2ceb9c76b0aade718f0f76051 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 13:19:45 +0100 Subject: [PATCH 06/26] Use connect_spec --- big_tests/tests/sm_SUITE.erl | 88 +++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 52f4d1241b..f88ac134c0 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -256,10 +256,7 @@ server_returns_failed(Config, ConnActions) -> escalus_connection:get_stanza(Alice, enable_sm_failed)). client_enables_sm_twice_fails_with_correct_error_stanza(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_session(), - {ok, Alice, Features} = escalus_connection:start(AliceSpec, Steps), - escalus_session:stream_management(Alice, Features), + Alice = connect_fresh(Config, alice, sm_session), escalus_connection:send(Alice, escalus_stanza:enable_sm()), escalus:assert(is_sm_failed, [<<"unexpected-request">>], escalus_connection:get_stanza(Alice, enable_sm_failed)), @@ -447,7 +444,7 @@ resend_more_offline_messages_than_buffer_size(Config) -> || I <- lists:seq(1, MessagesToSend)], % connect alice who wants to receive all messages from offline storage - {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_mgmt(after_session)), + Alice = connect_spec(AliceSpec, sm_after_session), escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), escalus:wait_for_stanzas(Alice, MessagesToSend * 2), %messages and ack requests @@ -470,7 +467,7 @@ resend_unacked_on_reconnection(Config) -> Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sm_presence), - AliceSpec0 = client_to_spec0(Alice), + AliceSpec = client_to_spec0(Alice), %% Bob sends some messages to Alice. [escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg)) || Msg <- Messages], @@ -480,8 +477,7 @@ resend_unacked_on_reconnection(Config) -> stop_client_and_wait_for_termination(Alice), %% Messages go to the offline store. %% Alice receives the messages from the offline store. - AliceSpec = [{manual_ack, true} | AliceSpec0], - {ok, NewAlice, _} = escalus_connection:start(AliceSpec), + NewAlice = connect_spec(AliceSpec, session_with_manual_ack), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), wait_for_messages(NewAlice, Messages), %% Alice acks the delayed messages so they won't go again @@ -497,12 +493,12 @@ preserve_order(Config) -> %% kill alice connection escalus_connection:kill(Alice), - wait_until_disconnected(AliceSpec), + wait_until_disconnected(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"2">>)), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"3">>)), - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), + NewAlice = connect_spec(AliceSpec, session), escalus_connection:send(NewAlice, escalus_stanza:enable_sm([resume])), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"4">>)), @@ -514,7 +510,7 @@ preserve_order(Config) -> receive_all_ordered(NewAlice, 1, 6), % replace connection - {ok, NewAlice2, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), + NewAlice2 = connect_spec(AliceSpec, session), % allow messages to go to the offline storage mongoose_helper:wait_for_n_offline_messages(NewAlice, 6), @@ -552,10 +548,10 @@ resend_unacked_after_resume_timeout(Config) -> escalus_connection:kill(Alice), %% ensure there is no session - wait_until_disconnected(AliceSpec), + wait_until_disconnected(Alice), %% alice come back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), + NewAlice = connect_spec(AliceSpec, session), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), escalus_new_assert:mix_match([is_presence, is_chat(<<"msg-1">>)], @@ -603,7 +599,6 @@ resume_session_state_send_message(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sr_presence), - AliceSpec = client_to_spec(Alice), ack_initial_presence(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), @@ -611,8 +606,7 @@ resume_session_state_send_message(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice, mim()), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), - - 1 = length(get_user_alive_resources(AliceSpec)), + assert_alive_resources(Alice, 1), %% send some messages and check if c2s can handle it escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), @@ -643,7 +637,6 @@ resume_session_state_send_message(Config) -> resume_session_state_stop_c2s(Config) -> Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sr_presence), - AliceSpec = client_to_spec(Alice), get_ack(Alice), ack_initial_presence(Alice), @@ -660,7 +653,7 @@ resume_session_state_stop_c2s(Config) -> % kill alice connection escalus_connection:kill(Alice), % session should be alive - 1 = length(get_user_alive_resources(AliceSpec)), + assert_alive_resources(Alice, 1), rpc(mim(), ejabberd_c2s, stop, [C2SPid]), wait_for_c2s_state_change(C2SPid, resume_session), %% suspend the process to ensure that Alice has enough time to reconnect, @@ -668,8 +661,7 @@ resume_session_state_stop_c2s(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec), - process_initial_stanza(NewAlice), + NewAlice = connect_spec(AliceSpec, presence), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), @@ -718,7 +710,7 @@ unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> {ok, NewAlice, _} = escalus_connection:start(NewSpec, connection_steps_to_enable_stream_resumption()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), %% ensure second C2S is registered so all the messages are bounced properly - mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(AliceSpec)) end, 2), + wait_for_resource_count(Alice, 2), ok = rpc(mim(), sys, terminate, [C2SPid, normal]), {NewResource, NewAlice}. @@ -761,7 +753,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> C2SPid = mongoose_helper:get_session_pid(Alice, mim()), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), - 1 = length(get_user_alive_resources(AliceSpec)), + assert_alive_resources(Alice, 1), escalus:assert(is_chat_message, [<<"msg-1">>], wait_for_unacked_msg_hook(0, Resource, 100)), escalus:assert(is_chat_message, [<<"msg-2">>], wait_for_unacked_msg_hook(0, Resource, 100)), @@ -826,7 +818,7 @@ resume_session_with_wrong_h_does_not_leak_sessions(Config) -> Resumed = try_to_resume_stream(Alice, SMID, 30), escalus:assert(is_stream_error, [<<"undefined-condition">>, <<>>], Resumed), - [] = get_user_present_resources(AliceSpec), + [] = get_user_present_resources(Alice), {error, smid_not_found} = get_sid_by_stream_id(SMID), escalus_connection:wait_for_close(Alice, timer:seconds(5)) end). @@ -842,7 +834,7 @@ resume_session_with_wrong_namespace_is_a_noop(Config) -> Attrs2 = lists:keyreplace(<<"xmlns">>, 1, Attrs, {<<"xmlns">>, <<"not-stream-mgnt">>}), escalus_connection:send(Alice, Resume#xmlel{attrs = Attrs2}), escalus_assert:has_no_stanzas(Alice), - [] = get_user_present_resources(AliceSpec), + [] = get_user_present_resources(Alice), true = escalus_connection:is_connected(Alice), escalus_connection:stop(Alice). @@ -853,22 +845,16 @@ resume_dead_session_results_in_item_not_found(Config) -> session_resumption_expects_item_not_found(Config, SMID). session_resumption_expects_item_not_found(Config, SMID) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_authenticate(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, auth), Resumed = try_to_resume_stream(Alice, SMID, 2), escalus:assert(is_sm_failed, [<<"item-not-found">>], Resumed), - [] = get_user_present_resources(AliceSpec), + [] = get_user_present_resources(Alice), true = escalus_connection:is_connected(Alice), escalus_connection:stop(Alice). resume_session_kills_old_C2S_gracefully(Config) -> - AliceSpec = [{manual_ack, true} - | escalus_fresh:create_fresh_user(Config, alice)], - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, presence_with_manual_ack), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), - process_initial_stanza(Alice), %% Monitor the C2S process and disconnect Alice. MonitorRef = erlang:monitor(process, C2SPid), @@ -1266,9 +1252,12 @@ extract_state_name(SysStatus) -> [_, _, _, _, [_, {data, FSMData} | _]]} = SysStatus, proplists:get_value("StateName", FSMData). -wait_until_disconnected(UserSpec) -> - mongoose_helper:wait_until(fun() -> get_user_alive_resources(UserSpec) =:= [] end, true, - #{name => get_user_alive_resources}). +wait_until_disconnected(Client) -> + wait_for_resource_count(Client, 0). + +wait_for_resource_count(Client, N) -> + mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(Client)) end, + N, #{name => get_user_alive_resources}). monitor_session(Client) -> C2SPid = mongoose_helper:get_session_pid(Client, mim()), @@ -1284,12 +1273,17 @@ wait_for_process_termination(MRef) -> end, ok. -get_user_alive_resources(UserSpec) -> +assert_alive_resources(Alice, N) -> + N = length(get_user_alive_resources(Alice)). + +get_user_alive_resources(Client) -> + UserSpec = client_to_spec(Client), {U, S} = get_us_from_spec(UserSpec), JID = mongoose_helper:make_jid(U, S, <<>>), rpc(mim(), ejabberd_sm, get_user_resources, [JID]). -get_user_present_resources(UserSpec) -> +get_user_present_resources(Client) -> + UserSpec = client_to_spec(Client), {U, S} = get_us_from_spec(UserSpec), JID = mongoose_helper:make_jid(U, S, <<>>), rpc(mim(), ejabberd_sm, get_user_present_resources, [JID]). @@ -1398,7 +1392,7 @@ kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> %% kill alice connection escalus_connection:kill(Alice), %% ensure there is no session - wait_until_disconnected(AliceSpec), + wait_until_disconnected(Alice), escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), NewAlice. @@ -1408,8 +1402,9 @@ client_to_smid(#client{props = Props}) -> client_to_spec(#client{props = Props}) -> Props. +%% Returns the original spec, without the dynamically added fields client_to_spec0(#client{props = Props}) -> - lists:foldl(fun proplists:delete/2, Props, [host, stream_id, resource]). + lists:foldl(fun proplists:delete/2, Props, [stream_id, resource]). get_ack(Client) -> escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). @@ -1419,17 +1414,24 @@ ack_initial_presence(Client) -> connect_fresh(Config, Name, FinalStep) -> Spec = escalus_fresh:create_fresh_user(Config, Name), - Steps = final_step_to_steps(FinalStep), ExtraOpts = final_step_to_extra_opts(FinalStep), - {ok, Client, _Features} = escalus_connection:start(ExtraOpts ++ Spec, Steps), + connect_spec(ExtraOpts ++ Spec, FinalStep). + +connect_spec(Spec, FinalStep) -> + Steps = final_step_to_steps(FinalStep), + {ok, Client, _} = escalus_connection:start(Spec, Steps), Client. +final_step_to_steps(auth) -> + connection_steps_to_authenticate(); final_step_to_steps(session) -> connection_steps_to_session(); final_step_to_steps(presence) -> connection_steps_to_presence(); final_step_to_steps(presence_with_manual_ack) -> connection_steps_to_presence(); +final_step_to_steps(session_with_manual_ack) -> + connection_steps_to_session(); final_step_to_steps(sr_presence) -> connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sm_presence) -> @@ -1443,6 +1445,8 @@ final_step_to_steps(sm_before_session) -> final_step_to_extra_opts(presence_with_manual_ack) -> [{manual_ack, true}]; +final_step_to_extra_opts(session_with_manual_ack) -> + [{manual_ack, true}]; final_step_to_extra_opts(_) -> []. From 6cb92ee9969d7356c134707d9bfd117d8e30ab0c Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 13:32:01 +0100 Subject: [PATCH 07/26] Add connect_same --- big_tests/tests/sm_SUITE.erl | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index f88ac134c0..de95973736 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -616,7 +616,7 @@ resume_session_state_send_message(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), + NewAlice = connect_spec(AliceSpec, session), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), escalus:assert(is_presence, escalus_connection:get_stanza(NewAlice, presence)), %% now we can resume c2s process of the old connection @@ -661,7 +661,7 @@ resume_session_state_stop_c2s(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - NewAlice = connect_spec(AliceSpec, presence), + NewAlice = connect_same(Alice, presence), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), @@ -674,10 +674,9 @@ resume_session_state_stop_c2s(Config) -> %% assert_no_offline_msgs, assert_c2s_state) written for wait_for_resumption %% testcase. session_established(Config) -> - Alice = connect_fresh(Config, alice, presence_with_manual_ack), - AliceSpec = client_to_spec(Alice), + Alice = connect_fresh(Config, alice, presence), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), - assert_no_offline_msgs(AliceSpec), + assert_no_offline_msgs(Alice), assert_c2s_state(C2SPid, session_established), escalus_connection:stop(Alice). @@ -690,7 +689,7 @@ wait_for_resumption(Config) -> Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), %% Ensure the c2s process is waiting for resumption. - assert_no_offline_msgs(AliceSpec), + assert_no_offline_msgs_spec(AliceSpec), wait_for_c2s_state_change(C2SPid, resume_session). unacknowledged_message_hook_resume(Config) -> @@ -861,7 +860,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> escalus_client:kill_connection(Config, Alice), %% Ensure the c2s process is waiting for resumption. - assert_no_offline_msgs(AliceSpec), + assert_no_offline_msgs(Alice), wait_for_c2s_state_change(C2SPid, resume_session), %% Resume the session. @@ -1220,14 +1219,15 @@ discard_offline_messages(Config, User, H) -> discard_offline_messages(Config, User, H+1) end. -assert_no_offline_msgs(Spec) -> +assert_no_offline_msgs(Client) -> + Spec = client_to_spec(Alice), + assert_no_offline_msgs_spec(Spec). + +assert_no_offline_msgs_spec(Spec) -> Username = escalus_utils:jid_to_lower(proplists:get_value(username, Spec)), Server = proplists:get_value(server, Spec), 0 = mongoose_helper:total_offline_messages({Username, Server}). -assert_no_offline_msgs() -> - 0 = mongoose_helper:total_offline_messages(). - wait_for_c2s_state_change(C2SPid, NewStateName) -> mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, NewStateName, #{name => get_c2s_state, time_left => timer:seconds(5)}). @@ -1422,6 +1422,9 @@ connect_spec(Spec, FinalStep) -> {ok, Client, _} = escalus_connection:start(Spec, Steps), Client. +connect_same(Client, FinalStep) -> + connect_spec(client_to_spec0(Client), FinalStep). + final_step_to_steps(auth) -> connection_steps_to_authenticate(); final_step_to_steps(session) -> From 927ee04634c3a546d4bb6f84238666100124026a Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 14:44:53 +0100 Subject: [PATCH 08/26] Simplify buffer_unacked_messages_and_die --- big_tests/tests/sm_SUITE.erl | 90 +++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index de95973736..72f92bdcee 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -256,7 +256,7 @@ server_returns_failed(Config, ConnActions) -> escalus_connection:get_stanza(Alice, enable_sm_failed)). client_enables_sm_twice_fails_with_correct_error_stanza(Config) -> - Alice = connect_fresh(Config, alice, sm_session), + Alice = connect_fresh(Config, alice, sm_before_session), escalus_connection:send(Alice, escalus_stanza:enable_sm()), escalus:assert(is_sm_failed, [<<"unexpected-request">>], escalus_connection:get_stanza(Alice, enable_sm_failed)), @@ -464,22 +464,21 @@ resend_more_offline_messages_than_buffer_size(Config) -> escalus_connection:stop(Bob). resend_unacked_on_reconnection(Config) -> - Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], + Texts = three_texts(), Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sm_presence), AliceSpec = client_to_spec0(Alice), %% Bob sends some messages to Alice. - [escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg)) - || Msg <- Messages], + send_messages(Bob, Alice, Texts), %% Alice receives the messages. - wait_for_messages(Alice, Messages), + wait_for_messages(Alice, Texts), %% Alice disconnects without acking the messages. stop_client_and_wait_for_termination(Alice), %% Messages go to the offline store. %% Alice receives the messages from the offline store. NewAlice = connect_spec(AliceSpec, session_with_manual_ack), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - wait_for_messages(NewAlice, Messages), + wait_for_messages(NewAlice, Texts), %% Alice acks the delayed messages so they won't go again %% to the offline store. escalus_connection:send(NewAlice, escalus_stanza:sm_ack(3)). @@ -616,7 +615,7 @@ resume_session_state_send_message(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - NewAlice = connect_spec(AliceSpec, session), + NewAlice = connect_same(Alice, session), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), escalus:assert(is_presence, escalus_connection:get_stanza(NewAlice, presence)), %% now we can resume c2s process of the old connection @@ -683,11 +682,10 @@ session_established(Config) -> %% Ensure that after a violent disconnection, %% the c2s waits for resumption (but don't resume yet). wait_for_resumption(Config) -> - AliceSpec = [{manual_ack, true} - | escalus_fresh:create_fresh_user(Config, alice)], + AliceSpec = escalus_fresh:create_fresh_user(Config, alice), Bob = connect_fresh(Config, bob, session), - Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], - {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), + Texts = three_texts(), + {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts), %% Ensure the c2s process is waiting for resumption. assert_no_offline_msgs_spec(AliceSpec), wait_for_c2s_state_change(C2SPid, resume_session). @@ -709,7 +707,7 @@ unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> {ok, NewAlice, _} = escalus_connection:start(NewSpec, connection_steps_to_enable_stream_resumption()), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), %% ensure second C2S is registered so all the messages are bounced properly - wait_for_resource_count(Alice, 2), + wait_for_resource_count(NewAlice, 2), ok = rpc(mim(), sys, terminate, [C2SPid, normal]), {NewResource, NewAlice}. @@ -738,8 +736,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], HookHandlerExtra = start_hook_listener(Resource), - {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), - process_initial_stanza(Alice), + Alice = connect_spec(AliceSpec, sr_presence), %% Ack the presence stanza get_ack(Alice), ack_initial_presence(Alice), @@ -790,15 +787,15 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> resume_session(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], - Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], + Texts = three_texts(), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> - {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), + {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts), %% Resume the session. - Steps = connection_steps_to_stream_resumption(SMID, 2), + Steps = connection_steps_to_stream_resumption(SMID, 1), {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), %% Alice receives the unacked messages from the previous %% interrupted session. - wait_for_messages(Alice, Messages), + wait_for_messages(Alice, Texts), %% Alice acks the received messages. escalus_connection:send(Alice, escalus_stanza:sm_ack(5)), escalus_connection:stop(Alice) @@ -807,7 +804,7 @@ resume_session(Config) -> resume_session_with_wrong_h_does_not_leak_sessions(Config) -> AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], - Messages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], + Messages = three_texts(), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), @@ -852,7 +849,7 @@ session_resumption_expects_item_not_found(Config, SMID) -> escalus_connection:stop(Alice). resume_session_kills_old_C2S_gracefully(Config) -> - Alice = connect_fresh(Config, alice, presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), %% Monitor the C2S process and disconnect Alice. @@ -925,24 +922,12 @@ try_to_resume_stream(Conn, SMID, PrevH) -> escalus_connection:send(Conn, escalus_stanza:resume(SMID, PrevH)), escalus_connection:get_stanza(Conn, get_resumed). -buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages) -> - Steps = connection_steps_to_enable_stream_resumption(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), - InitialPresence = setattr(escalus_stanza:presence(<<"available">>), - <<"id">>, <<"presence1">>), - escalus_connection:send(Alice, InitialPresence), - Presence = escalus_connection:get_stanza(Alice, presence1), - escalus:assert(is_presence, Presence), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), - _Presence = escalus_connection:get_stanza(Alice, presence2), +buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> + Alice = connect_spec(AliceSpec, sr_presence_with_manual_ack), %% Bobs sends some messages to Alice. - [escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg)) - || Msg <- Messages], + send_messages(Bob, Alice, Texts), %% Alice receives them, but doesn't ack. - Stanzas = [escalus_connection:get_stanza(Alice, {msg, I}) - || I <- lists:seq(1, 3)], - [escalus:assert(is_chat_message, [Msg], Stanza) - || {Msg, Stanza} <- lists:zip(Messages, Stanzas)], + wait_for_messages(Alice, Texts), %% Alice's connection is violently terminated. escalus_client:kill_connection(Config, Alice), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), @@ -952,7 +937,7 @@ buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages) -> aggressively_pipelined_resume(Config) -> AliceSpec = [{manual_ack, true}, {parser_opts, [{start_tag, <<"stream:stream">>}]} | escalus_fresh:create_fresh_user(Config, alice)], - UnackedMessages = [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>], + UnackedMessages = three_texts(), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, UnackedMessages), %% Resume the session. @@ -1017,9 +1002,8 @@ subscription_requests_are_buffered_properly(Config) -> _RosterPushReq = escalus:wait_for_stanza(Bob), % WHEN Alice becomes online... - {ok, Alice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), - InitialPresence = escalus_stanza:presence(<<"available">>), - escalus_connection:send(Alice, InitialPresence), + Alice = connect_spec(AliceSpec, sr_session), + send_initial_presence(Alice), %% subscribe could come before the initial presence escalus:assert_many([is_presence(<<"available">>), is_presence(<<"subscribe">>)], escalus:wait_for_stanzas(Alice, 2)), @@ -1033,7 +1017,7 @@ subscription_requests_are_buffered_properly(Config) -> escalus_client:kill_connection(Config, Alice), % ...and reconnects with session replacement. - {ok, Alice2, _} = escalus_connection:start(AliceSpec, connection_steps_to_session()), + Alice2 = connect_spec(AliceSpec, session), % THEN Alice receives (without sending initial presence): % * buffered available presence (because it's addressed to full JID) @@ -1197,8 +1181,12 @@ wait_for_unacked_msg_hook(Counter, Res, Timeout) -> timeout end. +send_initial_presence(User) -> + InitialPresence = escalus_stanza:presence(<<"available">>), + escalus_connection:send(User, InitialPresence). + process_initial_stanza(User) -> - escalus:send(User, escalus_stanza:presence(<<"available">>)), + send_initial_presence(User), escalus:assert(is_presence, escalus:wait_for_stanza(User)). discard_offline_messages(Config, UserName) -> @@ -1220,7 +1208,7 @@ discard_offline_messages(Config, User, H) -> end. assert_no_offline_msgs(Client) -> - Spec = client_to_spec(Alice), + Spec = client_to_spec(Client), assert_no_offline_msgs_spec(Spec). assert_no_offline_msgs_spec(Spec) -> @@ -1435,8 +1423,12 @@ final_step_to_steps(presence_with_manual_ack) -> connection_steps_to_presence(); final_step_to_steps(session_with_manual_ack) -> connection_steps_to_session(); +final_step_to_steps(sr_session) -> + connection_steps_to_enable_stream_resumption(); final_step_to_steps(sr_presence) -> connection_steps_to_enable_stream_resumption_and_presence(); +final_step_to_steps(sr_presence_with_manual_ack) -> + connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sm_presence) -> connection_steps_to_enable_stream_mgmt_and_presence(); final_step_to_steps(sm_after_session) -> @@ -1446,6 +1438,8 @@ final_step_to_steps(sm_after_bind) -> final_step_to_steps(sm_before_session) -> connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]. +final_step_to_extra_opts(sr_presence_with_manual_ack) -> + [{manual_ack, true}]; final_step_to_extra_opts(presence_with_manual_ack) -> [{manual_ack, true}]; final_step_to_extra_opts(session_with_manual_ack) -> @@ -1472,8 +1466,18 @@ connect_resume(Client, SMH) -> erlang:raise(Class, Error, Stacktrace) end. +send_messages(Bob, Alice, Texts) -> + [escalus:send(Bob, escalus_stanza:chat_to(Alice, Text)) + || Text <- Texts]. + wait_for_messages(Alice, Texts) -> Stanzas = escalus:wait_for_stanzas(Alice, length(Texts)), + assert_same_length(Stanzas, Texts), [escalus:assert(is_chat_message, [Text], Stanza) || {Text, Stanza} <- lists:zip(Texts, Stanzas)], ok. + +assert_same_length(List1, List2) when length(List1) =:= length(List2) -> ok. + +three_texts() -> + [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>]. From 31a3eb5fcd362b8540a414958b2de4f78811cd9d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:27:44 +0100 Subject: [PATCH 09/26] Specify manual_ack each time --- big_tests/tests/sm_SUITE.erl | 86 ++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 72f92bdcee..34e8655ea7 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -42,7 +42,8 @@ groups() -> {stream_mgmt_disabled, [], stream_mgmt_disabled_cases()}, {manual_ack_freq_long_session_timeout, [parallel], [preserve_order]}, {unacknowledged_message_hook, [parallel], unacknowledged_message_hook()}], - ct_helper:repeat_all_until_all_ok(G). +% ct_helper:repeat_all_until_all_ok(G). + G. parallel_test_cases() -> @@ -128,15 +129,13 @@ init_per_group(Group, Config) when Group =:= unacknowledged_message_hook; Group =:= manual_ack_freq_long_session_timeout; Group =:= parallel_manual_ack_freq_1; Group =:= manual_ack_freq_2 -> - dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)), - escalus_users:update_userspec(Config, alice, manual_ack, true); + dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)); init_per_group(stale_h, Config) -> - %% All tests in this group set up modules in init_per_testcase escalus_users:update_userspec(Config, alice, manual_ack, true); init_per_group(stream_mgmt_disabled, Config) -> dynamic_modules:stop(host_type(), ?MOD_SM), rpc(mim(), mnesia, delete_table, [sm_session]), - escalus_users:update_userspec(Config, alice, manual_ack, true); + Config; init_per_group(Group, Config) -> dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)), Config. @@ -388,7 +387,7 @@ client_acks_more_than_sent(Config) -> too_many_unacked_stanzas(Config) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sm_presence), + Alice = connect_fresh(Config, alice, sm_presence_with_manual_ack), get_ack(Alice), [escalus:send(Bob, escalus_stanza:chat_to(Alice, <<(integer_to_binary(N))/binary, ": Hi, Alice!">>)) @@ -402,9 +401,12 @@ too_many_unacked_stanzas(Config) -> server_requests_ack(Config) -> server_requests_ack(Config, 1). +server_requests_ack_freq_2(Config) -> + server_requests_ack(Config, 2). + server_requests_ack(Config, N) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sm_presence), + Alice = connect_fresh(Config, alice, sm_presence_with_manual_ack), %% ack request after initial presence maybe_assert_ack_request(1, N, Alice), escalus:send(Bob, escalus_stanza:chat_to(Alice, <<"Hi, Alice!">>)), @@ -422,19 +424,13 @@ maybe_assert_ack_request(StanzasRec, AckRequests, Alice) -> end, StanzasRec. -server_requests_ack_freq_2(Config) -> - Config1 = escalus_users:update_userspec(Config, alice, manual_ack, true), - server_requests_ack(Config1, 2). - server_requests_ack_after_session(Config) -> - Alice = connect_fresh(Config, alice, sm_before_session), + Alice = connect_fresh(Config, alice, sm_before_session_with_manual_ack), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, stream_mgmt_req)). - resend_more_offline_messages_than_buffer_size(Config) -> %% connect bob and alice - Bob = connect_fresh(Config, bob, session), - + Bob = connect_fresh(Config, bob, presence), AliceSpec = escalus_fresh:create_fresh_user(Config, alice), % sent some messages - more than unacked buffer size @@ -444,13 +440,14 @@ resend_more_offline_messages_than_buffer_size(Config) -> || I <- lists:seq(1, MessagesToSend)], % connect alice who wants to receive all messages from offline storage - Alice = connect_spec(AliceSpec, sm_after_session), - escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), + Alice = connect_spec(AliceSpec, sm_after_session_with_manual_ack), + mongoose_helper:wait_for_n_offline_messages(Alice, MessagesToSend), + send_initial_presence(Alice), escalus:wait_for_stanzas(Alice, MessagesToSend * 2), %messages and ack requests escalus_connection:get_stanza(Alice, presence), - escalus:wait_for_stanza(Alice), % ack request + get_ack(Alice), % ack request % confirm messages + presence escalus_connection:send(Alice, escalus_stanza:sm_ack(4)), @@ -597,7 +594,7 @@ gc_repeat_after_timeout_does_clean(Config) -> resume_session_state_send_message(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sr_presence), + Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), ack_initial_presence(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), @@ -635,7 +632,7 @@ resume_session_state_send_message(Config) -> %%for instance it can be done by mod ping resume_session_state_stop_c2s(Config) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sr_presence), + Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), get_ack(Alice), ack_initial_presence(Alice), @@ -660,12 +657,12 @@ resume_session_state_stop_c2s(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - NewAlice = connect_same(Alice, presence), + NewAlice = connect_same(Alice, presence_with_manual_ack), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), - escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), + escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(NewAlice, msg)), escalus_connection:stop(Bob), escalus_connection:stop(NewAlice). @@ -694,7 +691,7 @@ unacknowledged_message_hook_resume(Config) -> unacknowledged_message_hook_common(fun unacknowledged_message_hook_resume/4, Config). unacknowledged_message_hook_resume(AliceSpec, Resource, SMID, _C2SPid) -> - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_stream_resumption(SMID, 1)), + NewAlice = connect_spec(AliceSpec, {resume, SMID, 1}), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), {Resource, NewAlice}. @@ -704,7 +701,7 @@ unacknowledged_message_hook_bounce(Config) -> unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> NewResource = <<"new_", Resource/binary>>, NewSpec = lists:keystore(resource, 1, AliceSpec, {resource, NewResource}), - {ok, NewAlice, _} = escalus_connection:start(NewSpec, connection_steps_to_enable_stream_resumption()), + NewAlice = connect_spec(NewSpec, sr_session_with_manual_ack), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), %% ensure second C2S is registered so all the messages are bounced properly wait_for_resource_count(NewAlice, 2), @@ -717,7 +714,7 @@ unacknowledged_message_hook_offline(Config) -> unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> C2SRef = erlang:monitor(process, C2SPid), %%reset the session, so old C2S process is stopped - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_enable_stream_resumption()), + NewAlice = connect_spec(AliceSpec, sr_session_with_manual_ack), %% wait for old C2S termination before send presence. other way %% some of the latest unacknowledged messages can be bounced to %% the new C2S process instead of going to the mod_offline storage. @@ -736,7 +733,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], HookHandlerExtra = start_hook_listener(Resource), - Alice = connect_spec(AliceSpec, sr_presence), + Alice = connect_spec(AliceSpec, sr_presence_with_manual_ack), %% Ack the presence stanza get_ack(Alice), ack_initial_presence(Alice), @@ -1120,7 +1117,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> end). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> - Alice = connect_fresh(Config, alice, session), + Alice = connect_fresh(Config, alice, session_with_manual_ack), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm()), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1128,7 +1125,7 @@ no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> escalus_connection:stop(Alice). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption(Config) -> - Alice = connect_fresh(Config, alice, session), + Alice = connect_fresh(Config, alice, session_with_manual_ack), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm([resume])), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1402,12 +1399,12 @@ ack_initial_presence(Client) -> connect_fresh(Config, Name, FinalStep) -> Spec = escalus_fresh:create_fresh_user(Config, Name), - ExtraOpts = final_step_to_extra_opts(FinalStep), - connect_spec(ExtraOpts ++ Spec, FinalStep). + connect_spec(Spec, FinalStep). connect_spec(Spec, FinalStep) -> Steps = final_step_to_steps(FinalStep), - {ok, Client, _} = escalus_connection:start(Spec, Steps), + ExtraOpts = final_step_to_extra_opts(FinalStep), + {ok, Client, _} = escalus_connection:start(ExtraOpts ++ Spec, Steps), Client. connect_same(Client, FinalStep) -> @@ -1425,24 +1422,39 @@ final_step_to_steps(session_with_manual_ack) -> connection_steps_to_session(); final_step_to_steps(sr_session) -> connection_steps_to_enable_stream_resumption(); +final_step_to_steps(sr_session_with_manual_ack) -> + connection_steps_to_enable_stream_resumption(); final_step_to_steps(sr_presence) -> connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sr_presence_with_manual_ack) -> connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sm_presence) -> connection_steps_to_enable_stream_mgmt_and_presence(); +final_step_to_steps(sm_presence_with_manual_ack) -> + connection_steps_to_enable_stream_mgmt_and_presence(); final_step_to_steps(sm_after_session) -> connection_steps_to_enable_stream_mgmt(after_session); +final_step_to_steps(sm_after_session_with_manual_ack) -> + connection_steps_to_enable_stream_mgmt(after_session); final_step_to_steps(sm_after_bind) -> connection_steps_to_enable_stream_mgmt(after_bind); final_step_to_steps(sm_before_session) -> - connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]. - -final_step_to_extra_opts(sr_presence_with_manual_ack) -> - [{manual_ack, true}]; -final_step_to_extra_opts(presence_with_manual_ack) -> + connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; +final_step_to_steps(sm_before_session_with_manual_ack) -> + connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; +final_step_to_steps({resume, SMID, H}) -> + connection_steps_to_stream_resumption(SMID, H). + +final_step_to_extra_opts(X) + when X =:= sr_presence_with_manual_ack; + X =:= presence_with_manual_ack; + X =:= session_with_manual_ack; + X =:= sr_session_with_manual_ack; + X =:= sm_presence_with_manual_ack; + X =:= sm_before_session_with_manual_ack; + X =:= sm_after_session_with_manual_ack -> [{manual_ack, true}]; -final_step_to_extra_opts(session_with_manual_ack) -> +final_step_to_extra_opts({resume, _, _}) -> [{manual_ack, true}]; final_step_to_extra_opts(_) -> []. From c406be844248f975885b91a1dd3d0af8c9c0b8c8 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:37:38 +0100 Subject: [PATCH 10/26] Provide ack argument into connect_fresh --- big_tests/tests/sm_SUITE.erl | 78 ++++++++++++++---------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 34e8655ea7..023abe9c07 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -387,7 +387,7 @@ client_acks_more_than_sent(Config) -> too_many_unacked_stanzas(Config) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sm_presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sm_presence, manual), get_ack(Alice), [escalus:send(Bob, escalus_stanza:chat_to(Alice, <<(integer_to_binary(N))/binary, ": Hi, Alice!">>)) @@ -406,7 +406,7 @@ server_requests_ack_freq_2(Config) -> server_requests_ack(Config, N) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sm_presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sm_presence, manual), %% ack request after initial presence maybe_assert_ack_request(1, N, Alice), escalus:send(Bob, escalus_stanza:chat_to(Alice, <<"Hi, Alice!">>)), @@ -425,7 +425,7 @@ maybe_assert_ack_request(StanzasRec, AckRequests, Alice) -> StanzasRec. server_requests_ack_after_session(Config) -> - Alice = connect_fresh(Config, alice, sm_before_session_with_manual_ack), + Alice = connect_fresh(Config, alice, sm_before_session, manual), escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Alice, stream_mgmt_req)). resend_more_offline_messages_than_buffer_size(Config) -> @@ -440,7 +440,7 @@ resend_more_offline_messages_than_buffer_size(Config) -> || I <- lists:seq(1, MessagesToSend)], % connect alice who wants to receive all messages from offline storage - Alice = connect_spec(AliceSpec, sm_after_session_with_manual_ack), + Alice = connect_spec(AliceSpec, sm_after_session, manual), mongoose_helper:wait_for_n_offline_messages(Alice, MessagesToSend), send_initial_presence(Alice), @@ -473,7 +473,7 @@ resend_unacked_on_reconnection(Config) -> stop_client_and_wait_for_termination(Alice), %% Messages go to the offline store. %% Alice receives the messages from the offline store. - NewAlice = connect_spec(AliceSpec, session_with_manual_ack), + NewAlice = connect_spec(AliceSpec, session, manual), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), wait_for_messages(NewAlice, Texts), %% Alice acks the delayed messages so they won't go again @@ -594,7 +594,7 @@ gc_repeat_after_timeout_does_clean(Config) -> resume_session_state_send_message(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sr_presence, manual), ack_initial_presence(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), @@ -632,7 +632,7 @@ resume_session_state_send_message(Config) -> %%for instance it can be done by mod ping resume_session_state_stop_c2s(Config) -> Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sr_presence, manual), get_ack(Alice), ack_initial_presence(Alice), @@ -657,7 +657,7 @@ resume_session_state_stop_c2s(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - NewAlice = connect_same(Alice, presence_with_manual_ack), + NewAlice = connect_same(Alice, presence, manual), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), @@ -691,7 +691,7 @@ unacknowledged_message_hook_resume(Config) -> unacknowledged_message_hook_common(fun unacknowledged_message_hook_resume/4, Config). unacknowledged_message_hook_resume(AliceSpec, Resource, SMID, _C2SPid) -> - NewAlice = connect_spec(AliceSpec, {resume, SMID, 1}), + NewAlice = connect_spec(AliceSpec, {resume, SMID, 1}, manual), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), {Resource, NewAlice}. @@ -701,7 +701,7 @@ unacknowledged_message_hook_bounce(Config) -> unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> NewResource = <<"new_", Resource/binary>>, NewSpec = lists:keystore(resource, 1, AliceSpec, {resource, NewResource}), - NewAlice = connect_spec(NewSpec, sr_session_with_manual_ack), + NewAlice = connect_spec(NewSpec, sr_session, manual), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), %% ensure second C2S is registered so all the messages are bounced properly wait_for_resource_count(NewAlice, 2), @@ -714,7 +714,7 @@ unacknowledged_message_hook_offline(Config) -> unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> C2SRef = erlang:monitor(process, C2SPid), %%reset the session, so old C2S process is stopped - NewAlice = connect_spec(AliceSpec, sr_session_with_manual_ack), + NewAlice = connect_spec(AliceSpec, sr_session, manual), %% wait for old C2S termination before send presence. other way %% some of the latest unacknowledged messages can be bounced to %% the new C2S process instead of going to the mod_offline storage. @@ -733,7 +733,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], HookHandlerExtra = start_hook_listener(Resource), - Alice = connect_spec(AliceSpec, sr_presence_with_manual_ack), + Alice = connect_spec(AliceSpec, sr_presence, manual), %% Ack the presence stanza get_ack(Alice), ack_initial_presence(Alice), @@ -846,7 +846,7 @@ session_resumption_expects_item_not_found(Config, SMID) -> escalus_connection:stop(Alice). resume_session_kills_old_C2S_gracefully(Config) -> - Alice = connect_fresh(Config, alice, sr_presence_with_manual_ack), + Alice = connect_fresh(Config, alice, sr_presence, manual), C2SPid = mongoose_helper:get_session_pid(Alice, mim()), %% Monitor the C2S process and disconnect Alice. @@ -920,7 +920,7 @@ try_to_resume_stream(Conn, SMID, PrevH) -> escalus_connection:get_stanza(Conn, get_resumed). buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> - Alice = connect_spec(AliceSpec, sr_presence_with_manual_ack), + Alice = connect_spec(AliceSpec, sr_presence, manual), %% Bobs sends some messages to Alice. send_messages(Bob, Alice, Texts), %% Alice receives them, but doesn't ack. @@ -1117,7 +1117,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> end). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> - Alice = connect_fresh(Config, alice, session_with_manual_ack), + Alice = connect_fresh(Config, alice, session, manual), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm()), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1125,7 +1125,7 @@ no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt(Config) -> escalus_connection:stop(Alice). no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption(Config) -> - Alice = connect_fresh(Config, alice, session_with_manual_ack), + Alice = connect_fresh(Config, alice, session, manual), %% Should not crash anything! escalus_connection:send(Alice, escalus_stanza:enable_sm([resume])), Response = escalus_connection:get_stanza(Alice, service_unavailable), @@ -1398,17 +1398,29 @@ ack_initial_presence(Client) -> escalus_connection:send(Client, escalus_stanza:sm_ack(1)). connect_fresh(Config, Name, FinalStep) -> + connect_fresh(Config, Name, FinalStep, auto). + +connect_fresh(Config, Name, FinalStep, Ack) -> Spec = escalus_fresh:create_fresh_user(Config, Name), - connect_spec(Spec, FinalStep). + connect_spec(Spec, FinalStep, Ack). connect_spec(Spec, FinalStep) -> + connect_spec(Spec, FinalStep, auto). + +connect_spec(Spec, FinalStep, Ack) -> Steps = final_step_to_steps(FinalStep), - ExtraOpts = final_step_to_extra_opts(FinalStep), + ExtraOpts = ack_to_extra_opts(Ack), {ok, Client, _} = escalus_connection:start(ExtraOpts ++ Spec, Steps), Client. connect_same(Client, FinalStep) -> - connect_spec(client_to_spec0(Client), FinalStep). + connect_same(Client, FinalStep, auto). + +connect_same(Client, FinalStep, Ack) -> + connect_spec(client_to_spec0(Client), FinalStep, Ack). + +ack_to_extra_opts(auto) -> []; +ack_to_extra_opts(manual) -> [{manual_ack, true}]. final_step_to_steps(auth) -> connection_steps_to_authenticate(); @@ -1416,49 +1428,21 @@ final_step_to_steps(session) -> connection_steps_to_session(); final_step_to_steps(presence) -> connection_steps_to_presence(); -final_step_to_steps(presence_with_manual_ack) -> - connection_steps_to_presence(); -final_step_to_steps(session_with_manual_ack) -> - connection_steps_to_session(); final_step_to_steps(sr_session) -> connection_steps_to_enable_stream_resumption(); -final_step_to_steps(sr_session_with_manual_ack) -> - connection_steps_to_enable_stream_resumption(); final_step_to_steps(sr_presence) -> connection_steps_to_enable_stream_resumption_and_presence(); -final_step_to_steps(sr_presence_with_manual_ack) -> - connection_steps_to_enable_stream_resumption_and_presence(); final_step_to_steps(sm_presence) -> connection_steps_to_enable_stream_mgmt_and_presence(); -final_step_to_steps(sm_presence_with_manual_ack) -> - connection_steps_to_enable_stream_mgmt_and_presence(); final_step_to_steps(sm_after_session) -> connection_steps_to_enable_stream_mgmt(after_session); -final_step_to_steps(sm_after_session_with_manual_ack) -> - connection_steps_to_enable_stream_mgmt(after_session); final_step_to_steps(sm_after_bind) -> connection_steps_to_enable_stream_mgmt(after_bind); final_step_to_steps(sm_before_session) -> connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; -final_step_to_steps(sm_before_session_with_manual_ack) -> - connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; final_step_to_steps({resume, SMID, H}) -> connection_steps_to_stream_resumption(SMID, H). -final_step_to_extra_opts(X) - when X =:= sr_presence_with_manual_ack; - X =:= presence_with_manual_ack; - X =:= session_with_manual_ack; - X =:= sr_session_with_manual_ack; - X =:= sm_presence_with_manual_ack; - X =:= sm_before_session_with_manual_ack; - X =:= sm_after_session_with_manual_ack -> - [{manual_ack, true}]; -final_step_to_extra_opts({resume, _, _}) -> - [{manual_ack, true}]; -final_step_to_extra_opts(_) -> - []. - kill_and_connect_resume(Client) -> %% Get SMH before killing the old connection SMH = escalus_connection:get_sm_h(Client), From 48f99df6b7c3033bf06364b84596b8307b5ca162 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:42:51 +0100 Subject: [PATCH 11/26] More connect_spec usage --- big_tests/tests/sm_SUITE.erl | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 023abe9c07..2dbbb66c3b 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -782,14 +782,12 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> escalus_connection:stop(Bob). resume_session(Config) -> - AliceSpec = [{manual_ack, true} - | escalus_fresh:create_fresh_user(Config, alice)], + AliceSpec = escalus_fresh:create_fresh_user(Config, alice), Texts = three_texts(), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts), %% Resume the session. - Steps = connection_steps_to_stream_resumption(SMID, 1), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_spec(AliceSpec, {resume, SMID, 1}, manual), %% Alice receives the unacked messages from the previous %% interrupted session. wait_for_messages(Alice, Texts), @@ -799,15 +797,12 @@ resume_session(Config) -> end). resume_session_with_wrong_h_does_not_leak_sessions(Config) -> - AliceSpec = [{manual_ack, true} - | escalus_fresh:create_fresh_user(Config, alice)], + AliceSpec = escalus_fresh:create_fresh_user(Config, alice), Messages = three_texts(), escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> - {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), %% Resume the session. - Steps = connection_steps_to_authenticate(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_spec(AliceSpec, auth, manual), Resumed = try_to_resume_stream(Alice, SMID, 30), escalus:assert(is_stream_error, [<<"undefined-condition">>, <<>>], Resumed), @@ -820,9 +815,7 @@ resume_session_with_wrong_sid_returns_item_not_found(Config) -> session_resumption_expects_item_not_found(Config, <<"wrong-sid">>). resume_session_with_wrong_namespace_is_a_noop(Config) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - Steps = connection_steps_to_authenticate(), - {ok, Alice, _} = escalus_connection:start(AliceSpec, Steps), + Alice = connect_fresh(Config, alice, auth), #xmlel{attrs = Attrs} = Resume = escalus_stanza:resume(<<"doesnt_matter">>, 4), Attrs2 = lists:keyreplace(<<"xmlns">>, 1, Attrs, {<<"xmlns">>, <<"not-stream-mgnt">>}), escalus_connection:send(Alice, Resume#xmlel{attrs = Attrs2}), From 00d415333932a8b30df6b8c43ab1806a91fd26a8 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:44:10 +0100 Subject: [PATCH 12/26] Remove node argument from get_session_pid --- big_tests/tests/mongoose_helper.erl | 4 ++++ big_tests/tests/sm_SUITE.erl | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 1a675763f2..3bbbdef537 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -35,6 +35,7 @@ -export([make_jid/2]). -export([make_jid/3]). -export([make_jid_noprep/3]). +-export([get_session_pid/1]). -export([get_session_pid/2]). -export([get_session_info/2]). -export([wait_for_route_message_count/2]). @@ -441,6 +442,9 @@ make_jid(User, Server) -> make_jid(User, Server, Resource) -> jid:make(User, Server, Resource). +get_session_pid(User, Node) -> + get_session_pid(User, mim()). + get_session_pid(User, Node) -> Resource = escalus_client:resource(User), Username = escalus_client:username(User), diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 2dbbb66c3b..1eb675cc9d 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -358,7 +358,7 @@ h_non_given_closes_stream_gracefully(ConfigIn) -> Config = escalus_users:update_userspec(ConfigIn, alice, stream_management, true), escalus:fresh_story(Config, [{alice,1}], fun(Alice) -> - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), escalus:send(Alice, AStanza), escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], @@ -599,7 +599,7 @@ resume_session_state_send_message(Config) -> escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), %% kill alice connection - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), assert_alive_resources(Alice, 1), @@ -641,7 +641,7 @@ resume_session_state_stop_c2s(Config) -> escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), %% get pid of c2s - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), %% Wait c2s process to process our presence ack. %% Otherwise, we can receive two initial presences sometimes. wait_for_c2s_unacked_count(C2SPid, 1), @@ -671,7 +671,7 @@ resume_session_state_stop_c2s(Config) -> %% testcase. session_established(Config) -> Alice = connect_fresh(Config, alice, presence), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), assert_no_offline_msgs(Alice), assert_c2s_state(C2SPid, session_established), escalus_connection:stop(Alice). @@ -743,7 +743,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), %% kill alice connection - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), wait_for_c2s_state_change(C2SPid, resume_session), assert_alive_resources(Alice, 1), @@ -769,7 +769,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> ok end, ok), - NewC2SPid = mongoose_helper:get_session_pid(NewAlice, mim()), + NewC2SPid = mongoose_helper:get_session_pid(NewAlice), escalus_connection:kill(NewAlice), wait_for_c2s_state_change(NewC2SPid, resume_session), @@ -840,7 +840,7 @@ session_resumption_expects_item_not_found(Config, SMID) -> resume_session_kills_old_C2S_gracefully(Config) -> Alice = connect_fresh(Config, alice, sr_presence, manual), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), %% Monitor the C2S process and disconnect Alice. MonitorRef = erlang:monitor(process, C2SPid), @@ -920,7 +920,7 @@ buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> wait_for_messages(Alice, Texts), %% Alice's connection is violently terminated. escalus_client:kill_connection(Config, Alice), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), SMID = client_to_smid(Alice), {C2SPid, SMID}. @@ -1044,7 +1044,7 @@ messages_are_properly_flushed_during_resumption(Config) -> %% The receiver process would stop now wait_for_c2s_state(Alice, resume_session), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), wait_for_queue_length(C2SPid, 0), ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -1082,7 +1082,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), wait_for_c2s_state(Alice, resume_session), - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), ok = rpc(mim(), sys, suspend, [C2SPid]), %% send some dummy event. ignored by c2s but ensures that @@ -1238,7 +1238,7 @@ wait_for_resource_count(Client, N) -> N, #{name => get_user_alive_resources}). monitor_session(Client) -> - C2SPid = mongoose_helper:get_session_pid(Client, mim()), + C2SPid = mongoose_helper:get_session_pid(Client), erlang:monitor(process, C2SPid). -spec wait_for_process_termination(MRef :: reference()) -> ok. @@ -1359,7 +1359,7 @@ stop_client_and_wait_for_termination(Alice) -> ok = wait_for_process_termination(C2SRef). wait_for_c2s_state(Alice, StateName) -> - C2SPid = mongoose_helper:get_session_pid(Alice, mim()), + C2SPid = mongoose_helper:get_session_pid(Alice), mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, StateName). kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> @@ -1445,7 +1445,7 @@ kill_and_connect_resume(Client) -> connect_resume(Client, SMH) -> SMID = client_to_smid(Client), Spec = client_to_spec(Client), - C2SPid = mongoose_helper:get_session_pid(Client, mim()), + C2SPid = mongoose_helper:get_session_pid(Client), Steps = connection_steps_to_stream_resumption(SMID, SMH), try {ok, Client2, _} = escalus_connection:start(Spec, Steps), From 2705b1ade10d576c6dca75b642557ab3b916dcb3 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:48:44 +0100 Subject: [PATCH 13/26] Remove wait_for_c2s_state --- big_tests/tests/sm_SUITE.erl | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 1eb675cc9d..6a569c392f 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -1042,9 +1042,9 @@ messages_are_properly_flushed_during_resumption(Config) -> SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), %% The receiver process would stop now - wait_for_c2s_state(Alice, resume_session), - C2SPid = mongoose_helper:get_session_pid(Alice), + wait_for_c2s_state_change(C2SPid, resume_session), + wait_for_queue_length(C2SPid, 0), ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -1081,8 +1081,8 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> Alice = connect_fresh(Config, alice, sr_presence), SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), - wait_for_c2s_state(Alice, resume_session), C2SPid = mongoose_helper:get_session_pid(Alice), + wait_for_c2s_state_change(C2SPid, resume_session), ok = rpc(mim(), sys, suspend, [C2SPid]), %% send some dummy event. ignored by c2s but ensures that @@ -1358,10 +1358,6 @@ stop_client_and_wait_for_termination(Alice) -> escalus_connection:stop(Alice), ok = wait_for_process_termination(C2SRef). -wait_for_c2s_state(Alice, StateName) -> - C2SPid = mongoose_helper:get_session_pid(Alice), - mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, StateName). - kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> SMH = escalus_connection:get_sm_h(Alice), AliceSpec = client_to_spec(Alice), From f202966843ba169ede75c113d4d37d34be38f6dd Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 16:57:44 +0100 Subject: [PATCH 14/26] Move wait_for_c2s_state_name into mongoose_helper --- big_tests/tests/mongoose_helper.erl | 18 ++++++++++++-- big_tests/tests/sm_SUITE.erl | 38 +++++++++-------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 3bbbdef537..1c674e00de 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -50,6 +50,7 @@ -export([backup_and_set_config/2, backup_and_set_config_option/3, change_config_option/3]). -export([restore_config/1, restore_config_option/2]). -export([wait_for_n_offline_messages/2]). +-export([wait_for_c2s_state_name/2, get_c2s_state_name/1]). -import(distributed_helper, [mim/0, rpc/4]). @@ -442,7 +443,7 @@ make_jid(User, Server) -> make_jid(User, Server, Resource) -> jid:make(User, Server, Resource). -get_session_pid(User, Node) -> +get_session_pid(User) -> get_session_pid(User, mim()). get_session_pid(User, Node) -> @@ -572,4 +573,17 @@ wait_for_n_offline_messages(Client, N) -> LUser = escalus_utils:jid_to_lower(escalus_client:username(Client)), LServer = escalus_utils:jid_to_lower(escalus_client:server(Client)), WaitFn = fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, - mongoose_helper:wait_until(WaitFn, N). + wait_until(WaitFn, N). + +wait_for_c2s_state_name(C2SPid, NewStateName) -> + wait_until(fun() -> get_c2s_state_name(C2SPid) end, NewStateName, + #{name => get_c2s_state_name, time_left => timer:seconds(5)}). + +get_c2s_state_name(C2SPid) when is_pid(C2SPid) -> + SysStatus = rpc(mim(), sys, get_status, [C2SPid]), + extract_state_name(SysStatus). + +extract_state_name(SysStatus) -> + {status, _Pid, {module, _}, + [_, _, _, _, [_, {data, FSMData} | _]]} = SysStatus, + proplists:get_value("StateName", FSMData). diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 6a569c392f..c9707cb399 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -601,7 +601,7 @@ resume_session_state_send_message(Config) -> %% kill alice connection C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), assert_alive_resources(Alice, 1), %% send some messages and check if c2s can handle it @@ -651,7 +651,7 @@ resume_session_state_stop_c2s(Config) -> % session should be alive assert_alive_resources(Alice, 1), rpc(mim(), ejabberd_c2s, stop, [C2SPid]), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), %% suspend the process to ensure that Alice has enough time to reconnect, %% before resumption timeout occurs. ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -667,13 +667,13 @@ resume_session_state_stop_c2s(Config) -> escalus_connection:stop(NewAlice). %% This test only verifies the validity of helpers (get_session_pid, -%% assert_no_offline_msgs, assert_c2s_state) written for wait_for_resumption +%% assert_no_offline_msgs, get_c2s_state_name) written for wait_for_resumption %% testcase. session_established(Config) -> Alice = connect_fresh(Config, alice, presence), C2SPid = mongoose_helper:get_session_pid(Alice), assert_no_offline_msgs(Alice), - assert_c2s_state(C2SPid, session_established), + StateName = mongoose_helper:get_c2s_state_name(C2SPid), escalus_connection:stop(Alice). %% Ensure that after a violent disconnection, @@ -685,7 +685,7 @@ wait_for_resumption(Config) -> {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts), %% Ensure the c2s process is waiting for resumption. assert_no_offline_msgs_spec(AliceSpec), - wait_for_c2s_state_change(C2SPid, resume_session). + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session). unacknowledged_message_hook_resume(Config) -> unacknowledged_message_hook_common(fun unacknowledged_message_hook_resume/4, Config). @@ -745,7 +745,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> %% kill alice connection C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), assert_alive_resources(Alice, 1), escalus:assert(is_chat_message, [<<"msg-1">>], wait_for_unacked_msg_hook(0, Resource, 100)), @@ -771,7 +771,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> NewC2SPid = mongoose_helper:get_session_pid(NewAlice), escalus_connection:kill(NewAlice), - wait_for_c2s_state_change(NewC2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(NewC2SPid, resume_session), escalus:assert(is_chat_message, [<<"msg-1">>], wait_for_unacked_msg_hook(1, NewResource, 100)), escalus:assert(is_chat_message, [<<"msg-2">>], wait_for_unacked_msg_hook(1, NewResource, 100)), @@ -848,7 +848,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> %% Ensure the c2s process is waiting for resumption. assert_no_offline_msgs(Alice), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), %% Resume the session. NewAlice = connect_resume(Alice, 1), @@ -1043,7 +1043,7 @@ messages_are_properly_flushed_during_resumption(Config) -> escalus_client:kill_connection(Config, Alice), %% The receiver process would stop now C2SPid = mongoose_helper:get_session_pid(Alice), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), wait_for_queue_length(C2SPid, 0), ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -1082,7 +1082,7 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> SMH = escalus_connection:get_sm_h(Alice), escalus_client:kill_connection(Config, Alice), C2SPid = mongoose_helper:get_session_pid(Alice), - wait_for_c2s_state_change(C2SPid, resume_session), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), ok = rpc(mim(), sys, suspend, [C2SPid]), %% send some dummy event. ignored by c2s but ensures that @@ -1206,30 +1206,14 @@ assert_no_offline_msgs_spec(Spec) -> Server = proplists:get_value(server, Spec), 0 = mongoose_helper:total_offline_messages({Username, Server}). -wait_for_c2s_state_change(C2SPid, NewStateName) -> - mongoose_helper:wait_until(fun() -> get_c2s_state(C2SPid) end, NewStateName, - #{name => get_c2s_state, time_left => timer:seconds(5)}). - wait_for_c2s_unacked_count(C2SPid, Count) -> mongoose_helper:wait_until(fun() -> get_c2s_unacked_count(C2SPid) end, Count, - #{name => get_c2s_state, time_left => timer:seconds(5)}). + #{name => get_c2s_unacked_count, time_left => timer:seconds(5)}). get_c2s_unacked_count(C2SPid) -> Info = rpc(mim(), ejabberd_c2s, get_info, [C2SPid]), maps:get(stream_mgmt_buffer_size, Info). -assert_c2s_state(C2SPid, StateName) -> - StateName = get_c2s_state(C2SPid). - -get_c2s_state(C2SPid) when is_pid(C2SPid) -> - SysStatus = rpc(mim(), sys, get_status, [C2SPid]), - extract_state_name(SysStatus). - -extract_state_name(SysStatus) -> - {status, _Pid, {module, _}, - [_, _, _, _, [_, {data, FSMData} | _]]} = SysStatus, - proplists:get_value("StateName", FSMData). - wait_until_disconnected(Client) -> wait_for_resource_count(Client, 0). From aaee737a1a84b7390c70cd5569b39299460a2f93 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 14 Dec 2021 17:02:38 +0100 Subject: [PATCH 15/26] Use connect_same in kill_and_connect_with_resume_session_without_waiting_for_result --- big_tests/tests/sm_SUITE.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index c9707cb399..198170f353 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -42,8 +42,7 @@ groups() -> {stream_mgmt_disabled, [], stream_mgmt_disabled_cases()}, {manual_ack_freq_long_session_timeout, [parallel], [preserve_order]}, {unacknowledged_message_hook, [parallel], unacknowledged_message_hook()}], -% ct_helper:repeat_all_until_all_ok(G). - G. + ct_helper:repeat_all_until_all_ok(G). parallel_test_cases() -> @@ -261,7 +260,7 @@ client_enables_sm_twice_fails_with_correct_error_stanza(Config) -> escalus_connection:get_stanza(Alice, enable_sm_failed)), escalus:assert(is_stream_end, escalus_connection:get_stanza(Alice, enable_sm_failed)), - true = escalus_connection:wait_for_close(Alice,timer:seconds(5)). + true = escalus_connection:wait_for_close(Alice, timer:seconds(5)). session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza(Config) -> %% GIVEN USER WITH STREAM RESUMPTION ENABLED @@ -1344,8 +1343,7 @@ stop_client_and_wait_for_termination(Alice) -> kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> SMH = escalus_connection:get_sm_h(Alice), - AliceSpec = client_to_spec(Alice), - {ok, NewAlice, _} = escalus_connection:start(AliceSpec, connection_steps_to_authenticate()), + NewAlice = connect_same(Alice, auth), SMID = client_to_smid(Alice), %% kill alice connection escalus_connection:kill(Alice), From eb38790081408d24d357407d91e56ab981dfee4f Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 15:23:34 +0100 Subject: [PATCH 16/26] Use chat_to_short_jid where get_bjid has been used before --- big_tests/tests/sm_SUITE.erl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 198170f353..8cfb9d4334 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -484,23 +484,23 @@ preserve_order(Config) -> Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sr_presence), AliceSpec = client_to_spec(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"1">>)), %% kill alice connection escalus_connection:kill(Alice), wait_until_disconnected(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"2">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"3">>)), NewAlice = connect_spec(AliceSpec, session), escalus_connection:send(NewAlice, escalus_stanza:enable_sm([resume])), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"4">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"5">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"4">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"5">>)), escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"6">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"6">>)), receive_all_ordered(NewAlice, 1, 6), @@ -538,7 +538,7 @@ resend_unacked_after_resume_timeout(Config) -> Alice = connect_fresh(Config, alice, sr_presence), AliceSpec = client_to_spec(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), %% kill alice connection escalus_connection:kill(Alice), @@ -560,7 +560,7 @@ resume_expired_session_returns_correct_h(Config) -> Bob = connect_fresh(Config, bob, sr_presence), Alice = connect_fresh(Config, alice, sr_presence), %% Bob sends a message to Alice, and Alice receives it but doesn't acknowledge - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), escalus:wait_for_stanza(Alice), %% alice comes back, but too late, so resumption doesn't work, %% but she receives the previous h = 1 anyway @@ -596,7 +596,7 @@ resume_session_state_send_message(Config) -> Alice = connect_fresh(Config, alice, sr_presence, manual), ack_initial_presence(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), %% kill alice connection C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), @@ -604,8 +604,8 @@ resume_session_state_send_message(Config) -> assert_alive_resources(Alice, 1), %% send some messages and check if c2s can handle it - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-3">>)), %% suspend the process to ensure that Alice has enough time to reconnect, %% before resumption timeout occurs. ok = rpc(mim(), sys, suspend, [C2SPid]), @@ -636,7 +636,7 @@ resume_session_state_stop_c2s(Config) -> get_ack(Alice), ack_initial_presence(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), %% get pid of c2s @@ -739,8 +739,8 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> SMID = client_to_smid(Alice), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-1">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-2">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-2">>)), %% kill alice connection C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), @@ -752,8 +752,8 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> ?assertEqual(timeout, wait_for_unacked_msg_hook(0, Resource, 100)), %% send some messages and check if c2s can handle it - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-3">>)), - escalus_connection:send(Bob, escalus_stanza:chat_to(Alice, <<"msg-4">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-3">>)), + escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-4">>)), escalus:assert(is_chat_message, [<<"msg-3">>], wait_for_unacked_msg_hook(0, Resource, 100)), escalus:assert(is_chat_message, [<<"msg-4">>], wait_for_unacked_msg_hook(0, Resource, 100)), ?assertEqual(timeout, wait_for_unacked_msg_hook(0, Resource, 100)), From edecf71e453edbba893613379bbc72e8fc663569 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 15:26:15 +0100 Subject: [PATCH 17/26] Exterminate fully-qualified calls in mongoose_helper --- big_tests/tests/mongoose_helper.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 1c674e00de..28a879376f 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -64,7 +64,7 @@ is_rdbms_enabled(HostType) -> -spec mnesia_or_rdbms_backend() -> atom(). mnesia_or_rdbms_backend() -> Host = ct:get_config({hosts, mim, domain}), - case mongoose_helper:is_rdbms_enabled(Host) of + case is_rdbms_enabled(Host) of true -> rdbms; false -> mnesia end. @@ -462,7 +462,7 @@ get_session_info(RpcDetails, User) -> Info. wait_for_route_message_count(C2sPid, ExpectedCount) when is_pid(C2sPid), is_integer(ExpectedCount) -> - mongoose_helper:wait_until(fun() -> count_route_messages(C2sPid) end, ExpectedCount, #{name => has_route_message}). + wait_until(fun() -> count_route_messages(C2sPid) end, ExpectedCount, #{name => has_route_message}). count_route_messages(C2sPid) when is_pid(C2sPid) -> {messages, Messages} = rpc:pinfo(C2sPid, messages), @@ -572,7 +572,7 @@ do_restore_config_option(Option, {error, not_found}) -> wait_for_n_offline_messages(Client, N) -> LUser = escalus_utils:jid_to_lower(escalus_client:username(Client)), LServer = escalus_utils:jid_to_lower(escalus_client:server(Client)), - WaitFn = fun() -> mongoose_helper:total_offline_messages({LUser, LServer}) end, + WaitFn = fun() -> total_offline_messages({LUser, LServer}) end, wait_until(WaitFn, N). wait_for_c2s_state_name(C2SPid, NewStateName) -> From ced1ded5a1a0938bee19f024b62844402e22c2d2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 15:35:01 +0100 Subject: [PATCH 18/26] Remove default timer:seconds from wait_for_c2s_unacked_count Code style --- big_tests/tests/mongoose_helper.erl | 2 +- big_tests/tests/sm_SUITE.erl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 28a879376f..c46280d063 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -577,7 +577,7 @@ wait_for_n_offline_messages(Client, N) -> wait_for_c2s_state_name(C2SPid, NewStateName) -> wait_until(fun() -> get_c2s_state_name(C2SPid) end, NewStateName, - #{name => get_c2s_state_name, time_left => timer:seconds(5)}). + #{name => get_c2s_state_name}). get_c2s_state_name(C2SPid) when is_pid(C2SPid) -> SysStatus = rpc(mim(), sys, get_status, [C2SPid]), diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 8cfb9d4334..7b674e2837 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -274,7 +274,7 @@ session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza( escalus_connection:get_stanza(Alice, close_old_stream)), escalus:assert(is_stream_end, escalus_connection:get_stanza(Alice, close_old_stream)), - true = escalus_connection:wait_for_close(Alice,timer:seconds(5)), + true = escalus_connection:wait_for_close(Alice, timer:seconds(5)), true = escalus_connection:is_connected(Alice2), escalus_connection:stop(Alice2). @@ -364,7 +364,7 @@ h_non_given_closes_stream_gracefully(ConfigIn) -> escalus:wait_for_stanza(Alice)), mongoose_helper:wait_for_pid_to_die(C2SPid), escalus:assert(is_stream_end, escalus_connection:get_stanza(Alice, stream_end)), - true = escalus_connection:wait_for_close(Alice,timer:seconds(5)) + true = escalus_connection:wait_for_close(Alice, timer:seconds(5)) end). client_acks_more_than_sent(Config) -> @@ -1207,7 +1207,7 @@ assert_no_offline_msgs_spec(Spec) -> wait_for_c2s_unacked_count(C2SPid, Count) -> mongoose_helper:wait_until(fun() -> get_c2s_unacked_count(C2SPid) end, Count, - #{name => get_c2s_unacked_count, time_left => timer:seconds(5)}). + #{name => get_c2s_unacked_count}). get_c2s_unacked_count(C2SPid) -> Info = rpc(mim(), ejabberd_c2s, get_info, [C2SPid]), From 94d43ae942923f692b64328eaa45de3dc8a3e5c2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 16:56:02 +0100 Subject: [PATCH 19/26] Move functions into sm_helper --- big_tests/tests/sm_SUITE.erl | 401 ++++++++++------------------------ big_tests/tests/sm_helper.erl | 299 +++++++++++++++++++++++++ 2 files changed, 416 insertions(+), 284 deletions(-) create mode 100644 big_tests/tests/sm_helper.erl diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index 7b674e2837..ac7a0c2916 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -1,5 +1,81 @@ +%% Session Management tests -module(sm_SUITE). --compile([export_all, nowarn_export_all]). + +-export([suite/0, + all/0, + groups/0, + init_per_suite/1, + end_per_suite/1, + init_per_group/2, + end_per_group/2, + init_per_testcase/2, + end_per_testcase/2]). + +%% parallel group +-export([server_announces_sm/1, + server_enables_sm_before_session/1, + server_enables_sm_after_session/1, + server_returns_failed_after_start/1, + server_returns_failed_after_auth/1, + server_enables_resumption/1, + client_enables_sm_twice_fails_with_correct_error_stanza/1, + session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza/1, + session_resumed_and_old_session_dead_doesnt_route_error_to_new_session/1, + basic_ack/1, + h_ok_before_session/1, + h_ok_after_session_enabled_before_session/1, + h_ok_after_session_enabled_after_session/1, + h_ok_after_a_chat/1, + h_non_given_closes_stream_gracefully/1, + resend_unacked_on_reconnection/1, + session_established/1, + wait_for_resumption/1, + resume_session/1, + resume_session_with_wrong_h_does_not_leak_sessions/1, + resume_session_with_wrong_sid_returns_item_not_found/1, + resume_session_with_wrong_namespace_is_a_noop/1, + resume_dead_session_results_in_item_not_found/1, + resume_session_kills_old_C2S_gracefully/1, + aggressively_pipelined_resume/1, + replies_are_processed_by_resumed_session/1, + subscription_requests_are_buffered_properly/1, + messages_are_properly_flushed_during_resumption/1, + messages_are_properly_flushed_during_resumption_p1_fsm_old/1]). + +%% manual_ack_freq_2 group +-export([server_requests_ack_freq_2/1]). + +-export([client_acks_more_than_sent/1, + too_many_unacked_stanzas/1, + resend_unacked_after_resume_timeout/1, + resume_session_state_send_message/1, + resume_session_state_stop_c2s/1, + server_requests_ack_after_session/1, + resend_more_offline_messages_than_buffer_size/1, + server_requests_ack/1]). + +%% stale_h group +-export([resume_expired_session_returns_correct_h/1, + gc_repeat_after_never_means_no_cleaning/1, + gc_repeat_after_timeout_does_clean/1]). + +%% stream_mgmt_disabled group +-export([no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt/1, + no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption/1]). + +%% manual_ack_freq_long_session_timeout group +-export([preserve_order/1]). + +%% unacknowledged_message_hook group +-export([unacknowledged_message_hook_bounce/1, + unacknowledged_message_hook_offline/1, + unacknowledged_message_hook_resume/1]). + +%% Injected code callbacks +-export([rpc_start_hook_handler/3, + rpc_stop_hook_handler/2, + hook_handler_fn/3, + regression_handler/5]). -include_lib("exml/include/exml.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -17,6 +93,37 @@ -import(domain_helper, [host_type/0]). +-import(sm_helper, [connect_fresh/3, + connect_fresh/4, + connect_spec/2, + connect_spec/3, + connect_same/2, + connect_same/3, + connect_resume/2, + client_to_spec0/1, + client_to_spec/1, + client_to_smid/1, + wait_until_disconnected/1, + try_to_resume_stream/3, + kill_and_connect_with_resume_session_without_waiting_for_result/1, + stop_client_and_wait_for_termination/1, + assert_alive_resources/2, + get_user_present_resources/1, + get_sid_by_stream_id/1, + wait_for_c2s_unacked_count/2, + wait_for_resource_count/2, + wait_for_process_termination/1, + process_initial_stanza/1, + send_initial_presence/1, + kill_and_connect_resume/1, + monitor_session/1, + wait_for_process_termination/1, + wait_for_queue_length/2, + send_messages/3, + wait_for_messages/2, + get_ack/1, + ack_initial_presence/1]). + -define(LONG_TIMEOUT, 3600). -define(SHORT_TIMEOUT, 3). -define(SMALL_SM_BUFFER, 3). @@ -128,7 +235,8 @@ init_per_group(Group, Config) when Group =:= unacknowledged_message_hook; Group =:= manual_ack_freq_long_session_timeout; Group =:= parallel_manual_ack_freq_1; Group =:= manual_ack_freq_2 -> - dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)); + dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)), + Config; init_per_group(stale_h, Config) -> escalus_users:update_userspec(Config, alice, manual_ack, true); init_per_group(stream_mgmt_disabled, Config) -> @@ -206,6 +314,9 @@ stale_h(RepeatAfter, Geriatric) -> {stale_h_repeat_after, RepeatAfter}, {stale_h_geriatric, Geriatric}]}]. +make_smid() -> + base64:encode(crypto:strong_rand_bytes(21)). + register_smid(IntSmidId) -> S = {SMID = make_smid(), IntSmidId}, ok = rpc(mim(), ?MOD_SM, register_stale_smid_h, [host_type(), SMID, IntSmidId]), @@ -666,13 +777,12 @@ resume_session_state_stop_c2s(Config) -> escalus_connection:stop(NewAlice). %% This test only verifies the validity of helpers (get_session_pid, -%% assert_no_offline_msgs, get_c2s_state_name) written for wait_for_resumption +%% get_c2s_state_name) written for wait_for_resumption %% testcase. session_established(Config) -> Alice = connect_fresh(Config, alice, presence), C2SPid = mongoose_helper:get_session_pid(Alice), - assert_no_offline_msgs(Alice), - StateName = mongoose_helper:get_c2s_state_name(C2SPid), + session_established = mongoose_helper:get_c2s_state_name(C2SPid), escalus_connection:stop(Alice). %% Ensure that after a violent disconnection, @@ -682,8 +792,6 @@ wait_for_resumption(Config) -> Bob = connect_fresh(Config, bob, session), Texts = three_texts(), {C2SPid, _} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts), - %% Ensure the c2s process is waiting for resumption. - assert_no_offline_msgs_spec(AliceSpec), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session). unacknowledged_message_hook_resume(Config) -> @@ -846,7 +954,6 @@ resume_session_kills_old_C2S_gracefully(Config) -> escalus_client:kill_connection(Config, Alice), %% Ensure the c2s process is waiting for resumption. - assert_no_offline_msgs(Alice), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), %% Resume the session. @@ -863,54 +970,6 @@ resume_session_kills_old_C2S_gracefully(Config) -> end, escalus_connection:stop(NewAlice). -%% Connection steps -connection_steps_to_authenticate() -> - [start_stream, - stream_features, - maybe_use_ssl, - authenticate]. - -connection_steps_to_bind() -> - connection_steps_to_authenticate() ++ [bind]. - -connection_steps_to_session() -> - connection_steps_to_bind() ++ [session]. - -connection_steps_to_enable_stream_mgmt(after_session) -> - connection_steps_to_session() ++ [stream_management]; -connection_steps_to_enable_stream_mgmt(after_bind) -> - connection_steps_to_bind() ++ [stream_management]. - -connection_steps_to_enable_stream_resumption() -> - connection_steps_to_session() ++ [stream_resumption]. - -connection_steps_to_enable_stream_resumption_and_presence() -> - connection_steps_to_enable_stream_resumption() ++ [fun initial_presence_step/2]. - -connection_steps_to_presence() -> - connection_steps_to_session() ++ [fun initial_presence_step/2]. - -connection_steps_to_enable_stream_mgmt_and_presence() -> - connection_steps_to_enable_stream_mgmt(after_session) ++ [fun initial_presence_step/2]. - -connection_steps_to_stream_resumption(SMID, H) -> - connection_steps_to_authenticate() ++ [mk_resume_stream(SMID, H)]. - -mk_resume_stream(SMID, PrevH) -> - fun (Conn = #client{props = Props}, Features) -> - Resumed = try_to_resume_stream(Conn, SMID, PrevH), - true = escalus_pred:is_sm_resumed(SMID, Resumed), - {Conn#client{props = [{smid, SMID} | Props]}, Features} - end. - -initial_presence_step(Conn, Features) -> - process_initial_stanza(Conn), - {Conn, Features}. - -try_to_resume_stream(Conn, SMID, PrevH) -> - escalus_connection:send(Conn, escalus_stanza:resume(SMID, PrevH)), - escalus_connection:get_stanza(Conn, get_resumed). - buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> Alice = connect_spec(AliceSpec, sr_presence, manual), %% Bobs sends some messages to Alice. @@ -1170,124 +1229,14 @@ wait_for_unacked_msg_hook(Counter, Res, Timeout) -> timeout end. -send_initial_presence(User) -> - InitialPresence = escalus_stanza:presence(<<"available">>), - escalus_connection:send(User, InitialPresence). - -process_initial_stanza(User) -> - send_initial_presence(User), - escalus:assert(is_presence, escalus:wait_for_stanza(User)). - -discard_offline_messages(Config, UserName) -> - discard_offline_messages(Config, UserName, 1). - -discard_offline_messages(Config, UserName, H) when is_atom(UserName) -> - Spec = escalus_users:get_options(Config, UserName), - {ok, User, _} = escalus_connection:start(Spec), - escalus_connection:send(User, escalus_stanza:presence(<<"available">>)), - discard_offline_messages(Config, User, H); -discard_offline_messages(Config, User, H) -> - Stanza = escalus_connection:get_stanza(User, maybe_offline_msg), - escalus_connection:send(User, escalus_stanza:sm_ack(H)), - case escalus_pred:is_presence(Stanza) of - true -> - ok; - false -> - discard_offline_messages(Config, User, H+1) - end. - -assert_no_offline_msgs(Client) -> - Spec = client_to_spec(Client), - assert_no_offline_msgs_spec(Spec). - -assert_no_offline_msgs_spec(Spec) -> - Username = escalus_utils:jid_to_lower(proplists:get_value(username, Spec)), - Server = proplists:get_value(server, Spec), - 0 = mongoose_helper:total_offline_messages({Username, Server}). - -wait_for_c2s_unacked_count(C2SPid, Count) -> - mongoose_helper:wait_until(fun() -> get_c2s_unacked_count(C2SPid) end, Count, - #{name => get_c2s_unacked_count}). - -get_c2s_unacked_count(C2SPid) -> - Info = rpc(mim(), ejabberd_c2s, get_info, [C2SPid]), - maps:get(stream_mgmt_buffer_size, Info). - -wait_until_disconnected(Client) -> - wait_for_resource_count(Client, 0). - -wait_for_resource_count(Client, N) -> - mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(Client)) end, - N, #{name => get_user_alive_resources}). - -monitor_session(Client) -> - C2SPid = mongoose_helper:get_session_pid(Client), - erlang:monitor(process, C2SPid). - --spec wait_for_process_termination(MRef :: reference()) -> ok. -wait_for_process_termination(MRef) -> - receive - {'DOWN', MRef, _Type, _C2SPid, _Info} -> - ok - after 5000 -> - ct:fail(wait_for_process_termination_timeout) - end, - ok. - -assert_alive_resources(Alice, N) -> - N = length(get_user_alive_resources(Alice)). - -get_user_alive_resources(Client) -> - UserSpec = client_to_spec(Client), - {U, S} = get_us_from_spec(UserSpec), - JID = mongoose_helper:make_jid(U, S, <<>>), - rpc(mim(), ejabberd_sm, get_user_resources, [JID]). - -get_user_present_resources(Client) -> - UserSpec = client_to_spec(Client), - {U, S} = get_us_from_spec(UserSpec), - JID = mongoose_helper:make_jid(U, S, <<>>), - rpc(mim(), ejabberd_sm, get_user_present_resources, [JID]). - -get_sid_by_stream_id(SMID) -> - rpc(mim(), ?MOD_SM, get_sid, [SMID]). - -get_us_from_spec(UserSpec) -> - U = proplists:get_value(username, UserSpec), - S = proplists:get_value(server, UserSpec), - {U, S}. - -clear_session_table() -> - Node = ct:get_config({hosts, mim, node}), - SessionBackend = rpc(mim(), ejabberd_sm, sm_backend, []), - rpc(mim(), SessionBackend, cleanup, [Node]). - -clear_sm_session_table() -> - rpc(mim(), mnesia, clear_table, [sm_session]). - is_chat(Content) -> fun(Stanza) -> escalus_pred:is_chat_message(Content, Stanza) end. is_presence(Type) -> fun(Stanza) -> escalus_pred:is_presence_with_type(Type, Stanza) end. -given_fresh_user_with_spec(Spec) -> - {ok, User, _} = escalus_connection:start(Spec), - process_initial_stanza(User), - User. - -wait_for_queue_length(Pid, Length) -> - F = fun() -> - case rpc(mim(), erlang, process_info, [Pid, messages]) of - {messages, List} when length(List) =:= Length -> - ok; - {messages, List} -> - {messages, length(List), List}; - Other -> - Other - end - end, - mongoose_helper:wait_until(F, ok, #{name => {wait_for_queue_length, Length}}). +three_texts() -> + [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>]. %%-------------------------------------------------------------------- %% IQ handler necessary for reproducing "replies_are_processed_by_resumed_session" @@ -1332,119 +1281,3 @@ wait_for_session(JID, Retries, SleepTime) -> _ -> ok end. - -make_smid() -> - base64:encode(crypto:strong_rand_bytes(21)). - -stop_client_and_wait_for_termination(Alice) -> - C2SRef = monitor_session(Alice), - escalus_connection:stop(Alice), - ok = wait_for_process_termination(C2SRef). - -kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> - SMH = escalus_connection:get_sm_h(Alice), - NewAlice = connect_same(Alice, auth), - SMID = client_to_smid(Alice), - %% kill alice connection - escalus_connection:kill(Alice), - %% ensure there is no session - wait_until_disconnected(Alice), - escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), - NewAlice. - -client_to_smid(#client{props = Props}) -> - proplists:get_value(smid, Props). - -client_to_spec(#client{props = Props}) -> - Props. - -%% Returns the original spec, without the dynamically added fields -client_to_spec0(#client{props = Props}) -> - lists:foldl(fun proplists:delete/2, Props, [stream_id, resource]). - -get_ack(Client) -> - escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). - -ack_initial_presence(Client) -> - escalus_connection:send(Client, escalus_stanza:sm_ack(1)). - -connect_fresh(Config, Name, FinalStep) -> - connect_fresh(Config, Name, FinalStep, auto). - -connect_fresh(Config, Name, FinalStep, Ack) -> - Spec = escalus_fresh:create_fresh_user(Config, Name), - connect_spec(Spec, FinalStep, Ack). - -connect_spec(Spec, FinalStep) -> - connect_spec(Spec, FinalStep, auto). - -connect_spec(Spec, FinalStep, Ack) -> - Steps = final_step_to_steps(FinalStep), - ExtraOpts = ack_to_extra_opts(Ack), - {ok, Client, _} = escalus_connection:start(ExtraOpts ++ Spec, Steps), - Client. - -connect_same(Client, FinalStep) -> - connect_same(Client, FinalStep, auto). - -connect_same(Client, FinalStep, Ack) -> - connect_spec(client_to_spec0(Client), FinalStep, Ack). - -ack_to_extra_opts(auto) -> []; -ack_to_extra_opts(manual) -> [{manual_ack, true}]. - -final_step_to_steps(auth) -> - connection_steps_to_authenticate(); -final_step_to_steps(session) -> - connection_steps_to_session(); -final_step_to_steps(presence) -> - connection_steps_to_presence(); -final_step_to_steps(sr_session) -> - connection_steps_to_enable_stream_resumption(); -final_step_to_steps(sr_presence) -> - connection_steps_to_enable_stream_resumption_and_presence(); -final_step_to_steps(sm_presence) -> - connection_steps_to_enable_stream_mgmt_and_presence(); -final_step_to_steps(sm_after_session) -> - connection_steps_to_enable_stream_mgmt(after_session); -final_step_to_steps(sm_after_bind) -> - connection_steps_to_enable_stream_mgmt(after_bind); -final_step_to_steps(sm_before_session) -> - connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; -final_step_to_steps({resume, SMID, H}) -> - connection_steps_to_stream_resumption(SMID, H). - -kill_and_connect_resume(Client) -> - %% Get SMH before killing the old connection - SMH = escalus_connection:get_sm_h(Client), - escalus_connection:kill(Client), - connect_resume(Client, SMH). - -connect_resume(Client, SMH) -> - SMID = client_to_smid(Client), - Spec = client_to_spec(Client), - C2SPid = mongoose_helper:get_session_pid(Client), - Steps = connection_steps_to_stream_resumption(SMID, SMH), - try - {ok, Client2, _} = escalus_connection:start(Spec, Steps), - Client2 - catch Class:Error:Stacktrace -> - ct:pal("C2S info ~p", [rpc:pinfo(C2SPid, [messages, current_stacktrace])]), - erlang:raise(Class, Error, Stacktrace) - end. - -send_messages(Bob, Alice, Texts) -> - [escalus:send(Bob, escalus_stanza:chat_to(Alice, Text)) - || Text <- Texts]. - -wait_for_messages(Alice, Texts) -> - Stanzas = escalus:wait_for_stanzas(Alice, length(Texts)), - assert_same_length(Stanzas, Texts), - [escalus:assert(is_chat_message, [Text], Stanza) - || {Text, Stanza} <- lists:zip(Texts, Stanzas)], - ok. - -assert_same_length(List1, List2) when length(List1) =:= length(List2) -> ok. - -three_texts() -> - [<<"msg-1">>, <<"msg-2">>, <<"msg-3">>]. diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl new file mode 100644 index 0000000000..2c90a2d110 --- /dev/null +++ b/big_tests/tests/sm_helper.erl @@ -0,0 +1,299 @@ +%% Session Management helpers +-module(sm_helper). + +-export([client_to_spec0/1, + client_to_spec/1, + client_to_smid/1, + get_sid_by_stream_id/1]). + +%% Connection helpers +-export([connect_fresh/3, + connect_fresh/4, + connect_spec/2, + connect_spec/3, + connect_same/2, + connect_same/3, + connect_resume/2]). + +%% Waiting and introspection helpers +-export([wait_until_disconnected/1, + try_to_resume_stream/3, + kill_and_connect_with_resume_session_without_waiting_for_result/1, + stop_client_and_wait_for_termination/1, + assert_alive_resources/2, + get_user_present_resources/1, + wait_for_c2s_unacked_count/2, + wait_for_resource_count/2, + wait_for_process_termination/1, + process_initial_stanza/1, + kill_and_connect_resume/1, + monitor_session/1, + wait_for_process_termination/1, + wait_for_queue_length/2]). + +%% Stanza helpers +-export([send_initial_presence/1, + send_messages/3, + wait_for_messages/2, + get_ack/1, + ack_initial_presence/1]). + +-include_lib("escalus/include/escalus.hrl"). + +-import(distributed_helper, [mim/0, + rpc/4]). + +-define(MOD_SM, mod_stream_management). + +client_to_smid(#client{props = Props}) -> + proplists:get_value(smid, Props). + +client_to_spec(#client{props = Props}) -> + Props. + +%% Returns the original spec, without the dynamically added fields +client_to_spec0(#client{props = Props}) -> + lists:foldl(fun proplists:delete/2, Props, [stream_id, resource]). + +get_sid_by_stream_id(SMID) -> + rpc(mim(), ?MOD_SM, get_sid, [SMID]). + + +%% Connection helpers + +final_step_to_steps(auth) -> + connection_steps_to_authenticate(); +final_step_to_steps(session) -> + connection_steps_to_session(); +final_step_to_steps(presence) -> + connection_steps_to_presence(); +final_step_to_steps(sr_session) -> + connection_steps_to_enable_stream_resumption(); +final_step_to_steps(sr_presence) -> + connection_steps_to_enable_stream_resumption_and_presence(); +final_step_to_steps(sm_presence) -> + connection_steps_to_enable_stream_mgmt_and_presence(); +final_step_to_steps(sm_after_session) -> + connection_steps_to_enable_stream_mgmt(after_session); +final_step_to_steps(sm_after_bind) -> + connection_steps_to_enable_stream_mgmt(after_bind); +final_step_to_steps(sm_before_session) -> + connection_steps_to_enable_stream_mgmt(after_bind) ++ [session]; +final_step_to_steps({resume, SMID, H}) -> + connection_steps_to_stream_resumption(SMID, H). + +%% Connection steps +connection_steps_to_authenticate() -> + [start_stream, + stream_features, + maybe_use_ssl, + authenticate]. + +connection_steps_to_bind() -> + connection_steps_to_authenticate() ++ [bind]. + +connection_steps_to_session() -> + connection_steps_to_bind() ++ [session]. + +connection_steps_to_enable_stream_mgmt(after_session) -> + connection_steps_to_session() ++ [stream_management]; +connection_steps_to_enable_stream_mgmt(after_bind) -> + connection_steps_to_bind() ++ [stream_management]. + +connection_steps_to_enable_stream_resumption() -> + connection_steps_to_session() ++ [stream_resumption]. + +connection_steps_to_enable_stream_resumption_and_presence() -> + connection_steps_to_enable_stream_resumption() ++ [fun initial_presence_step/2]. + +connection_steps_to_presence() -> + connection_steps_to_session() ++ [fun initial_presence_step/2]. + +connection_steps_to_enable_stream_mgmt_and_presence() -> + connection_steps_to_enable_stream_mgmt(after_session) ++ [fun initial_presence_step/2]. + +connection_steps_to_stream_resumption(SMID, H) -> + connection_steps_to_authenticate() ++ [mk_resume_stream(SMID, H)]. + +mk_resume_stream(SMID, PrevH) -> + fun (Conn = #client{props = Props}, Features) -> + Resumed = try_to_resume_stream(Conn, SMID, PrevH), + true = escalus_pred:is_sm_resumed(SMID, Resumed), + {Conn#client{props = [{smid, SMID} | Props]}, Features} + end. + +try_to_resume_stream(Conn, SMID, PrevH) -> + escalus_connection:send(Conn, escalus_stanza:resume(SMID, PrevH)), + escalus_connection:get_stanza(Conn, get_resumed). + +initial_presence_step(Conn, Features) -> + process_initial_stanza(Conn), + {Conn, Features}. + +%% Making connections with different set of steps is a common procedure in sm_SUITE +%% This group of functions helps to keep code short and clean +connect_fresh(Config, Name, FinalStep) -> + connect_fresh(Config, Name, FinalStep, auto). + +connect_fresh(Config, Name, FinalStep, Ack) -> + Spec = escalus_fresh:create_fresh_user(Config, Name), + connect_spec(Spec, FinalStep, Ack). + +connect_spec(Spec, FinalStep) -> + connect_spec(Spec, FinalStep, auto). + +connect_spec(Spec, FinalStep, Ack) -> + Steps = final_step_to_steps(FinalStep), + ExtraOpts = ack_to_extra_opts(Ack), + {ok, Client, _} = escalus_connection:start(ExtraOpts ++ Spec, Steps), + Client. + +connect_same(Client, FinalStep) -> + connect_same(Client, FinalStep, auto). + +connect_same(Client, FinalStep, Ack) -> + connect_spec(client_to_spec0(Client), FinalStep, Ack). + +ack_to_extra_opts(auto) -> []; +ack_to_extra_opts(manual) -> [{manual_ack, true}]. + +kill_and_connect_resume(Client) -> + %% Get SMH before killing the old connection + SMH = escalus_connection:get_sm_h(Client), + escalus_connection:kill(Client), + connect_resume(Client, SMH). + +connect_resume(Client, SMH) -> + SMID = client_to_smid(Client), + Spec = client_to_spec(Client), + C2SPid = mongoose_helper:get_session_pid(Client), + Steps = connection_steps_to_stream_resumption(SMID, SMH), + try + {ok, Client2, _} = escalus_connection:start(Spec, Steps), + Client2 + catch Class:Error:Stacktrace -> + ct:pal("C2S info ~p", [rpc:pinfo(C2SPid, [messages, current_stacktrace])]), + erlang:raise(Class, Error, Stacktrace) + end. + +kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> + SMH = escalus_connection:get_sm_h(Alice), + NewAlice = connect_same(Alice, auth), + SMID = client_to_smid(Alice), + %% kill alice connection + escalus_connection:kill(Alice), + %% ensure there is no session + wait_until_disconnected(Alice), + escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), + NewAlice. + +stop_client_and_wait_for_termination(Alice) -> + C2SRef = monitor_session(Alice), + escalus_connection:stop(Alice), + ok = wait_for_process_termination(C2SRef). + + +%% Waiting and introspection helpers + +monitor_session(Client) -> + C2SPid = mongoose_helper:get_session_pid(Client), + erlang:monitor(process, C2SPid). + +-spec wait_for_process_termination(MRef :: reference()) -> ok. +wait_for_process_termination(MRef) -> + receive + {'DOWN', MRef, _Type, _C2SPid, _Info} -> + ok + after 5000 -> + ct:fail(wait_for_process_termination_timeout) + end, + ok. + +wait_for_queue_length(Pid, Length) -> + F = fun() -> + case rpc(mim(), erlang, process_info, [Pid, messages]) of + {messages, List} when length(List) =:= Length -> + ok; + {messages, List} -> + {messages, length(List), List}; + Other -> + Other + end + end, + mongoose_helper:wait_until(F, ok, #{name => {wait_for_queue_length, Length}}). + +wait_for_c2s_unacked_count(C2SPid, Count) -> + mongoose_helper:wait_until(fun() -> get_c2s_unacked_count(C2SPid) end, Count, + #{name => get_c2s_unacked_count}). + +get_c2s_unacked_count(C2SPid) -> + Info = rpc(mim(), ejabberd_c2s, get_info, [C2SPid]), + maps:get(stream_mgmt_buffer_size, Info). + +wait_until_disconnected(Client) -> + wait_for_resource_count(Client, 0). + +wait_for_resource_count(Client, N) -> + mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(Client)) end, + N, #{name => get_user_alive_resources}). + +assert_alive_resources(Alice, N) -> + N = length(get_user_alive_resources(Alice)). + +get_user_alive_resources(Client) -> + UserSpec = client_to_spec(Client), + {U, S} = get_us_from_spec(UserSpec), + JID = mongoose_helper:make_jid(U, S, <<>>), + rpc(mim(), ejabberd_sm, get_user_resources, [JID]). + +get_user_present_resources(Client) -> + UserSpec = client_to_spec(Client), + {U, S} = get_us_from_spec(UserSpec), + JID = mongoose_helper:make_jid(U, S, <<>>), + rpc(mim(), ejabberd_sm, get_user_present_resources, [JID]). + +get_us_from_spec(UserSpec) -> + U = proplists:get_value(username, UserSpec), + S = proplists:get_value(server, UserSpec), + {U, S}. + + +%% Cleaning helpers + +clear_session_table() -> + Node = ct:get_config({hosts, mim, node}), + SessionBackend = rpc(mim(), ejabberd_sm, sm_backend, []), + rpc(mim(), SessionBackend, cleanup, [Node]). + +clear_sm_session_table() -> + rpc(mim(), mnesia, clear_table, [sm_session]). + + +%% Stanza helpers + +send_initial_presence(User) -> + InitialPresence = escalus_stanza:presence(<<"available">>), + escalus_connection:send(User, InitialPresence). + +process_initial_stanza(User) -> + send_initial_presence(User), + escalus:assert(is_presence, escalus:wait_for_stanza(User)). + +send_messages(Bob, Alice, Texts) -> + [escalus:send(Bob, escalus_stanza:chat_to(Alice, Text)) + || Text <- Texts]. + +wait_for_messages(Alice, Texts) -> + Stanzas = escalus:wait_for_stanzas(Alice, length(Texts)), + assert_same_length(Stanzas, Texts), + [escalus:assert(is_chat_message, [Text], Stanza) + || {Text, Stanza} <- lists:zip(Texts, Stanzas)], + ok. + +assert_same_length(List1, List2) when length(List1) =:= length(List2) -> ok. + +get_ack(Client) -> + escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). + +ack_initial_presence(Client) -> + escalus_connection:send(Client, escalus_stanza:sm_ack(1)). From 7f9159c7713e2d9d3d4004afbdb2650cf27566a4 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 17:00:29 +0100 Subject: [PATCH 20/26] Use connect_fresh with server_returns_failed --- big_tests/tests/sm_SUITE.erl | 14 +++++--------- big_tests/tests/sm_helper.erl | 9 ++++++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index ac7a0c2916..eede52dd7f 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -344,22 +344,18 @@ server_enables_sm_after_session(Config) -> connect_fresh(Config, alice, sm_after_session). server_returns_failed_after_start(Config) -> - server_returns_failed(Config, []). + Alice = connect_fresh(Config, alice, before_auth), + server_returns_failed(Alice). server_returns_failed_after_auth(Config) -> - server_returns_failed(Config, [authenticate]). + Alice = connect_fresh(Config, alice, auth), + server_returns_failed(Alice). server_enables_resumption(Config) -> Alice = connect_fresh(Config, alice, sr_presence), escalus_connection:stop(Alice). -server_returns_failed(Config, ConnActions) -> - AliceSpec = escalus_fresh:create_fresh_user(Config, alice), - {ok, Alice, _} = escalus_connection:start(AliceSpec, - [start_stream, - stream_features, - maybe_use_ssl] - ++ ConnActions), +server_returns_failed(Alice) -> escalus_connection:send(Alice, escalus_stanza:enable_sm()), escalus:assert(is_sm_failed, [<<"unexpected-request">>], escalus_connection:get_stanza(Alice, enable_sm_failed)). diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl index 2c90a2d110..6af576a610 100644 --- a/big_tests/tests/sm_helper.erl +++ b/big_tests/tests/sm_helper.erl @@ -61,6 +61,8 @@ get_sid_by_stream_id(SMID) -> %% Connection helpers +final_step_to_steps(before_auth) -> + connection_steps_before_auth(); final_step_to_steps(auth) -> connection_steps_to_authenticate(); final_step_to_steps(session) -> @@ -83,11 +85,12 @@ final_step_to_steps({resume, SMID, H}) -> connection_steps_to_stream_resumption(SMID, H). %% Connection steps -connection_steps_to_authenticate() -> +connection_steps_before_auth() -> [start_stream, stream_features, - maybe_use_ssl, - authenticate]. + maybe_use_ssl]. +connection_steps_to_authenticate() -> + connection_steps_before_auth() ++ [authenticate]. connection_steps_to_bind() -> connection_steps_to_authenticate() ++ [bind]. From 82b7113c00559f18650f700d4c08643747ff15a3 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 17:03:19 +0100 Subject: [PATCH 21/26] Remove clear_session_table --- big_tests/tests/sm_helper.erl | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl index 6af576a610..c907261f40 100644 --- a/big_tests/tests/sm_helper.erl +++ b/big_tests/tests/sm_helper.erl @@ -22,14 +22,13 @@ stop_client_and_wait_for_termination/1, assert_alive_resources/2, get_user_present_resources/1, - wait_for_c2s_unacked_count/2, + wait_for_queue_length/2, wait_for_resource_count/2, + wait_for_c2s_unacked_count/2, wait_for_process_termination/1, process_initial_stanza/1, kill_and_connect_resume/1, - monitor_session/1, - wait_for_process_termination/1, - wait_for_queue_length/2]). + monitor_session/1]). %% Stanza helpers -export([send_initial_presence/1, @@ -261,17 +260,6 @@ get_us_from_spec(UserSpec) -> {U, S}. -%% Cleaning helpers - -clear_session_table() -> - Node = ct:get_config({hosts, mim, node}), - SessionBackend = rpc(mim(), ejabberd_sm, sm_backend, []), - rpc(mim(), SessionBackend, cleanup, [Node]). - -clear_sm_session_table() -> - rpc(mim(), mnesia, clear_table, [sm_session]). - - %% Stanza helpers send_initial_presence(User) -> From e00a9ef0fe4255be6bd2811ce8b11678664c2cbf Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 17:21:25 +0100 Subject: [PATCH 22/26] Get rid of last manual_ack confusion --- big_tests/tests/sm_SUITE.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index eede52dd7f..a49220603a 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -238,7 +238,7 @@ init_per_group(Group, Config) when Group =:= unacknowledged_message_hook; dynamic_modules:ensure_modules(host_type(), required_modules(group, Group)), Config; init_per_group(stale_h, Config) -> - escalus_users:update_userspec(Config, alice, manual_ack, true); + Config; init_per_group(stream_mgmt_disabled, Config) -> dynamic_modules:stop(host_type(), ?MOD_SM), rpc(mim(), mnesia, delete_table, [sm_session]), @@ -665,12 +665,13 @@ resend_unacked_after_resume_timeout(Config) -> resume_expired_session_returns_correct_h(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, sr_presence), - Alice = connect_fresh(Config, alice, sr_presence), + Alice = connect_fresh(Config, alice, sr_presence, manual), %% Bob sends a message to Alice, and Alice receives it but doesn't acknowledge escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), escalus:wait_for_stanza(Alice), %% alice comes back, but too late, so resumption doesn't work, %% but she receives the previous h = 1 anyway + %% NewAlice is also manual ack NewAlice = kill_and_connect_with_resume_session_without_waiting_for_result(Alice), FailedResumption = escalus_connection:get_stanza(NewAlice, failed_resumption), <<"1">> = exml_query:attr(FailedResumption, <<"h">>), @@ -1037,7 +1038,7 @@ replies_are_processed_by_resumed_session(Config) -> %% 7. Packet rerouting crashes on the buffered sub request, preventing resending whole buffer %% 8. B doesn't receive the buffered message subscription_requests_are_buffered_properly(Config) -> - AliceSpec = [{manual_ack, true} | escalus_fresh:create_fresh_user(Config, alice)], + AliceSpec = escalus_fresh:create_fresh_user(Config, alice), MsgBody = <<"buffered">>, escalus:fresh_story(Config, [{bob, 1}], fun(Bob) -> % GIVEN Bob's pending subscription to Alice's presence @@ -1046,7 +1047,7 @@ subscription_requests_are_buffered_properly(Config) -> _RosterPushReq = escalus:wait_for_stanza(Bob), % WHEN Alice becomes online... - Alice = connect_spec(AliceSpec, sr_session), + Alice = connect_spec(AliceSpec, sr_session, manual), send_initial_presence(Alice), %% subscribe could come before the initial presence escalus:assert_many([is_presence(<<"available">>), is_presence(<<"subscribe">>)], @@ -1061,7 +1062,7 @@ subscription_requests_are_buffered_properly(Config) -> escalus_client:kill_connection(Config, Alice), % ...and reconnects with session replacement. - Alice2 = connect_spec(AliceSpec, session), + Alice2 = connect_spec(AliceSpec, session, manual), % THEN Alice receives (without sending initial presence): % * buffered available presence (because it's addressed to full JID) From 3d7cf11ab2c3ebbfa6a8a0e3c4291bd7268e40c8 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 15 Dec 2021 21:54:32 +0100 Subject: [PATCH 23/26] Remove exports from sm_SUITE Enable preserve_order testcase (it was not running) Fix resume_expired_session_returns_correct_h Define suite() callback first --- big_tests/tests/ct_helper.erl | 25 ++- big_tests/tests/mongoose_helper.erl | 4 +- big_tests/tests/sm_SUITE.erl | 249 ++++++++++------------------ big_tests/tests/sm_helper.erl | 26 ++- 4 files changed, 122 insertions(+), 182 deletions(-) diff --git a/big_tests/tests/ct_helper.erl b/big_tests/tests/ct_helper.erl index 106e3469af..f92bcdb9fd 100644 --- a/big_tests/tests/ct_helper.erl +++ b/big_tests/tests/ct_helper.erl @@ -3,7 +3,8 @@ repeat_all_until_all_ok/1, repeat_all_until_all_ok/2, repeat_all_until_any_fail/1, - repeat_all_until_any_fail/2]). + repeat_all_until_any_fail/2, + groups_to_all/1]). -type group_name() :: atom(). @@ -110,3 +111,25 @@ sensible_maximum_repeats() -> is_ct_started() -> lists:keymember(common_test, 1, application:which_applications()). + +groups_to_all(Groups) -> + [{group, Name} || {Name, _Opts, _Cases} <- Groups]. +<<<<<<< HEAD + +%% Module needs to be compiled with export_helper +make_groups(Module) -> + Info = Module:groups_info(), + [prepare_group(GroupAndCases) || GroupAndCases <- Info]. + +prepare_group({Group, Cases}) -> + {Group, group_name_to_props(Group), Cases}. + +group_name_to_props(Group) -> + case lists:prefix("parallel", atom_to_list(Group)) of + true -> + [parallel]; + false -> + [] + end. +======= +>>>>>>> c9bce834d... x diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index c46280d063..4109317557 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -382,8 +382,8 @@ do_wait_until(Fun, #{validator := Validator} = Opts) -> true -> {ok, Value}; _ -> wait_and_continue(Fun, Value, Opts) end - catch Error:Reason -> - wait_and_continue(Fun, {Error, Reason}, Opts) + catch Error:Reason:Stacktrace -> + wait_and_continue(Fun, {Error, Reason, Stacktrace}, Opts) end. simplify_history([H|[H|_]=T], Times) -> diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index a49220603a..f4d99c861f 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -1,75 +1,11 @@ -%% Session Management tests +%% Stream Management tests -module(sm_SUITE). --export([suite/0, - all/0, - groups/0, - init_per_suite/1, - end_per_suite/1, - init_per_group/2, - end_per_group/2, - init_per_testcase/2, - end_per_testcase/2]). - -%% parallel group --export([server_announces_sm/1, - server_enables_sm_before_session/1, - server_enables_sm_after_session/1, - server_returns_failed_after_start/1, - server_returns_failed_after_auth/1, - server_enables_resumption/1, - client_enables_sm_twice_fails_with_correct_error_stanza/1, - session_resumed_then_old_session_is_closed_gracefully_with_correct_error_stanza/1, - session_resumed_and_old_session_dead_doesnt_route_error_to_new_session/1, - basic_ack/1, - h_ok_before_session/1, - h_ok_after_session_enabled_before_session/1, - h_ok_after_session_enabled_after_session/1, - h_ok_after_a_chat/1, - h_non_given_closes_stream_gracefully/1, - resend_unacked_on_reconnection/1, - session_established/1, - wait_for_resumption/1, - resume_session/1, - resume_session_with_wrong_h_does_not_leak_sessions/1, - resume_session_with_wrong_sid_returns_item_not_found/1, - resume_session_with_wrong_namespace_is_a_noop/1, - resume_dead_session_results_in_item_not_found/1, - resume_session_kills_old_C2S_gracefully/1, - aggressively_pipelined_resume/1, - replies_are_processed_by_resumed_session/1, - subscription_requests_are_buffered_properly/1, - messages_are_properly_flushed_during_resumption/1, - messages_are_properly_flushed_during_resumption_p1_fsm_old/1]). - -%% manual_ack_freq_2 group --export([server_requests_ack_freq_2/1]). - --export([client_acks_more_than_sent/1, - too_many_unacked_stanzas/1, - resend_unacked_after_resume_timeout/1, - resume_session_state_send_message/1, - resume_session_state_stop_c2s/1, - server_requests_ack_after_session/1, - resend_more_offline_messages_than_buffer_size/1, - server_requests_ack/1]). - -%% stale_h group --export([resume_expired_session_returns_correct_h/1, - gc_repeat_after_never_means_no_cleaning/1, - gc_repeat_after_timeout_does_clean/1]). - -%% stream_mgmt_disabled group --export([no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt/1, - no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption/1]). - -%% manual_ack_freq_long_session_timeout group --export([preserve_order/1]). - -%% unacknowledged_message_hook group --export([unacknowledged_message_hook_bounce/1, - unacknowledged_message_hook_offline/1, - unacknowledged_message_hook_resume/1]). +-compile(export_all). +-include_lib("exml/include/exml.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("escalus/include/escalus.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Injected code callbacks -export([rpc_start_hook_handler/3, @@ -77,20 +13,10 @@ hook_handler_fn/3, regression_handler/5]). --include_lib("exml/include/exml.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("escalus/include/escalus.hrl"). --include_lib("common_test/include/ct.hrl"). - --define(MOD_SM, mod_stream_management). --define(CONSTRAINT_CHECK_TIMEOUT, 5000). - -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]). --import(escalus_stanza, [setattr/3]). - -import(domain_helper, [host_type/0]). -import(sm_helper, [connect_fresh/3, @@ -103,9 +29,7 @@ client_to_spec0/1, client_to_spec/1, client_to_smid/1, - wait_until_disconnected/1, try_to_resume_stream/3, - kill_and_connect_with_resume_session_without_waiting_for_result/1, stop_client_and_wait_for_termination/1, assert_alive_resources/2, get_user_present_resources/1, @@ -124,6 +48,8 @@ get_ack/1, ack_initial_presence/1]). +-define(MOD_SM, mod_stream_management). +-define(CONSTRAINT_CHECK_TIMEOUT, 5000). -define(LONG_TIMEOUT, 3600). -define(SHORT_TIMEOUT, 3). -define(SMALL_SM_BUFFER, 3). @@ -132,27 +58,21 @@ %% Suite configuration %%-------------------------------------------------------------------- +suite() -> + require_rpc_nodes([mim]) ++ escalus:suite(). + all() -> - [{group, parallel}, - {group, parallel_manual_ack_freq_1}, - {group, manual_ack_freq_2}, - {group, stale_h}, - {group, stream_mgmt_disabled}, - {group, unacknowledged_message_hook} - ]. + ct_helper:groups_to_all(groups()). groups() -> - G = [{parallel, [parallel], parallel_test_cases()}, - {parallel_manual_ack_freq_1, [parallel], parallel_manual_ack_test_cases()}, - {manual_ack_freq_2, [], [server_requests_ack_freq_2]}, - {stale_h, [], stale_h_test_cases()}, - {stream_mgmt_disabled, [], stream_mgmt_disabled_cases()}, - {manual_ack_freq_long_session_timeout, [parallel], [preserve_order]}, - {unacknowledged_message_hook, [parallel], unacknowledged_message_hook()}], - ct_helper:repeat_all_until_all_ok(G). - - -parallel_test_cases() -> + P = [parallel], + [{parallel, P, parallel_cases()}, + {parallel_manual_ack_freq_1, P, parallel_manual_ack_freq_1_cases()}, + {manual_ack_freq_2, [], manual_ack_freq_2_cases()}, + {stale_h, [], stale_h_cases()}, + {parallel_unacknowledged_message_hook, P, parallel_unacknowledged_message_hook_cases()}]. + +parallel_cases() -> [server_announces_sm, server_enables_sm_before_session, server_enables_sm_after_session, @@ -181,10 +101,9 @@ parallel_test_cases() -> replies_are_processed_by_resumed_session, subscription_requests_are_buffered_properly, messages_are_properly_flushed_during_resumption, - messages_are_properly_flushed_during_resumption_p1_fsm_old - ]. + messages_are_properly_flushed_during_resumption_p1_fsm_old]. -parallel_manual_ack_test_cases() -> +parallel_manual_ack_freq_1_cases() -> [client_acks_more_than_sent, too_many_unacked_stanzas, resend_unacked_after_resume_timeout, @@ -192,30 +111,28 @@ parallel_manual_ack_test_cases() -> resume_session_state_stop_c2s, server_requests_ack_after_session, resend_more_offline_messages_than_buffer_size, - server_requests_ack - ]. + server_requests_ack]. + +manual_ack_freq_2_cases() -> + [server_requests_ack_freq_2]. -stale_h_test_cases() -> - [ - resume_expired_session_returns_correct_h, +stale_h_cases() -> + [resume_expired_session_returns_correct_h, gc_repeat_after_never_means_no_cleaning, - gc_repeat_after_timeout_does_clean - ]. + gc_repeat_after_timeout_does_clean]. stream_mgmt_disabled_cases() -> - [ - no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt, - no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption - ]. + [no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt, + no_crash_if_stream_mgmt_disabled_but_client_requests_stream_mgmt_with_resumption]. -unacknowledged_message_hook() -> +manual_ack_freq_long_session_timeout_cases() -> + [preserve_order]. + +parallel_unacknowledged_message_hook_cases() -> [unacknowledged_message_hook_bounce, unacknowledged_message_hook_offline, unacknowledged_message_hook_resume]. -suite() -> - require_rpc_nodes([mim]) ++ escalus:suite(). - %%-------------------------------------------------------------------- %% Init & teardown %%-------------------------------------------------------------------- @@ -231,7 +148,7 @@ end_per_suite(Config) -> dynamic_modules:restore_modules(Config), escalus:end_per_suite(Config). -init_per_group(Group, Config) when Group =:= unacknowledged_message_hook; +init_per_group(Group, Config) when Group =:= parallel_unacknowledged_message_hook; Group =:= manual_ack_freq_long_session_timeout; Group =:= parallel_manual_ack_freq_1; Group =:= manual_ack_freq_2 -> @@ -282,7 +199,7 @@ end_per_testcase(CaseName, Config) -> required_modules(Scope, Name) -> SMConfig = case required_sm_opts(Scope, Name) of stopped -> stopped; - ExtraOpts -> common_sm_opts() ++ ExtraOpts + ExtraOpts -> merge_proplists(common_sm_opts(), ExtraOpts) end, [{mod_stream_management, SMConfig}, {mod_offline, []}]. @@ -295,9 +212,10 @@ required_sm_opts(group, manual_ack_freq_2) -> [{ack_freq, 2}]; required_sm_opts(group, stream_mgmt_disabled) -> stopped; -required_sm_opts(group, Group) when Group =:= unacknowledged_message_hook; - Group =:= manual_ack_freq_long_session_timeout -> +required_sm_opts(group, parallel_unacknowledged_message_hook) -> [{ack_freq, 1}]; +required_sm_opts(group, manual_ack_freq_long_session_timeout) -> + [{ack_freq, 1}, {buffer_max, 1000}]; required_sm_opts(testcase, resume_expired_session_returns_correct_h) -> [{ack_freq, 1}, {resume_timeout, ?SHORT_TIMEOUT} | stale_h(?LONG_TIMEOUT, ?LONG_TIMEOUT)]; @@ -309,6 +227,12 @@ required_sm_opts(testcase, gc_repeat_after_timeout_does_clean) -> common_sm_opts() -> [{buffer_max, ?SMALL_SM_BUFFER}]. +merge_proplists(Defaults, Values) -> + Values ++ delete_keys(proplists:get_keys(Values), Defaults). + +delete_keys(Keys, List) -> + lists:foldl(fun proplists:delete/2, List, Keys). + stale_h(RepeatAfter, Geriatric) -> [{stale_h, [{enabled, true}, {stale_h_repeat_after, RepeatAfter}, @@ -580,7 +504,7 @@ resend_unacked_on_reconnection(Config) -> %% Messages go to the offline store. %% Alice receives the messages from the offline store. NewAlice = connect_spec(AliceSpec, session, manual), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), wait_for_messages(NewAlice, Texts), %% Alice acks the delayed messages so they won't go again %% to the offline store. @@ -589,34 +513,35 @@ resend_unacked_on_reconnection(Config) -> preserve_order(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), - Alice = connect_fresh(Config, alice, sr_presence), + Alice = connect_fresh(Config, alice, sr_presence, manual), AliceSpec = client_to_spec(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"1">>)), %% kill alice connection escalus_connection:kill(Alice), - wait_until_disconnected(Alice), + C2SPid = mongoose_helper:get_session_pid(Alice), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"2">>)), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"3">>)), - NewAlice = connect_spec(AliceSpec, session), + NewAlice = connect_spec(AliceSpec, session, manual), escalus_connection:send(NewAlice, escalus_stanza:enable_sm([resume])), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"4">>)), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"5">>)), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"6">>)), receive_all_ordered(NewAlice, 1, 6), % replace connection - NewAlice2 = connect_spec(AliceSpec, session), + NewAlice2 = connect_spec(AliceSpec, session, manual), % allow messages to go to the offline storage mongoose_helper:wait_for_n_offline_messages(NewAlice, 6), - escalus_connection:send(NewAlice2, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice2), % receves messages in correct order receive_all_ordered(NewAlice2, 1, 6), @@ -624,20 +549,20 @@ preserve_order(Config) -> escalus_connection:stop(Bob), escalus_connection:stop(NewAlice2). -receive_all_ordered(Conn, N, Total) -> - case catch escalus_connection:get_stanza(Conn, msg) of - #xmlel{} = Stanza -> - NN = case Stanza#xmlel.name of - <<"message">> -> - escalus:assert(is_chat_message, [integer_to_binary(N)], Stanza), - N + 1; - _ -> - N - end, - receive_all_ordered(Conn, NN, Total); - _Error when N =:= Total -> - ok - end. +%% Receive messages from N to Last +receive_all_ordered(_Conn, N, Last) when N > Last -> + ok; +receive_all_ordered(Conn, N, Last) -> + %% Ignores acks and presences + Stanza = escalus_connection:get_stanza(Conn, {msg, N}), + NN = case Stanza#xmlel.name of + <<"message">> -> + escalus:assert(is_chat_message, [integer_to_binary(N)], Stanza), + N + 1; + _ -> + N + end, + receive_all_ordered(Conn, NN, Last). resend_unacked_after_resume_timeout(Config) -> %% connect bob and alice @@ -650,11 +575,12 @@ resend_unacked_after_resume_timeout(Config) -> escalus_connection:kill(Alice), %% ensure there is no session - wait_until_disconnected(Alice), + C2SPid = mongoose_helper:get_session_pid(Alice), + mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), %% alice come back and receives unacked message NewAlice = connect_spec(AliceSpec, session), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), escalus_new_assert:mix_match([is_presence, is_chat(<<"msg-1">>)], escalus:wait_for_stanzas(NewAlice, 2)), @@ -666,20 +592,22 @@ resume_expired_session_returns_correct_h(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, sr_presence), Alice = connect_fresh(Config, alice, sr_presence, manual), + get_ack(Alice), + %% Bob sends a message to Alice, and Alice receives it but doesn't acknowledge escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), - escalus:wait_for_stanza(Alice), + escalus:assert(is_chat_message, [<<"msg-1">>], escalus:wait_for_stanza(Alice)), %% alice comes back, but too late, so resumption doesn't work, %% but she receives the previous h = 1 anyway %% NewAlice is also manual ack - NewAlice = kill_and_connect_with_resume_session_without_waiting_for_result(Alice), + NewAlice = sm_helper:kill_and_connect_with_resume_session_without_waiting_for_result(Alice), FailedResumption = escalus_connection:get_stanza(NewAlice, failed_resumption), <<"1">> = exml_query:attr(FailedResumption, <<"h">>), %% And we can continue with bind and session escalus_session:session(escalus_session:bind(NewAlice)), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - Stanzas = [escalus_connection:get_stanza(NewAlice, msg), - escalus_connection:get_stanza(NewAlice, msg)], + send_initial_presence(NewAlice), + Stanzas = [escalus_connection:get_stanza(NewAlice, {msg, 1}), + escalus_connection:get_stanza(NewAlice, {msg, 2})], escalus_new_assert:mix_match([is_presence, is_chat(<<"msg-1">>)], Stanzas), escalus_connection:stop(Bob), escalus_connection:stop(NewAlice). @@ -719,9 +647,7 @@ resume_session_state_send_message(Config) -> ok = rpc(mim(), sys, suspend, [C2SPid]), %% alice comes back and receives unacked message - NewAlice = connect_same(Alice, session), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), - escalus:assert(is_presence, escalus_connection:get_stanza(NewAlice, presence)), + NewAlice = connect_same(Alice, presence), %% now we can resume c2s process of the old connection %% and let it process session resumption timeout ok = rpc(mim(), sys, resume, [C2SPid]), @@ -796,7 +722,7 @@ unacknowledged_message_hook_resume(Config) -> unacknowledged_message_hook_resume(AliceSpec, Resource, SMID, _C2SPid) -> NewAlice = connect_spec(AliceSpec, {resume, SMID, 1}, manual), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), {Resource, NewAlice}. unacknowledged_message_hook_bounce(Config) -> @@ -806,7 +732,7 @@ unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> NewResource = <<"new_", Resource/binary>>, NewSpec = lists:keystore(resource, 1, AliceSpec, {resource, NewResource}), NewAlice = connect_spec(NewSpec, sr_session, manual), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), %% ensure second C2S is registered so all the messages are bounced properly wait_for_resource_count(NewAlice, 2), ok = rpc(mim(), sys, terminate, [C2SPid, normal]), @@ -826,7 +752,7 @@ unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> %% C2S, but the message sequence is broken (the bounced messages %% delivered before the messages from the mod_offline storage) wait_for_process_termination(C2SRef), - escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), + send_initial_presence(NewAlice), {Resource, NewAlice}. unacknowledged_message_hook_common(RestartConnectionFN, Config) -> @@ -947,7 +873,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), %% Monitor the C2S process and disconnect Alice. - MonitorRef = erlang:monitor(process, C2SPid), + MonitorRef = monitor_session(Alice), escalus_client:kill_connection(Config, Alice), %% Ensure the c2s process is waiting for resumption. @@ -957,14 +883,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> NewAlice = connect_resume(Alice, 1), %% C2S process should die gracefully with Reason=normal. - receive - {'DOWN', MonitorRef, process, C2SPid, normal} -> - ok; - Msg -> - ct:fail("C2S did not die gracefully. Instead received: ~p", [Msg]) - after timer:seconds(1) -> - ct:fail("Old C2S did not die in time after session resumption.") - end, + wait_for_process_termination(MonitorRef), escalus_connection:stop(NewAlice). buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl index c907261f40..3dac5993f8 100644 --- a/big_tests/tests/sm_helper.erl +++ b/big_tests/tests/sm_helper.erl @@ -1,4 +1,4 @@ -%% Session Management helpers +%% Stream Management helpers -module(sm_helper). -export([client_to_spec0/1, @@ -16,8 +16,7 @@ connect_resume/2]). %% Waiting and introspection helpers --export([wait_until_disconnected/1, - try_to_resume_stream/3, +-export([try_to_resume_stream/3, kill_and_connect_with_resume_session_without_waiting_for_result/1, stop_client_and_wait_for_termination/1, assert_alive_resources/2, @@ -180,15 +179,20 @@ connect_resume(Client, SMH) -> kill_and_connect_with_resume_session_without_waiting_for_result(Alice) -> SMH = escalus_connection:get_sm_h(Alice), - NewAlice = connect_same(Alice, auth), + %% SMID could be anything, + %% we would fail anyway because C2S process would be dead when resuming SMID = client_to_smid(Alice), %% kill alice connection - escalus_connection:kill(Alice), - %% ensure there is no session - wait_until_disconnected(Alice), + kill_client_and_wait_for_termination(Alice), + NewAlice = connect_same(Alice, auth), escalus_connection:send(NewAlice, escalus_stanza:resume(SMID, SMH)), NewAlice. +kill_client_and_wait_for_termination(Alice) -> + C2SRef = monitor_session(Alice), + escalus_connection:kill(Alice), + ok = wait_for_process_termination(C2SRef). + stop_client_and_wait_for_termination(Alice) -> C2SRef = monitor_session(Alice), escalus_connection:stop(Alice), @@ -208,8 +212,7 @@ wait_for_process_termination(MRef) -> ok after 5000 -> ct:fail(wait_for_process_termination_timeout) - end, - ok. + end. wait_for_queue_length(Pid, Length) -> F = fun() -> @@ -232,9 +235,6 @@ get_c2s_unacked_count(C2SPid) -> Info = rpc(mim(), ejabberd_c2s, get_info, [C2SPid]), maps:get(stream_mgmt_buffer_size, Info). -wait_until_disconnected(Client) -> - wait_for_resource_count(Client, 0). - wait_for_resource_count(Client, N) -> mongoose_helper:wait_until(fun() -> length(get_user_alive_resources(Client)) end, N, #{name => get_user_alive_resources}). @@ -281,8 +281,6 @@ wait_for_messages(Alice, Texts) -> || {Text, Stanza} <- lists:zip(Texts, Stanzas)], ok. -assert_same_length(List1, List2) when length(List1) =:= length(List2) -> ok. - get_ack(Client) -> escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). From b0b9748cf120054c013e9ea7e2fb4871914cc383 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 16 Dec 2021 00:35:20 +0100 Subject: [PATCH 24/26] Dont import rarely used functions --- big_tests/tests/sm_SUITE.erl | 83 ++++++++++++++---------------------- 1 file changed, 33 insertions(+), 50 deletions(-) diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index f4d99c861f..c8986f1df7 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -26,25 +26,8 @@ connect_same/2, connect_same/3, connect_resume/2, - client_to_spec0/1, - client_to_spec/1, - client_to_smid/1, - try_to_resume_stream/3, - stop_client_and_wait_for_termination/1, - assert_alive_resources/2, - get_user_present_resources/1, - get_sid_by_stream_id/1, - wait_for_c2s_unacked_count/2, - wait_for_resource_count/2, - wait_for_process_termination/1, process_initial_stanza/1, send_initial_presence/1, - kill_and_connect_resume/1, - monitor_session/1, - wait_for_process_termination/1, - wait_for_queue_length/2, - send_messages/3, - wait_for_messages/2, get_ack/1, ack_initial_presence/1]). @@ -313,7 +296,7 @@ session_resumed_and_old_session_dead_doesnt_route_error_to_new_session(Config) - %% GIVEN USER WITH STREAM RESUMPTION ENABLED Alice = connect_fresh(Config, alice, sr_presence), %% WHEN FIRST SESSION DIES AND USER RESUMES FROM NEW CLIENT - Alice2 = kill_and_connect_resume(Alice), + Alice2 = sm_helper:kill_and_connect_resume(Alice), process_initial_stanza(Alice2), %% THEN new session does not have any message rerouted false = escalus_client:has_stanzas(Alice2), @@ -494,18 +477,18 @@ resend_unacked_on_reconnection(Config) -> Texts = three_texts(), Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sm_presence), - AliceSpec = client_to_spec0(Alice), + AliceSpec = sm_helper:client_to_spec0(Alice), %% Bob sends some messages to Alice. - send_messages(Bob, Alice, Texts), + sm_helper:send_messages(Bob, Alice, Texts), %% Alice receives the messages. - wait_for_messages(Alice, Texts), + sm_helper:wait_for_messages(Alice, Texts), %% Alice disconnects without acking the messages. - stop_client_and_wait_for_termination(Alice), + sm_helper:stop_client_and_wait_for_termination(Alice), %% Messages go to the offline store. %% Alice receives the messages from the offline store. NewAlice = connect_spec(AliceSpec, session, manual), send_initial_presence(NewAlice), - wait_for_messages(NewAlice, Texts), + sm_helper:wait_for_messages(NewAlice, Texts), %% Alice acks the delayed messages so they won't go again %% to the offline store. escalus_connection:send(NewAlice, escalus_stanza:sm_ack(3)). @@ -514,7 +497,7 @@ preserve_order(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sr_presence, manual), - AliceSpec = client_to_spec(Alice), + AliceSpec = sm_helper:client_to_spec(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"1">>)), %% kill alice connection @@ -568,7 +551,7 @@ resend_unacked_after_resume_timeout(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), Alice = connect_fresh(Config, alice, sr_presence), - AliceSpec = client_to_spec(Alice), + AliceSpec = sm_helper:client_to_spec(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), %% kill alice connection @@ -637,7 +620,7 @@ resume_session_state_send_message(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), - assert_alive_resources(Alice, 1), + sm_helper:assert_alive_resources(Alice, 1), %% send some messages and check if c2s can handle it escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-2">>)), @@ -677,12 +660,12 @@ resume_session_state_stop_c2s(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), %% Wait c2s process to process our presence ack. %% Otherwise, we can receive two initial presences sometimes. - wait_for_c2s_unacked_count(C2SPid, 1), + sm_helper:wait_for_c2s_unacked_count(C2SPid, 1), % kill alice connection escalus_connection:kill(Alice), % session should be alive - assert_alive_resources(Alice, 1), + sm_helper:assert_alive_resources(Alice, 1), rpc(mim(), ejabberd_c2s, stop, [C2SPid]), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), %% suspend the process to ensure that Alice has enough time to reconnect, @@ -734,7 +717,7 @@ unacknowledged_message_hook_bounce(AliceSpec, Resource, _SMID, C2SPid) -> NewAlice = connect_spec(NewSpec, sr_session, manual), send_initial_presence(NewAlice), %% ensure second C2S is registered so all the messages are bounced properly - wait_for_resource_count(NewAlice, 2), + sm_helper:wait_for_resource_count(NewAlice, 2), ok = rpc(mim(), sys, terminate, [C2SPid, normal]), {NewResource, NewAlice}. @@ -751,7 +734,7 @@ unacknowledged_message_hook_offline(AliceSpec, Resource, _SMID, C2SPid) -> %% looks like all the unacknowledged messages arrive to the new %% C2S, but the message sequence is broken (the bounced messages %% delivered before the messages from the mod_offline storage) - wait_for_process_termination(C2SRef), + sm_helper:wait_for_process_termination(C2SRef), send_initial_presence(NewAlice), {Resource, NewAlice}. @@ -768,7 +751,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> get_ack(Alice), ack_initial_presence(Alice), - SMID = client_to_smid(Alice), + SMID = sm_helper:client_to_smid(Alice), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-1">>)), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"msg-2">>)), @@ -776,7 +759,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), escalus_connection:kill(Alice), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), - assert_alive_resources(Alice, 1), + sm_helper:assert_alive_resources(Alice, 1), escalus:assert(is_chat_message, [<<"msg-1">>], wait_for_unacked_msg_hook(0, Resource, 100)), escalus:assert(is_chat_message, [<<"msg-2">>], wait_for_unacked_msg_hook(0, Resource, 100)), @@ -820,7 +803,7 @@ resume_session(Config) -> Alice = connect_spec(AliceSpec, {resume, SMID, 1}, manual), %% Alice receives the unacked messages from the previous %% interrupted session. - wait_for_messages(Alice, Texts), + sm_helper:wait_for_messages(Alice, Texts), %% Alice acks the received messages. escalus_connection:send(Alice, escalus_stanza:sm_ack(5)), escalus_connection:stop(Alice) @@ -833,11 +816,11 @@ resume_session_with_wrong_h_does_not_leak_sessions(Config) -> {_, SMID} = buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Messages), %% Resume the session. Alice = connect_spec(AliceSpec, auth, manual), - Resumed = try_to_resume_stream(Alice, SMID, 30), + Resumed = sm_helper:try_to_resume_stream(Alice, SMID, 30), escalus:assert(is_stream_error, [<<"undefined-condition">>, <<>>], Resumed), - [] = get_user_present_resources(Alice), - {error, smid_not_found} = get_sid_by_stream_id(SMID), + [] = sm_helper:get_user_present_resources(Alice), + {error, smid_not_found} = sm_helper:get_sid_by_stream_id(SMID), escalus_connection:wait_for_close(Alice, timer:seconds(5)) end). @@ -850,7 +833,7 @@ resume_session_with_wrong_namespace_is_a_noop(Config) -> Attrs2 = lists:keyreplace(<<"xmlns">>, 1, Attrs, {<<"xmlns">>, <<"not-stream-mgnt">>}), escalus_connection:send(Alice, Resume#xmlel{attrs = Attrs2}), escalus_assert:has_no_stanzas(Alice), - [] = get_user_present_resources(Alice), + [] = sm_helper:get_user_present_resources(Alice), true = escalus_connection:is_connected(Alice), escalus_connection:stop(Alice). @@ -862,9 +845,9 @@ resume_dead_session_results_in_item_not_found(Config) -> session_resumption_expects_item_not_found(Config, SMID) -> Alice = connect_fresh(Config, alice, auth), - Resumed = try_to_resume_stream(Alice, SMID, 2), + Resumed = sm_helper:try_to_resume_stream(Alice, SMID, 2), escalus:assert(is_sm_failed, [<<"item-not-found">>], Resumed), - [] = get_user_present_resources(Alice), + [] = sm_helper:get_user_present_resources(Alice), true = escalus_connection:is_connected(Alice), escalus_connection:stop(Alice). @@ -873,7 +856,7 @@ resume_session_kills_old_C2S_gracefully(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), %% Monitor the C2S process and disconnect Alice. - MonitorRef = monitor_session(Alice), + MonitorRef = sm_helper:monitor_session(Alice), escalus_client:kill_connection(Config, Alice), %% Ensure the c2s process is waiting for resumption. @@ -883,19 +866,19 @@ resume_session_kills_old_C2S_gracefully(Config) -> NewAlice = connect_resume(Alice, 1), %% C2S process should die gracefully with Reason=normal. - wait_for_process_termination(MonitorRef), + sm_helper:wait_for_process_termination(MonitorRef), escalus_connection:stop(NewAlice). buffer_unacked_messages_and_die(Config, AliceSpec, Bob, Texts) -> Alice = connect_spec(AliceSpec, sr_presence, manual), %% Bobs sends some messages to Alice. - send_messages(Bob, Alice, Texts), + sm_helper:send_messages(Bob, Alice, Texts), %% Alice receives them, but doesn't ack. - wait_for_messages(Alice, Texts), + sm_helper:wait_for_messages(Alice, Texts), %% Alice's connection is violently terminated. escalus_client:kill_connection(Config, Alice), C2SPid = mongoose_helper:get_session_pid(Alice), - SMID = client_to_smid(Alice), + SMID = sm_helper:client_to_smid(Alice), {C2SPid, SMID}. aggressively_pipelined_resume(Config) -> @@ -939,7 +922,7 @@ replies_are_processed_by_resumed_session(Config) -> escalus:send(Alice, IQReq), %% ... goes down and session is resumed. - Alice2 = kill_and_connect_resume(Alice), + Alice2 = sm_helper:kill_and_connect_resume(Alice), %% THEN the client receives the reply properly. IQReply = escalus:wait_for_stanza(Alice2), @@ -1019,7 +1002,7 @@ messages_are_properly_flushed_during_resumption(Config) -> C2SPid = mongoose_helper:get_session_pid(Alice), mongoose_helper:wait_for_c2s_state_name(C2SPid, resume_session), - wait_for_queue_length(C2SPid, 0), + sm_helper:wait_for_queue_length(C2SPid, 0), ok = rpc(mim(), sys, suspend, [C2SPid]), % WHEN new session requests resumption @@ -1030,14 +1013,14 @@ messages_are_properly_flushed_during_resumption(Config) -> MsgBody = <<"flush-regression">>, spawn_link(fun() -> - wait_for_queue_length(C2SPid, 1), + sm_helper:wait_for_queue_length(C2SPid, 1), % Bob sends a message... escalus:send(Bob, escalus_stanza:chat_to(Alice, MsgBody)), % ...we ensure that a message is enqueued in Alice's session... % (2 messages = resume request + Bob's message) - wait_for_queue_length(C2SPid, 2), + sm_helper:wait_for_queue_length(C2SPid, 2), % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) @@ -1065,14 +1048,14 @@ messages_are_properly_flushed_during_resumption_p1_fsm_old(Config) -> MsgBody = <<"flush-regression">>, spawn_link(fun() -> - wait_for_queue_length(C2SPid, 2), + sm_helper:wait_for_queue_length(C2SPid, 2), % Bob sends a message... escalus:send(Bob, escalus_stanza:chat_to(Alice, MsgBody)), % ...we ensure that a message is enqueued in Alice's session... % (2 messages = resume request + Bob's message) - wait_for_queue_length(C2SPid, 3), + sm_helper:wait_for_queue_length(C2SPid, 3), % ...and old process is resumed. ok = rpc(mim(), sys, resume, [C2SPid]) From 27e506834978fc13412f6b6d6d5a70571658d1a5 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 16 Dec 2021 13:11:23 +0100 Subject: [PATCH 25/26] Enforce order in preserve_order The proper fix would require extra tests to reproduce alternative orders reliably and fix them --- big_tests/tests/ct_helper.erl | 19 --------------- big_tests/tests/sm_SUITE.erl | 46 +++++++++++++++++++++++------------ big_tests/tests/sm_helper.erl | 15 +++++++++--- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/big_tests/tests/ct_helper.erl b/big_tests/tests/ct_helper.erl index f92bcdb9fd..dd5a0f0197 100644 --- a/big_tests/tests/ct_helper.erl +++ b/big_tests/tests/ct_helper.erl @@ -114,22 +114,3 @@ is_ct_started() -> groups_to_all(Groups) -> [{group, Name} || {Name, _Opts, _Cases} <- Groups]. -<<<<<<< HEAD - -%% Module needs to be compiled with export_helper -make_groups(Module) -> - Info = Module:groups_info(), - [prepare_group(GroupAndCases) || GroupAndCases <- Info]. - -prepare_group({Group, Cases}) -> - {Group, group_name_to_props(Group), Cases}. - -group_name_to_props(Group) -> - case lists:prefix("parallel", atom_to_list(Group)) of - true -> - [parallel]; - false -> - [] - end. -======= ->>>>>>> c9bce834d... x diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index c8986f1df7..da75fc8c68 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -493,6 +493,9 @@ resend_unacked_on_reconnection(Config) -> %% to the offline store. escalus_connection:send(NewAlice, escalus_stanza:sm_ack(3)). +%% Remove wait_for_n_offline_messages and you will get anything, but preserve_order +%% TODO Test without wait_for_n_offline_messages. It would require changes in SM +%% and more strict tests, reproducing delays in SM and in mod_offline. preserve_order(Config) -> %% connect bob and alice Bob = connect_fresh(Config, bob, presence), @@ -514,10 +517,16 @@ preserve_order(Config) -> escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"4">>)), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"5">>)), + %% Without this check we will get stuff out of order + mongoose_helper:wait_for_n_offline_messages(NewAlice, 5), + send_initial_presence(NewAlice), + %% Without this check we can get "6, 1, 2, 3, 4, 5" messages in the next receive_all_ordered + mongoose_helper:wait_for_n_offline_messages(NewAlice, 0), escalus_connection:send(Bob, escalus_stanza:chat_to_short_jid(Alice, <<"6">>)), - receive_all_ordered(NewAlice, 1, 6), + %% "2, 3, 4, 5, 6, 1" is possible (where only 1 is from offline storage, the rest is from sm) + receive_all_ordered(NewAlice, 6), % replace connection NewAlice2 = connect_spec(AliceSpec, session, manual), @@ -527,25 +536,30 @@ preserve_order(Config) -> send_initial_presence(NewAlice2), % receves messages in correct order - receive_all_ordered(NewAlice2, 1, 6), + receive_all_ordered(NewAlice2, 6), escalus_connection:stop(Bob), escalus_connection:stop(NewAlice2). -%% Receive messages from N to Last -receive_all_ordered(_Conn, N, Last) when N > Last -> - ok; -receive_all_ordered(Conn, N, Last) -> - %% Ignores acks and presences - Stanza = escalus_connection:get_stanza(Conn, {msg, N}), - NN = case Stanza#xmlel.name of - <<"message">> -> - escalus:assert(is_chat_message, [integer_to_binary(N)], Stanza), - N + 1; - _ -> - N - end, - receive_all_ordered(Conn, NN, Last). +receive_all_ordered(Conn, Last) -> + receive_all_ordered(Conn, 1, Last, []). + +%% Receive messages from N to Last. +%% Ignores acks and presences. +%% Handles case when out of order and when not enough stanzas. +receive_all_ordered(_Conn, N, Last, Acc) when N > Last -> + Texts = lists:map(fun integer_to_binary/1, lists:seq(1, Last)), + sm_helper:assert_messages(Acc, Texts); +receive_all_ordered(Conn, N, Last, Acc) -> + Stanzas = escalus:wait_for_stanzas(Conn, 1), + case Stanzas of + [#xmlel{name = <<"message">>}] -> + receive_all_ordered(Conn, N + 1, Last, Acc ++ Stanzas); + [_] -> %% Ack or presence + receive_all_ordered(Conn, N, Last, Acc); + [] -> + ct:fail({timeout_waiting, N, Acc}) + end. resend_unacked_after_resume_timeout(Config) -> %% connect bob and alice diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl index 3dac5993f8..d6c377633c 100644 --- a/big_tests/tests/sm_helper.erl +++ b/big_tests/tests/sm_helper.erl @@ -33,6 +33,7 @@ -export([send_initial_presence/1, send_messages/3, wait_for_messages/2, + assert_messages/2, get_ack/1, ack_initial_presence/1]). @@ -276,10 +277,18 @@ send_messages(Bob, Alice, Texts) -> wait_for_messages(Alice, Texts) -> Stanzas = escalus:wait_for_stanzas(Alice, length(Texts)), + assert_messages(Stanzas, Texts). + +assert_messages(Stanzas, Texts) -> assert_same_length(Stanzas, Texts), - [escalus:assert(is_chat_message, [Text], Stanza) - || {Text, Stanza} <- lists:zip(Texts, Stanzas)], - ok. + Checks = [escalus_pred:is_chat_message(Text, Stanza) + || {Text, Stanza} <- lists:zip(Texts, Stanzas)], + case lists:usort(Checks) of + [true] -> + ok; + _ -> + ct:fail({assert_messages_failed, Checks, Stanzas, Texts}) + end. get_ack(Client) -> escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)). From 7bb63ff72fc0743ce5bf73a03d03d643ee348575 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 16 Dec 2021 13:20:52 +0100 Subject: [PATCH 26/26] Print expected and received texts in sm_helper:assert_messages --- big_tests/tests/sm_helper.erl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/big_tests/tests/sm_helper.erl b/big_tests/tests/sm_helper.erl index d6c377633c..2311379c8e 100644 --- a/big_tests/tests/sm_helper.erl +++ b/big_tests/tests/sm_helper.erl @@ -280,16 +280,19 @@ wait_for_messages(Alice, Texts) -> assert_messages(Stanzas, Texts). assert_messages(Stanzas, Texts) -> - assert_same_length(Stanzas, Texts), - Checks = [escalus_pred:is_chat_message(Text, Stanza) - || {Text, Stanza} <- lists:zip(Texts, Stanzas)], - case lists:usort(Checks) of - [true] -> + Bodies = lists:map(fun get_body/1, Stanzas), + case Bodies of + Texts -> ok; _ -> - ct:fail({assert_messages_failed, Checks, Stanzas, Texts}) + ct:fail({assert_messages_failed, Stanzas, + {expected, Texts}, + {received, Bodies}}) end. +get_body(Stanza) -> + exml_query:path(Stanza, [{element, <<"body">>}, cdata]). + get_ack(Client) -> escalus:assert(is_sm_ack_request, escalus_connection:get_stanza(Client, ack)).