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:      function_ensure_subscribed_metric_subscribes
   32:     ].
   33: 
   34: init_per_suite(C) ->
   35:     application:load(exometer_core),
   36:     application:set_env(exometer_core, mongooseim_report_interval, 1000),
   37:     {Port, Socket} = carbon_cache_server:start(),
   38:     Sup = spawn(fun() ->
   39:                         mim_ct_sup:start_link(ejabberd_sup),
   40:                         Hooks =
   41:                         {gen_hook,
   42:                          {gen_hook, start_link, []},
   43:                          permanent,
   44:                          brutal_kill,
   45:                          worker,
   46:                          [gen_hook]},
   47:                         C2SSupervisor =
   48:                         {ejabberd_c2s_sup,
   49:                          {ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]},
   50:                          permanent,
   51:                          infinity,
   52:                          supervisor,
   53:                          [ejabberd_tmp_sup]},
   54:                         supervisor:start_child(ejabberd_sup, Hooks),
   55:                         supervisor:start_child(ejabberd_sup, C2SSupervisor),
   56:                         receive
   57:                             stop ->
   58:                                 ok
   59:                         end
   60:                 end),
   61:     Reporters = get_reporters_cfg(Port),
   62:     application:set_env(exometer_core, report, Reporters),
   63:     PortServer = carbon_cache_server:wait_for_accepting(),
   64:     gen_tcp:controlling_process(Socket, PortServer),
   65:     {ok, _Apps} = application:ensure_all_started(exometer_core),
   66:     exometer:new([carbon, packets], spiral),
   67:     [{carbon_port, Port}, {test_sup, Sup}, {carbon_server, PortServer}, {carbon_socket, Socket} | C].
   68: 
   69: end_per_suite(C) ->
   70:     Sup = ?config(test_sup, C),
   71:     Sup ! stop,
   72:     CarbonServer = ?config(carbon_server, C),
   73:     erlang:exit(CarbonServer, kill),
   74:     CarbonSocket = ?config(carbon_socket, C),
   75:     gen_tcp:close(CarbonSocket),
   76:     application:stop(exometer_core),
   77:     C.
   78: 
   79: init_per_group(Group, C) ->
   80:     [mongoose_config:set_opt(Key, Value) || {Key, Value} <- opts(Group)],
   81:     mongoose_metrics:init(),
   82:     C.
   83: 
   84: end_per_group(Group, C) ->
   85:     mongoose_metrics:remove_host_type_metrics(<<"localhost">>),
   86:     mongoose_metrics:remove_host_type_metrics(global),
   87:     [mongoose_config:unset_opt(Key) || {Key, _Value} <- opts(Group)],
   88:     C.
   89: 
   90: init_per_testcase(CN, C) when tcp_connections_detected =:= CN;
   91:                               tcp_metric_varies_with_tcp_variations =:= CN ->
   92:     exometer:setopts([global, tcpPortsUsed], [{sample_interval, 50}]),
   93:     exometer:repair([global, tcpPortsUsed]),
   94:     C;
   95: init_per_testcase(queued_messages_increase, C) ->
   96:     exometer:setopts([global, processQueueLengths], [{sample_interval, 50}]),
   97:     exometer:repair([global, processQueueLengths]),
   98:     PidsFun = fun() -> put('$internal_queue_len', 1),
   99:                        receive die -> ok
  100:                        end
  101:               end,
  102:     Pids = [spawn(PidsFun) || _ <- lists:seq(1,5)],
  103:     lists:foreach(fun(Pid) -> Pid ! undefined end, Pids),
  104:     [{pids, Pids} | C];
  105: init_per_testcase(_N, C) ->
  106:     C.
  107: 
  108: end_per_testcase(queued_messages_increase, C) ->
  109:     [Pid ! die || Pid <- ?config(pids, C)],
  110:     C;
  111: end_per_testcase(_N, C) ->
  112:     C.
  113: 
  114: up_time_positive(_C) ->
  115:     {ok, [{value, X}]} = mongoose_metrics:get_metric_value(global, nodeUpTime),
  116:     ?assert(X > 0).
  117: 
  118: function_ensure_subscribed_metric_subscribes(_C) ->
  119:     SubMetric = [happy_metric],
  120:     UnsubMetric = [sad_metric],
  121:     mongoose_metrics:ensure_subscribed_metric(global, SubMetric, spiral),
  122:     mongoose_metrics:ensure_metric(global, UnsubMetric, spiral),
  123:     Subs = exometer_report:list_subscriptions(exometer_report_graphite),
  124:     try
  125:         true = lists:keymember([global|SubMetric], 1, Subs),
  126:         false = lists:keymember([global|UnsubMetric], 1, Subs)
  127:     catch C:E:S ->
  128:               ct:pal("Subs ~p", [Subs]),
  129:               erlang:raise(C, E, S)
  130:     end.
  131: 
  132: get_new_tcp_metric_value(OldValue) ->
  133:     Validator = fun(NewValue) -> OldValue =/= NewValue end,
  134:     {ok, {ok, [{value, X}]}} = async_helper:wait_until(
  135:       fun() -> mongoose_metrics:get_metric_value(global, tcpPortsUsed) end,
  136:       true, #{validator => Validator, sleep_time => 30, time_left => 500}
  137:      ),
  138:     X.
  139: 
  140: tcp_connections_detected(_C) ->
  141:     get_new_tcp_metric_value({ok, []}).
  142: 
  143: tcp_metric_varies_with_tcp_variations(_C) ->
  144:     X = get_new_tcp_metric_value({ok, []}),
  145:     {ok, Socket} = gen_tcp:listen(1805, []),
  146:     Y = get_new_tcp_metric_value({ok, [{value, X}]}),
  147:     ?assert(Y == X + 1),
  148:     gen_tcp:close(Socket),
  149:     X = get_new_tcp_metric_value({ok, [{value, Y}]}).
  150: 
  151: queued_messages_increase(_C) ->
  152:     async_helper:wait_until(
  153:       fun() ->
  154:               {ok, L} = mongoose_metrics:get_metric_value(global, processQueueLengths),
  155:               lists:sort(L)
  156:       end,
  157:       [{fsm, 5}, {regular, 5}, {total, 10}]
  158:      ).
  159: 
  160: no_skip_metric(_C) ->
  161:     ok = mongoose_metrics:create_generic_hook_metric(<<"localhost">>, sm_register_connection_hook),
  162:     undefined = exometer:info([<<"localhost">>, sm_register_connection_hook]).
  163: 
  164: subscriptions_initialised(_C) ->
  165:     true = wait_for_update(exometer:get_value([carbon, packets], count), 60).
  166: 
  167: wait_for_update({ok, [{count,X}]}, 0) ->
  168:     X > 0;
  169: wait_for_update({ok, [{count,X}]}, _N) when X > 0 ->
  170:     true;
  171: wait_for_update({ok, [{count,0}]}, N) ->
  172:     timer:sleep(1000),
  173:     wait_for_update(exometer:get_value([carbon, packets], count), N-1).
  174: 
  175: opts(Group) ->
  176:     [{hosts, [<<"localhost">>]},
  177:      {host_types, []},
  178:      {all_metrics_are_global, Group =:= all_metrics_are_global}].
  179: 
  180: get_reporters_cfg(Port) ->
  181:     [{reporters, [
  182:                  {exometer_report_graphite, [
  183:                                              {prefix, "mongooseim"},
  184:                                              {connect_timeout, 10000},
  185:                                              {host, "127.0.0.1"},
  186:                                              {port, Port},
  187:                                              {api_key, ""}
  188:                                             ]}
  189:                 ]}].