1: -module(mongooseim_metrics_SUITE).
    2: 
    3: -include_lib("exml/include/exml.hrl").
    4: -include_lib("proper/include/proper.hrl").
    5: -include_lib("eunit/include/eunit.hrl").
    6: -include("jlib.hrl").
    7: -include_lib("common_test/include/ct.hrl").
    8: 
    9: -compile([export_all, nowarn_export_all]).
   10: 
   11: all() ->
   12:     [
   13:      {group, ordinary_mode},
   14:      {group, all_metrics_are_global}
   15:     ].
   16: 
   17: groups() ->
   18:     [
   19:      {ordinary_mode, [], all_metrics_list()},
   20:      {all_metrics_are_global, [], all_metrics_list()}
   21:     ].
   22: 
   23: all_metrics_list() ->
   24:     [
   25:      no_skip_metric,
   26:      subscriptions_initialised,
   27:      tcp_connections_detected,
   28:      tcp_metric_varies_with_tcp_variations,
   29:      up_time_positive,
   30:      queued_messages_increase
   31:     ].
   32: 
   33: init_per_suite(C) ->
   34:     application:load(exometer_core),
   35:     application:set_env(exometer_core, mongooseim_report_interval, 1000),
   36:     {Port, Socket} = carbon_cache_server:start(),
   37:     Sup = spawn(fun() ->
   38:                         mim_ct_sup:start_link(ejabberd_sup),
   39:                         Hooks =
   40:                         {gen_hook,
   41:                          {gen_hook, start_link, []},
   42:                          permanent,
   43:                          brutal_kill,
   44:                          worker,
   45:                          [gen_hook]},
   46:                         C2SSupervisor =
   47:                         {ejabberd_c2s_sup,
   48:                          {ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
   49:                          permanent,
   50:                          infinity,
   51:                          supervisor,
   52:                          [ejabberd_tmp_sup]},
   53:                         supervisor:start_child(ejabberd_sup, Hooks),
   54:                         supervisor:start_child(ejabberd_sup, C2SSupervisor),
   55:                         receive
   56:                             stop ->
   57:                                 ok
   58:                         end
   59:                 end),
   60:     Reporters = get_reporters_cfg(Port),
   61:     application:set_env(exometer_core, report, Reporters),
   62:     PortServer = carbon_cache_server:wait_for_accepting(),
   63:     gen_tcp:controlling_process(Socket, PortServer),
   64:     {ok, _Apps} = application:ensure_all_started(exometer_core),
   65:     exometer:new([carbon, packets], spiral),
   66:     [{carbon_port, Port}, {test_sup, Sup}, {carbon_server, PortServer}, {carbon_socket, Socket} | C].
   67: 
   68: end_per_suite(C) ->
   69:     Sup = ?config(test_sup, C),
   70:     Sup ! stop,
   71:     CarbonServer = ?config(carbon_server, C),
   72:     erlang:exit(CarbonServer, kill),
   73:     CarbonSocket = ?config(carbon_socket, C),
   74:     gen_tcp:close(CarbonSocket),
   75:     application:stop(exometer_core),
   76:     C.
   77: 
   78: init_per_group(Group, C) ->
   79:     [mongoose_config:set_opt(Key, Value) || {Key, Value} <- opts(Group)],
   80:     mongoose_metrics:init(),
   81:     C.
   82: 
   83: end_per_group(Group, C) ->
   84:     mongoose_metrics:remove_host_type_metrics(<<"localhost">>),
   85:     mongoose_metrics:remove_host_type_metrics(global),
   86:     [mongoose_config:unset_opt(Key) || {Key, _Value} <- opts(Group)],
   87:     C.
   88: 
   89: init_per_testcase(CN, C) when tcp_connections_detected =:= CN;
   90:                               tcp_metric_varies_with_tcp_variations =:= CN ->
   91:     exometer:setopts([global, tcpPortsUsed], [{sample_interval, 50}]),
   92:     exometer:repair([global, tcpPortsUsed]),
   93:     C;
   94: init_per_testcase(queued_messages_increase, C) ->
   95:     exometer:setopts([global, processQueueLengths], [{sample_interval, 50}]),
   96:     exometer:repair([global, processQueueLengths]),
   97:     PidsFun = fun() -> put('$internal_queue_len', 1),
   98:                        receive die -> ok
   99:                        end
  100:               end,
  101:     Pids = [spawn(PidsFun) || _ <- lists:seq(1,5)],
  102:     lists:foreach(fun(Pid) -> Pid ! undefined end, Pids),
  103:     [{pids, Pids} | C];
  104: init_per_testcase(_N, C) ->
  105:     C.
  106: 
  107: end_per_testcase(queued_messages_increase, C) ->
  108:     [Pid ! die || Pid <- ?config(pids, C)],
  109:     C;
  110: end_per_testcase(_N, C) ->
  111:     C.
  112: 
  113: up_time_positive(_C) ->
  114:     {ok, [{value, X}]} = mongoose_metrics:get_metric_value(global, nodeUpTime),
  115:     ?assert(X > 0).
  116: 
  117: get_new_tcp_metric_value(OldValue) ->
  118:     Validator = fun(NewValue) -> OldValue =/= NewValue end,
  119:     {ok, {ok, [{value, X}]}} = async_helper:wait_until(
  120:       fun() -> mongoose_metrics:get_metric_value(global, tcpPortsUsed) end,
  121:       true, #{validator => Validator, sleep_time => 30, time_left => 500}
  122:      ),
  123:     X.
  124: 
  125: tcp_connections_detected(_C) ->
  126:     get_new_tcp_metric_value({ok, []}).
  127: 
  128: tcp_metric_varies_with_tcp_variations(_C) ->
  129:     X = get_new_tcp_metric_value({ok, []}),
  130:     {ok, Socket} = gen_tcp:listen(1805, []),
  131:     Y = get_new_tcp_metric_value({ok, [{value, X}]}),
  132:     ?assert(Y == X + 1),
  133:     gen_tcp:close(Socket),
  134:     X = get_new_tcp_metric_value({ok, [{value, Y}]}).
  135: 
  136: queued_messages_increase(_C) ->
  137:     async_helper:wait_until(
  138:       fun() ->
  139:               {ok, L} = mongoose_metrics:get_metric_value(global, processQueueLengths),
  140:               lists:sort(L)
  141:       end,
  142:       [{fsm, 5}, {regular, 5}, {total, 10}]
  143:      ).
  144: 
  145: no_skip_metric(_C) ->
  146:     ok = mongoose_metrics:create_generic_hook_metric(<<"localhost">>, sm_register_connection_hook),
  147:     undefined = exometer:info([<<"localhost">>, sm_register_connection_hook]).
  148: 
  149: subscriptions_initialised(_C) ->
  150:     true = wait_for_update(exometer:get_value([carbon, packets], count), 60).
  151: 
  152: wait_for_update({ok, [{count,X}]}, 0) ->
  153:     X > 0;
  154: wait_for_update({ok, [{count,X}]}, _N) when X > 0 ->
  155:     true;
  156: wait_for_update({ok, [{count,0}]}, N) ->
  157:     timer:sleep(1000),
  158:     wait_for_update(exometer:get_value([carbon, packets], count), N-1).
  159: 
  160: opts(Group) ->
  161:     [{hosts, [<<"localhost">>]},
  162:      {host_types, []},
  163:      {all_metrics_are_global, Group =:= all_metrics_are_global}].
  164: 
  165: get_reporters_cfg(Port) ->
  166:     [{reporters, [
  167:                  {exometer_report_graphite, [
  168:                                              {prefix, "mongooseim"},
  169:                                              {connect_timeout, 10000},
  170:                                              {host, "127.0.0.1"},
  171:                                              {port, Port},
  172:                                              {api_key, ""}
  173:                                             ]}
  174:                 ]}].