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: 
   17: -module(mod_time_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: -include_lib("escalus/include/escalus.hrl").
   20: -include_lib("common_test/include/ct.hrl").
   21: -include_lib("eunit/include/eunit.hrl").
   22: -include_lib("escalus/include/escalus_xmlns.hrl").
   23: -include_lib("exml/include/exml.hrl").
   24: %%--------------------------------------------------------------------
   25: %% Suite configuration
   26: %%--------------------------------------------------------------------
   27: 
   28: all() ->
   29:     [{group, mod_time}].
   30: 
   31: groups() ->
   32:     [{mod_time, [parallel], [ask_for_time, time_service_discovery]}].
   33: 
   34: suite() ->
   35:     escalus:suite().
   36: 
   37: %%--------------------------------------------------------------------
   38: %% Init & teardown
   39: %%--------------------------------------------------------------------
   40: 
   41: init_per_suite(Config) ->
   42:     dynamic_modules:start(domain_helper:host_type(), mod_time, config_parser_helper:default_mod_config(mod_time)),
   43:     escalus:init_per_suite(Config).
   44: 
   45: end_per_suite(Config) ->
   46:     dynamic_modules:stop(domain_helper:host_type(), mod_time),
   47:     escalus_fresh:clean(),
   48:     escalus:end_per_suite(Config).
   49: 
   50: init_per_group(mod_time, Config) ->
   51:     Config.
   52: 
   53: end_per_group(mod_time, Config) ->
   54:     Config.
   55: 
   56: init_per_testcase(CaseName, Config) ->
   57:     escalus:init_per_testcase(CaseName, Config).
   58: 
   59: end_per_testcase(CaseName, Config) ->
   60:     escalus:end_per_testcase(CaseName, Config).
   61: 
   62: %%--------------------------------------------------------------------
   63: %% Time request test
   64: %%--------------------------------------------------------------------
   65: 
   66: ask_for_time(Config) ->
   67:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
   68:         Server = escalus_users:get_server(Config, alice),
   69:         ID = escalus_stanza:id(),
   70:         TimeStanza = time_request_stanza(Server, ID),
   71:         escalus_client:send(Alice, TimeStanza),
   72:         Reply = escalus:wait_for_stanza(Alice, 5000),
   73:         escalus:assert(is_iq_result, Reply),
   74:         escalus:assert(fun check_ns/1, Reply),
   75:         {Tzo, Utc} = time_from_stanza(Reply),
   76:         ?assertEqual(true, tzo_regex(Tzo)),
   77:         ?assertEqual(true, utc_regex(Utc))
   78:     end).
   79: 
   80: %%--------------------------------------------------------------------
   81: %% Service discovery test
   82: %%--------------------------------------------------------------------
   83: 
   84: time_service_discovery(Config) ->
   85:     escalus:fresh_story(
   86:         Config, [{alice, 1}],
   87:         fun(Client) ->
   88:             ServJID = escalus_client:server(Client),
   89:             Res = escalus:send_and_wait(Client,
   90:                                         escalus_stanza:disco_info(ServJID)),
   91:             escalus:assert(is_iq_result, Res),
   92:             escalus:assert(has_feature, [?NS_TIME], Res)
   93:         end).
   94: %%--------------------------------------------------------------------
   95: %% Helpers
   96: %%--------------------------------------------------------------------
   97: 
   98: time_request_stanza(Server, ID) ->
   99:     #xmlel{name = <<"iq">>,
  100:            attrs = [{<<"type">>, <<"get">>},
  101:                     {<<"id">>, ID}, {<<"to">>, Server}],
  102:            children = [#xmlel{name = <<"time">>,
  103:                               attrs = [{<<"xmlns">>, ?NS_TIME}]}]}.
  104: 
  105: check_ns(#xmlel{name = <<"iq">>, attrs = _, children = [Child]}) ->
  106:     case Child of
  107:         #xmlel{name = <<"time">>, attrs = [{<<"xmlns">>, ?NS_TIME}], children = _} -> true;
  108:         _ -> false
  109:     end;
  110: 
  111: check_ns(_) ->
  112:     false.
  113: 
  114: time_from_stanza(#xmlel{name = <<"iq">>, attrs = _, children = [Child]}) ->
  115:     case Child of
  116:         #xmlel{name = <<"time">>, attrs = [{<<"xmlns">>, ?NS_TIME}], children = Times} ->
  117:             case Times of
  118:                 [#xmlel{name = <<"tzo">>, attrs = _, children = [#xmlcdata{content = Tzo}]},
  119:                  #xmlel{name = <<"utc">>, attrs = _, children = [#xmlcdata{content = Utc}]}] ->
  120:                     {Tzo, Utc};
  121:                 _ -> no_timezone
  122:             end;
  123:         _ -> wrong_stanza
  124:     end.
  125: 
  126: %% check XEP-0082: XMPP Date and Time Profiles
  127: tzo_regex(Tzo) ->
  128:     String = binary_to_list(Tzo),
  129:     {match, [{0, 6}]} = re:run(String, "^[+|-][0-9]{2}:[0-9]{2}"),
  130:     true.
  131: 
  132: utc_regex(Utc) ->
  133:     String = binary_to_list(Utc),
  134:     {match, _} = re:run(String, "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z"),
  135:     true.