1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2002-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: -module(cpu_sup_SUITE).
   20: -include_lib("test_server/include/test_server.hrl").
   21: 
   22: %% Test server specific exports
   23: -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
   24: -export([init_per_suite/1, end_per_suite/1]).
   25: -export([init_per_testcase/2, end_per_testcase/2]).
   26: 
   27: %% Test cases
   28: -export([load_api/1]).
   29: -export([util_api/1, util_values/1]).
   30: -export([port/1]).
   31: -export([terminate/1, unavailable/1, restart/1]).
   32: 
   33: %% Default timetrap timeout (set in init_per_testcase)
   34: -define(default_timeout, ?t:minutes(1)).
   35: 
   36: init_per_suite(Config) when is_list(Config) ->
   37:     ?line ok = application:start(os_mon),
   38:     Config.
   39: 
   40: end_per_suite(Config) when is_list(Config) ->
   41:     ?line ok = application:stop(os_mon),
   42:     Config.
   43: 
   44: init_per_testcase(unavailable, Config) ->
   45:     terminate(Config),
   46:     init_per_testcase(dummy, Config);
   47: init_per_testcase(_Case, Config) ->
   48:     Dog = ?t:timetrap(?default_timeout),
   49:     [{watchdog, Dog} | Config].
   50: 
   51: end_per_testcase(unavailable, Config) ->
   52:     restart(Config),
   53:     end_per_testcase(dummy, Config);
   54: end_per_testcase(_Case, Config) ->
   55:     Dog = ?config(watchdog, Config),
   56:     ?t:timetrap_cancel(Dog),
   57:     ok.
   58: 
   59: suite() -> [{ct_hooks,[ts_install_cth]}].
   60: 
   61: all() -> 
   62:     case test_server:os_type() of
   63: 	{unix, sunos} ->
   64: 	    [load_api, util_api, util_values, port, unavailable];
   65: 	{unix, linux} ->
   66: 	    [load_api, util_api, util_values, port, unavailable];
   67: 	{unix, _OSname} -> [load_api];
   68: 	_OS -> [unavailable]
   69:     end.
   70: 
   71: groups() -> 
   72:     [].
   73: 
   74: init_per_group(_GroupName, Config) ->
   75:     Config.
   76: 
   77: end_per_group(_GroupName, Config) ->
   78:     Config.
   79: 
   80: 
   81: load_api(suite) ->
   82:     [];
   83: load_api(doc) ->
   84:     ["Test of load API functions"];
   85: load_api(Config) when is_list(Config) ->
   86: 
   87:     %% nprocs()
   88:     ?line N = cpu_sup:nprocs(),
   89:     ?line true = is_integer(N),
   90:     ?line true = N>0,
   91:     ?line true = N<1000000,
   92: 
   93:     %% avg1()
   94:     ?line Load1 = cpu_sup:avg1(),
   95:     ?line true = is_integer(Load1),
   96:     ?line true = Load1>0,
   97: 
   98:     %% avg5()
   99:     ?line Load5 = cpu_sup:avg5(),
  100:     ?line true = is_integer(Load5),
  101:     ?line true = Load5>0,
  102: 
  103:     %% avg15()
  104:     ?line Load15 = cpu_sup:avg15(),
  105:     ?line true = is_integer(Load15),
  106:     ?line true = Load15>0,
  107: 
  108:     ok.
  109: 
  110: util_api(suite) ->
  111:     [];
  112: util_api(doc) ->
  113:     ["Test of utilization API functions"];
  114: util_api(Config) when is_list(Config) ->
  115:     %% Some useful funs when testing util/1
  116:     BusyP = fun({user, _Share}) -> true;
  117: 	       ({nice_user, _Share}) -> true;
  118: 	       ({kernel, _Share}) -> true;
  119: 	       ({hard_irq, _Share}) -> true;
  120: 	       ({soft_irq, _Share}) -> true;
  121: 	       (_) -> false
  122: 	    end,
  123:     NonBusyP = fun({wait, _Share}) -> true;
  124: 		  ({idle, _Share}) -> true;
  125: 		  ({steal, _Share}) -> true;
  126: 		  (_) -> false
  127: 	       end,
  128:     Sum = fun({_Tag, X}, Acc) -> Acc+X end,
  129: 
  130:     %% util()
  131:     ?line Util1 = cpu_sup:util(),
  132:     ?line true = is_number(Util1),
  133:     ?line true = Util1>0,
  134:     ?line Util2 = cpu_sup:util(),
  135:     ?line true = is_number(Util2),
  136:     ?line true = Util2>0,
  137: 
  138:     %% util([])
  139:     ?line {all, Busy1, NonBusy1, []} = cpu_sup:util([]),
  140:     ?line 100.00 = Busy1 + NonBusy1,
  141: 
  142:     %% util([detailed])
  143:     ?line {Cpus2, Busy2, NonBusy2, []} = cpu_sup:util([detailed]),
  144:     ?line true = lists:all(fun(X) -> is_integer(X) end, Cpus2),
  145:     ?line true = lists:all(BusyP, Busy2),
  146:     ?line true = lists:all(NonBusyP, NonBusy2),
  147:     ?line 100.00 = lists:foldl(Sum,0,Busy2)+lists:foldl(Sum,0,NonBusy2),
  148: 
  149:     %% util([per_cpu])
  150:     ?line [{Cpu3, Busy3, NonBusy3, []}|_] = cpu_sup:util([per_cpu]),
  151:     ?line true = is_integer(Cpu3),
  152:     ?line 100.00 = Busy3 + NonBusy3,
  153: 
  154:     %% util([detailed, per_cpu])
  155:     ?line [{Cpu4, Busy4, NonBusy4, []}|_] =
  156: 	cpu_sup:util([detailed, per_cpu]),
  157:     ?line true = is_integer(Cpu4),
  158:     ?line true = lists:all(BusyP, Busy2),
  159:     ?line true = lists:all(NonBusyP, NonBusy2),
  160:     ?line 100.00 = lists:foldl(Sum,0,Busy4)+lists:foldl(Sum,0,NonBusy4),
  161: 
  162:     %% bad util/1 calls
  163:     ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util(detailed)),
  164:     ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util([detialed])),
  165: 
  166:     ok.
  167: 
  168: -define(SPIN_TIME, 1000).
  169: 
  170: util_values(suite) ->
  171:     [];
  172: util_values(doc) ->
  173:     ["Test utilization values"];
  174: util_values(Config) when is_list(Config) ->
  175: 
  176:     Tester = self(),
  177:     Ref = make_ref(),
  178:     Loop = fun (L) -> L(L) end,
  179:     Spinner = fun () ->
  180: 		      Looper = spawn_link(fun () -> Loop(Loop) end),
  181: 		      receive after ?SPIN_TIME -> ok end,
  182: 		      unlink(Looper),
  183: 		      exit(Looper, kill),
  184: 		      Tester ! Ref
  185: 	      end,
  186: 
  187:     ?line cpu_sup:util(),
  188: 
  189:     ?line spawn_link(Spinner),
  190:     ?line receive Ref -> ok end,
  191:     ?line HighUtil1 = cpu_sup:util(),
  192: 
  193:     ?line receive after ?SPIN_TIME -> ok end,
  194:     ?line LowUtil1 = cpu_sup:util(),
  195: 
  196:     ?line spawn_link(Spinner),
  197:     ?line receive Ref -> ok end,
  198:     ?line HighUtil2 = cpu_sup:util(),
  199: 
  200:     ?line receive after ?SPIN_TIME -> ok end,
  201:     ?line LowUtil2 = cpu_sup:util(),
  202: 
  203:     Utils = [{high1,HighUtil1}, {low1,LowUtil1},
  204: 	     {high2,HighUtil2}, {low2,LowUtil2}],
  205:     ?t:format("Utils: ~p~n", [Utils]),
  206: 
  207:     ?line false = LowUtil1 > HighUtil1,
  208:     ?line false = LowUtil1 > HighUtil2,
  209:     ?line false = LowUtil2 > HighUtil1,
  210:     ?line false = LowUtil2 > HighUtil2,
  211: 
  212:     ok.
  213: 
  214: 
  215: % Outdated
  216: % The portprogram is now restarted if killed, and not by os_mon...
  217: 
  218: port(suite) ->
  219:     [];
  220: port(doc) ->
  221:     ["Test that cpu_sup handles a terminating port program"];
  222: port(Config) when is_list(Config) ->
  223:     case cpu_sup_os_pid() of
  224: 	{ok, PidStr} ->
  225: 	    %% Monitor cpu_sup
  226: 	    ?line MonRef = erlang:monitor(process, cpu_sup),
  227: 	    ?line N1 = cpu_sup:nprocs(),
  228: 	    ?line true = N1>0,
  229: 
  230: 	    %% Kill the port program
  231: 	    case os:cmd("kill -9 " ++ PidStr) of
  232: 		[] ->
  233: 		    %% cpu_sup should not terminate
  234: 		    receive
  235: 			{'DOWN', MonRef, _, _, Reason} ->
  236: 			    ?line ?t:fail({unexpected_exit_reason, Reason})
  237: 		    after 3000 ->
  238: 			ok
  239: 		    end,
  240: 
  241: 		    %% Give cpu_sup time to restart cpu_sup port
  242: 		    ?t:sleep(?t:seconds(3)),
  243: 		    ?line N2 = cpu_sup:nprocs(),
  244: 		    ?line true = N2>0,
  245: 
  246: 		    erlang:demonitor(MonRef),
  247: 		    ok;
  248: 
  249: 		Line ->
  250: 		    erlang:demonitor(MonRef),
  251: 		    {skip, {not_killed, Line}}
  252: 	    end;
  253: 	_ ->
  254: 	    {skip, os_pid_not_found }
  255:     end.
  256: 
  257: terminate(suite) ->
  258:     [];
  259: terminate(Config) when is_list(Config) ->
  260:     ?line ok = application:set_env(os_mon, start_cpu_sup, false),
  261:     ?line ok = supervisor:terminate_child(os_mon_sup, cpu_sup),
  262:     ok.
  263: 
  264: unavailable(suite) ->
  265:     [];
  266: unavailable(doc) ->
  267:     ["Test correct behaviour when service is unavailable"];
  268: unavailable(Config) when is_list(Config) ->
  269: 
  270:     %% Make sure all API functions return their dummy values
  271:     ?line 0 = cpu_sup:nprocs(),
  272:     ?line 0 = cpu_sup:avg1(),
  273:     ?line 0 = cpu_sup:avg5(),
  274:     ?line 0 = cpu_sup:avg15(),
  275:     ?line 0 = cpu_sup:util(),
  276:     ?line {all,0,0,[]} = cpu_sup:util([]),
  277:     ?line {all,0,0,[]} = cpu_sup:util([detailed]),
  278:     ?line {all,0,0,[]} = cpu_sup:util([per_cpu]),
  279:     ?line {all,0,0,[]} = cpu_sup:util([detailed,per_cpu]),
  280: 
  281:     ok.
  282: 
  283: restart(suite) ->
  284:     [];
  285: restart(Config) when is_list(Config) ->
  286:     ?line ok = application:set_env(os_mon, start_cpu_sup, true),
  287:     ?line {ok, _Pid} = supervisor:restart_child(os_mon_sup, cpu_sup),
  288:     ok.
  289: 
  290: %% Aux
  291: 
  292: cpu_sup_os_pid() ->
  293:     Str = os:cmd("ps -e | grep '[c]pu_sup'"),
  294:     case io_lib:fread("~s", Str) of
  295: 	{ok, [Pid], _Rest} -> {ok, Pid};
  296: 	_ -> {error, pid_not_found}
  297:     end.