1: %%============================================================================== 2: %% Copyright 2015 Erlang Solutions Ltd. 3: %% 4: %% Licensed under the Apache License, Version 2.0 (the "License"); 5: %% you may not use this file except in compliance with the License. 6: %% You may obtain a copy of the License at 7: %% 8: %% http://www.apache.org/licenses/LICENSE-2.0 9: %% 10: %% Unless required by applicable law or agreed to in writing, software 11: %% distributed under the License is distributed on an "AS IS" BASIS, 12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13: %% See the License for the specific language governing permissions and 14: %% limitations under the License. 15: %%============================================================================== 16: -module(mod_ping_SUITE). 17: -compile([export_all, nowarn_export_all]). 18: 19: -include_lib("escalus/include/escalus.hrl"). 20: -include_lib("escalus/include/escalus_xmlns.hrl"). 21: -include_lib("exml/include/exml.hrl"). 22: -include_lib("common_test/include/ct.hrl"). 23: 24: -import(domain_helper, [domain/0]). 25: 26: %%-------------------------------------------------------------------- 27: %% Suite configuration 28: %%-------------------------------------------------------------------- 29: all() -> 30: [{group, client_ping}, 31: {group, server_ping}, 32: {group, server_ping_kill}]. 33: 34: groups() -> 35: % Don't make these parallel! Metrics tests will most probably fail 36: % and injected hook will most probably won't work as expected. 37: G = [{client_ping, [], [disco, ping]}, 38: {server_ping, [], all_tests()}, 39: {server_ping_kill, [], all_tests()} 40: ], 41: ct_helper:repeat_all_until_all_ok(G). 42: 43: client_ping_test_cases() -> 44: [ping, 45: wrong_ping]. 46: 47: all_tests() -> 48: [disco, 49: ping, 50: wrong_ping, 51: active, 52: active_keep_alive, 53: server_ping_pong, 54: server_ping_pang]. 55: 56: suite() -> 57: escalus:suite(). 58: 59: ping_interval() -> 60: timer:seconds(3). 61: 62: ping_req_timeout() -> 63: timer:seconds(2). 64: 65: init_per_suite(Config) -> 66: mongoose_helper:inject_module(?MODULE), 67: escalus:init_per_suite(Config). 68: 69: end_per_suite(Config) -> 70: escalus_fresh:clean(), 71: escalus:end_per_suite(Config). 72: 73: init_per_group(client_ping, Config) -> 74: start_mod_ping([]), 75: Config; 76: init_per_group(server_ping, Config) -> 77: start_mod_ping([{send_pings, true}, 78: {ping_interval, ping_interval()}, 79: {ping_req_timeout, ping_req_timeout()}]), 80: Config; 81: init_per_group(server_ping_kill, Config) -> 82: start_mod_ping([{send_pings, true}, 83: {ping_interval, ping_interval()}, 84: {ping_req_timeout, ping_req_timeout()}, 85: {timeout_action, kill}]), 86: [{timeout_action, kill} | Config]. 87: 88: end_per_group(_GroupName, Config) -> 89: HostType = domain_helper:host_type(mim), 90: dynamic_modules:stop(HostType, mod_ping), 91: Config. 92: 93: init_per_testcase(server_ping_pong = CN, Config) -> 94: NConfig = setup_pong_hook(Config), 95: escalus:init_per_testcase(CN, NConfig); 96: init_per_testcase(CaseName, Config) -> 97: escalus:init_per_testcase(CaseName, Config). 98: 99: end_per_testcase(server_ping_pong = CN, Config) -> 100: NConfig = clear_pong_hook(Config), 101: escalus:init_per_testcase(CN, NConfig); 102: end_per_testcase(CaseName, Config) -> 103: escalus:end_per_testcase(CaseName, Config). 104: 105: start_mod_ping(Opts) -> 106: HostType = domain_helper:host_type(mim), 107: dynamic_modules:start(HostType, mod_ping, Opts). 108: 109: setup_pong_hook(Config) -> 110: Pid = self(), 111: HostType = domain_helper:host_type(mim), 112: mongoose_helper:successful_rpc(?MODULE, setup_pong_hook, [HostType, Pid]), 113: [{pid, Pid} | Config]. 114: 115: setup_pong_hook(HostType, Pid) -> 116: gen_hook:add_handler(user_ping_response, HostType, 117: fun ?MODULE:pong_hook_handler/3, 118: #{pid => Pid}, 50). 119: 120: pong_hook_handler(Acc, 121: #{jid := JID} = _Params, 122: #{pid := Pid} = _Extra) -> 123: Pid ! {pong, jid:to_binary(jid:to_lower(JID))}, 124: {ok, Acc}. 125: 126: clear_pong_hook(Config) -> 127: {value, {_, Pid}, NConfig} = lists:keytake(pid, 1, Config), 128: HostType = domain_helper:host_type(mim), 129: mongoose_helper:successful_rpc(?MODULE, clear_pong_hook, [HostType, Pid]), 130: NConfig. 131: 132: clear_pong_hook(HostType, Pid) -> 133: gen_hook:delete_handler(user_ping_response, HostType, 134: fun ?MODULE:pong_hook_handler/3, 135: #{pid => Pid}, 50). 136: 137: %%-------------------------------------------------------------------- 138: %% Ping tests 139: %%-------------------------------------------------------------------- 140: disco(Config) -> 141: escalus:fresh_story( 142: Config, [{alice, 1}], 143: fun(Alice) -> 144: escalus_client:send(Alice, escalus_stanza:disco_info(domain())), 145: Response = escalus_client:wait_for_stanza(Alice), 146: escalus:assert(has_feature, [?NS_PING], Response) 147: end). 148: 149: ping(ConfigIn) -> 150: Domain = domain(), 151: HostType = domain_helper:host_type(mim), 152: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 153: Metrics = [ 154: {[HostTypePrefix, mod_ping, ping_response],0}, 155: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 156: ], 157: Config = [{mongoose_metrics, Metrics} | ConfigIn], 158: escalus:fresh_story(Config, [{alice, 1}], 159: fun(Alice) -> 160: PingReq = escalus_stanza:ping_request(Domain), 161: escalus_client:send(Alice, PingReq), 162: 163: PingResp = escalus_client:wait_for_stanza(Alice), 164: escalus:assert(is_iq_result, [PingReq], PingResp) 165: end). 166: 167: wrong_ping(Config) -> 168: escalus:fresh_story(Config, [{alice, 1}], 169: fun(Alice) -> 170: Domain = domain(), 171: IQ = escalus_stanza:iq(<<"get">>, [#xmlel{name = <<"unsupported">>, 172: attrs = [{<<"xmlns">>, ?NS_PING}] 173: }]), 174: PingReq = escalus_stanza:to(IQ, Domain), 175: escalus_client:send(Alice, PingReq), 176: 177: PingResp = escalus_client:wait_for_stanza(Alice), 178: escalus:assert(is_iq_error, [PingReq], PingResp) 179: end). 180: 181: active(ConfigIn) -> 182: Domain = domain(), 183: HostType = domain_helper:host_type(mim), 184: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 185: Metrics = [ 186: {[HostTypePrefix, mod_ping, ping_response],0}, 187: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 188: ], 189: Config = [{mongoose_metrics, Metrics} | ConfigIn], 190: escalus:fresh_story(Config, [{alice, 1}], 191: fun(Alice) -> 192: wait_ping_interval(0.75), 193: escalus_client:send(Alice, escalus_stanza:ping_request(Domain)), 194: escalus:assert(is_iq_result, escalus_client:wait_for_stanza(Alice)), 195: % wait more time and check if connection got ping req 196: wait_ping_interval(0.5), 197: % it shouldn't as the ping was sent 198: false = escalus_client:has_stanzas(Alice) 199: end). 200: 201: active_keep_alive(ConfigIn) -> 202: HostType = domain_helper:host_type(mim), 203: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 204: Metrics = [ 205: {[HostTypePrefix, mod_ping, ping_response],0}, 206: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 207: ], 208: Config = [{mongoose_metrics, Metrics} | ConfigIn], 209: escalus:fresh_story(Config, [{alice, 1}], 210: fun(Alice) -> 211: wait_ping_interval(0.75), 212: escalus_tcp:send(Alice#client.rcv_pid, #xmlcdata{content = "\n"}), 213: wait_ping_interval(0.5), 214: 215: false = escalus_client:has_stanzas(Alice) 216: end). 217: 218: server_ping_pong(ConfigIn) -> 219: HostType = domain_helper:host_type(mim), 220: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 221: Metrics = [ 222: {[HostTypePrefix, mod_ping, ping_response], 5}, 223: {[HostTypePrefix, mod_ping, ping_response_timeout], 0}, 224: {[HostTypePrefix, mod_ping, ping_response_time], changed} 225: ], 226: Config = [{mongoose_metrics, Metrics} | ConfigIn], 227: %% We use 5 Alices because with just 1 sample the histogram may look like it hasn't changed 228: %% due to exometer histogram implementation 229: escalus:fresh_story(Config, [{alice, 5}], 230: fun(Alice1, Alice2, Alice3, Alice4, Alice5) -> 231: lists:foreach(fun(Alice) -> 232: PingReq = wait_for_ping_req(Alice), 233: Pong = escalus_stanza:iq_result(PingReq), 234: escalus_client:send(Alice, Pong) 235: end, [Alice1, Alice2, Alice3, Alice4, Alice5]), 236: wait_for_pong_hooks(5) 237: end). 238: 239: server_ping_pang(ConfigIn) -> 240: HostType = domain_helper:host_type(mim), 241: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 242: Metrics = [ 243: {[HostTypePrefix, mod_ping, ping_response], 0}, 244: {[HostTypePrefix, mod_ping, ping_response_timeout], 1} 245: ], 246: Config = [{mongoose_metrics, Metrics} | ConfigIn], 247: escalus:fresh_story(Config, [{alice, 1}], 248: fun(Alice) -> 249: wait_for_ping_req(Alice), 250: %% do not resp to ping req 251: ct:sleep(ping_req_timeout() + timer:seconds(1)/2), 252: TimeoutAction = ?config(timeout_action, Config), 253: check_connection(TimeoutAction, Alice), 254: escalus_client:kill_connection(Config, Alice) 255: end). 256: 257: wait_ping_interval(Ration) -> 258: WaitTime = ping_interval() * Ration, 259: ct:sleep(WaitTime). 260: 261: check_connection(kill, Client) -> 262: false = escalus_connection:is_connected(Client); 263: check_connection(_, Client) -> 264: true = escalus_connection:is_connected(Client). 265: 266: wait_for_ping_req(Alice) -> 267: PingReq = escalus_client:wait_for_stanza(Alice, timer:seconds(10)), 268: escalus:assert(is_iq_get, PingReq), 269: <<"urn:xmpp:ping">> = exml_query:path(PingReq, [{element, <<"ping">>}, 270: {attr, <<"xmlns">>}]), 271: PingReq. 272: 273: wait_for_pong_hooks(0) -> 274: ok; 275: wait_for_pong_hooks(N) -> 276: receive 277: {pong, _} -> wait_for_pong_hooks(N-1) 278: after 279: 5000 -> 280: ct:fail({pong_hook_runs_missing, N}) 281: end.