1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1997-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: %%% Purpose : Test the timer module a simpler/faster test than timer_SUITE
   20: 
   21: -module(timer_simple_SUITE).
   22: 
   23: %% external
   24: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   25: 	 init_per_group/2,end_per_group/2, 
   26: 	 init_per_testcase/2,
   27: 	 apply_after/1,
   28: 	 send_after1/1,
   29: 	 send_after2/1,
   30: 	 send_after3/1,
   31: 	 exit_after1/1,
   32: 	 exit_after2/1,
   33: 	 kill_after1/1,
   34: 	 kill_after2/1,
   35: 	 apply_interval/1,
   36: 	 send_interval1/1,
   37: 	 send_interval2/1,
   38: 	 send_interval3/1,
   39: 	 send_interval4/1,
   40: 	 cancel1/1,
   41: 	 cancel2/1,
   42: 	 tc/1,
   43: 	 unique_refs/1,
   44: 	 timer_perf/1]).
   45: 
   46: %% internal
   47: -export([forever/0,
   48: 	 do_nrev/2,
   49: 	 send/2,
   50: 	 timer/4,
   51: 	 timer/5]).
   52: 
   53: -include_lib("test_server/include/test_server.hrl").
   54: 
   55: -define(MAXREF, (1 bsl 18)).
   56: -define(REFMARG, 30).
   57: 
   58: suite() -> [{ct_hooks,[ts_install_cth]}].
   59: 
   60: all() -> 
   61:     [apply_after, send_after1, send_after2, send_after3,
   62:      exit_after1, exit_after2, kill_after1, kill_after2,
   63:      apply_interval, send_interval1, send_interval2,
   64:      send_interval3, send_interval4, cancel1, cancel2, tc,
   65:      unique_refs, timer_perf].
   66: 
   67: groups() -> 
   68:     [].
   69: 
   70: init_per_suite(Config) ->
   71:     Config.
   72: 
   73: end_per_suite(_Config) ->
   74:     ok.
   75: 
   76: init_per_group(_GroupName, Config) ->
   77:     Config.
   78: 
   79: end_per_group(_GroupName, Config) ->
   80:     Config.
   81: 
   82: 
   83: init_per_testcase(_, Config) when is_list(Config) ->
   84:     timer:start(),
   85:     Config.
   86: 
   87: %% Testing timer interface!!
   88: 
   89: apply_after(doc) -> "Test of apply_after, with sending of message.";
   90: apply_after(suite) -> [];
   91: apply_after(Config) when is_list(Config) ->
   92:     ?line timer:apply_after(500, ?MODULE, send, [self(), ok_apply]),
   93:     ?line ok = get_mess(1000, ok_apply).
   94: 
   95: send_after1(doc) -> "Test of send_after with time = 0.";
   96: send_after1(suite) -> [];
   97: send_after1(Config) when is_list(Config) ->
   98:     ?line timer:send_after(0, ok_send1),
   99:     ?line ok = get_mess(1000, ok_send1).
  100: 
  101: send_after2(doc) -> "Test of send_after with time = 500.";
  102: send_after2(suite) -> [];
  103: send_after2(Config) when is_list(Config) ->
  104:     ?line timer:send_after(500, self(), ok_send2),
  105:     ?line ok = get_mess(2000, ok_send2).
  106: 
  107: send_after3(doc) -> "Test of send_after with time = 500, with receiver "
  108: 			"a registered process. [OTP-2735]";
  109: send_after3(suite) -> [];
  110: send_after3(Config) when is_list(Config) ->
  111:     ?line Name = list_to_atom(pid_to_list(self())),
  112:     ?line register(Name, self()),
  113:     ?line timer:send_after(500, Name, ok_send3),
  114:     ?line ok = get_mess(2000, ok_send3),
  115:     ?line unregister(Name).
  116: 
  117: exit_after1(doc) -> "Test of exit_after with time = 1000.";
  118: exit_after1(suite) -> [];
  119: exit_after1(Config) when is_list(Config) ->
  120:     ?line process_flag(trap_exit, true),
  121:     ?line Pid = spawn_link(?MODULE, forever, []),
  122:     ?line timer:exit_after(1000, Pid, exit_test1),
  123:     ?line ok = get_mess(5000, {'EXIT', Pid, exit_test1}).
  124: 
  125: exit_after2(doc) -> "Test of exit_after with time = 1000. The process to "
  126: 			"exit is the name of a registered process. "
  127: 			"[OTP-2735]";
  128: exit_after2(suite) -> [];
  129: exit_after2(Config) when is_list(Config) ->
  130:     ?line process_flag(trap_exit, true),
  131:     ?line Pid = spawn_link(?MODULE, forever, []),
  132:     ?line Name = list_to_atom(pid_to_list(Pid)),
  133:     ?line register(Name, Pid),
  134:     ?line timer:exit_after(1000, Name, exit_test2),
  135:     ?line ok = get_mess(2000, {'EXIT', Pid, exit_test2}).
  136: 
  137: kill_after1(doc) -> "Test of kill_after with time = 1000.";
  138: kill_after1(suite) -> [];
  139: kill_after1(Config) when is_list(Config) ->
  140:     ?line process_flag(trap_exit, true),
  141:     ?line Pid = spawn_link(?MODULE, forever, []),
  142:     ?line timer:kill_after(1000, Pid),
  143:     ?line ok = get_mess(2000, {'EXIT', Pid, killed}).
  144: 
  145: kill_after2(doc) -> "Test of kill_after with time = 1000. The process to "
  146: 			"exit is the name of a registered process. "
  147: 			"[OTP-2735]";
  148: kill_after2(suite) -> [];
  149: kill_after2(Config) when is_list(Config) ->
  150:     ?line process_flag(trap_exit, true),
  151:     ?line Pid = spawn_link(?MODULE, forever, []),
  152:     ?line Name = list_to_atom(pid_to_list(Pid)),
  153:     ?line register(Name, Pid),
  154:     ?line timer:kill_after(1000, Name),
  155:     ?line ok = get_mess(2000, {'EXIT', Pid, killed}).
  156: 
  157: apply_interval(doc) -> "Test of apply_interval by sending messages. Receive "
  158:                        "3 messages, cancel the timer, and check that we do "
  159: 		       "not get any more messages.";
  160: apply_interval(suite) -> [];
  161: apply_interval(Config) when is_list(Config) ->
  162:     ?line {ok, Ref} = timer:apply_interval(1000, ?MODULE, send, 
  163: 				     [self(), apply_int]),
  164:     ?line ok = get_mess(1500, apply_int, 3),
  165:     ?line timer:cancel(Ref),
  166:     ?line nor = get_mess(1000, apply_int).
  167: 
  168: send_interval1(doc) -> "Test of send_interval/2. Receive 5 messages, cancel "
  169: 		      "the timer, and check that we do not get any more "
  170:                       "messages.";
  171: send_interval1(suite) -> [];
  172: send_interval1(Config) when is_list(Config) ->
  173:     {ok, Ref} = timer:send_interval(1000, send_int),
  174:     ?line ok = get_mess(1500, send_int, 5),
  175:     timer:cancel(Ref),
  176:     ?line nor = get_mess(1000, send_int). % We should receive only five
  177: 
  178: send_interval2(doc) -> "Test of send_interval/3. Receive 2 messages, cancel "
  179: 		      "the timer, and check that we do not get any more "
  180:                       "messages.";
  181: send_interval2(suite) -> [];
  182: send_interval2(Config) when is_list(Config) ->
  183:     {ok, Ref} = timer:send_interval(1000, self(), send_int2),
  184:     ?line ok = get_mess(1500, send_int2, 2),
  185:     timer:cancel(Ref),
  186:     ?line nor = get_mess(1000, send_int2).  % We should receive only two
  187: 
  188: send_interval3(doc) -> "Test of send_interval/3. Receive 2 messages, cancel "
  189: 		      "the timer, and check that we do not get any more "
  190:                       "messages. The receiver is the name of a registered "
  191: 			   "process. [OTP-2735]";
  192: send_interval3(suite) -> [];
  193: send_interval3(Config) when is_list(Config) ->
  194:     ?line process_flag(trap_exit, true),
  195:     ?line Name = list_to_atom(pid_to_list(self())),
  196:     ?line register(Name, self()),
  197:     ?line {ok, Ref} = timer:send_interval(1000, Name, send_int3),
  198:     ?line ok = get_mess(1500, send_int3, 2),
  199:     timer:cancel(Ref),
  200:     ?line nor = get_mess(1000, send_int3),  % We should receive only two
  201:     ?line unregister(Name).
  202: 
  203: send_interval4(doc) -> "Test that send interval stops sending msg when the "
  204: 			   "receiving process terminates.";
  205: send_interval4(suite) -> [];
  206: send_interval4(Config) when is_list(Config) ->
  207:     ?line timer:send_interval(500, one_time_only),
  208:     receive 
  209: 	one_time_only -> ok
  210:     end,
  211:     ?line timer_server ! {'EXIT', self(), normal}, % Should remove the timer
  212:     ?line timer:send_after(600, send_intv_ok),
  213:     ?line send_intv_ok = receive 
  214: 			     Msg -> Msg
  215: 			 end.
  216: 
  217: cancel1(doc) -> "Test that we can cancel a timer.";
  218: cancel1(suite) -> [];
  219: cancel1(Config) when is_list(Config) ->
  220:     ?line {ok, Ref} = timer:send_after(1000, this_should_be_canceled),
  221:     ?line timer:cancel(Ref),
  222:     ?line nor = get_mess(2000, this_should_be_canceled). % We should rec 0 msgs
  223: 
  224: cancel2(doc) -> "Test cancel/1 with bad argument.";
  225: cancel2(suite) -> [];
  226: cancel2(Config) when is_list(Config) ->
  227:     ?line {error, badarg} = timer:cancel(no_reference). 
  228: 
  229: tc(doc) -> "Test sleep/1 and tc/3.";
  230: tc(suite) -> [];
  231: tc(Config) when is_list(Config) ->
  232:     %% This should test both sleep and tc/3
  233:     ?line {Res1, ok} = timer:tc(timer, sleep, [500]),
  234:     ?line ok = 	if
  235: 		    Res1 < 500*1000 -> {too_early, Res1}; % Too early
  236: 		    Res1 > 800*1000 -> {too_late, Res1};  % Too much time
  237: 		    true -> ok
  238: 		end,
  239: 
  240:     %% tc/2
  241:     ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]),
  242:     ?line ok = 	if
  243: 		    Res2 < 500*1000 -> {too_early, Res2}; % Too early
  244: 		    Res2 > 800*1000 -> {too_late, Res2};  % Too much time
  245: 		    true -> ok
  246: 		end,
  247:     
  248:     %% tc/1
  249:     ?line {Res3, ok} = timer:tc(fun() -> timer:sleep(500) end),
  250:     ?line ok = 	if
  251:     		    Res3 < 500*1000 -> {too_early, Res3}; % Too early
  252:     		    Res3 > 800*1000 -> {too_late, Res3};  % Too much time
  253:     		    true -> ok
  254:     		end,
  255: 
  256:     %% Check that timer:tc don't catch errors
  257:     ?line ok = try timer:tc(erlang, exit, [foo]) 
  258: 	       catch exit:foo -> ok
  259: 	       end,
  260:     
  261:     ?line ok = try timer:tc(fun(Reason) -> 1 = Reason end, [foo]) 
  262: 	       catch error:{badmatch,_} -> ok
  263: 	       end,
  264:     
  265:     ?line ok = try timer:tc(fun() -> throw(foo) end) 
  266: 	       catch foo -> ok
  267: 	       end,
  268:     
  269:     %% Check that return values are propageted
  270:     Self = self(),
  271:     ?line {_, Self} = timer:tc(erlang, self, []),
  272:     ?line {_, Self} = timer:tc(fun(P) -> P end, [self()]),
  273:     ?line {_, Self} = timer:tc(fun() -> self() end),
  274: 
  275:     ?line Sec = timer:seconds(4),
  276:     ?line Min = timer:minutes(4),
  277:     ?line Hour = timer:hours(4),
  278:     ?line MyRes = 4*1000 + 4*60*1000 + 4*60*60*1000,
  279:     ?line if  MyRes == Sec + Min + Hour -> ok end,
  280:     ?line TimerRes = timer:hms(4,4,4),
  281:     ?line if MyRes == TimerRes -> ok end,
  282:     ok.
  283: 
  284: unique_refs(doc) ->
  285:     "Tests that cancellations of one-shot timers do not accidentally "
  286: 	"cancel interval timers [OTP-2771].";
  287: unique_refs(suite) ->
  288:     [];
  289: unique_refs(Config) when is_list(Config) ->
  290:     ?line ITimers = repeat_send_interval(10),		% 10 interval timers
  291:     ?line eat_refs(?MAXREF - ?REFMARG),
  292:     ?line set_and_cancel_one_shots(?REFMARG),
  293:     ?line NumLeft = num_timers(),
  294:     ?line io:format("~w timers left, should be 10\n", [NumLeft]),
  295:     ?line cancel(ITimers),
  296:     ?line receive_nisse(),
  297:     ?line 10 = NumLeft.
  298: 
  299: 
  300: repeat_send_interval(0) ->
  301:     [];
  302: repeat_send_interval(M) ->
  303:     ?line {ok, Ref} = timer:send_interval(6000,self(), nisse),
  304:     ?line [Ref| repeat_send_interval(M - 1)].
  305: 
  306: eat_refs(0) ->
  307:     0;
  308: eat_refs(N) ->
  309:     _ = make_ref(),
  310:     eat_refs(N-1).
  311: 
  312: set_and_cancel_one_shots(0) ->
  313:     0;
  314: set_and_cancel_one_shots(N) ->
  315:     {ok, Ref} = timer:send_after(7000, self(), kalle),
  316:     %% Cancel twice
  317:     timer:cancel(Ref),			
  318:     timer:cancel(Ref),
  319:     set_and_cancel_one_shots(N-1).
  320: 
  321: cancel([T| Ts]) ->
  322:     ?line timer:cancel(T),
  323:     ?line cancel(Ts);
  324: cancel([]) ->
  325:     ok.
  326: 
  327: num_timers() ->
  328:     {{_, TotalTimers},{_, _IntervalTimers}} = timer:get_status(),
  329:     TotalTimers.
  330: 
  331: receive_nisse() ->    
  332:     receive
  333: 	nisse ->
  334: 	    receive_nisse()
  335:     after 0 ->
  336: 	    ok
  337:     end.
  338: 
  339: 
  340: get_mess(Time, Mess) -> get_mess(Time, Mess, 1).
  341: get_mess(_, _, 0) -> ok;  % Received
  342: get_mess(Time, Mess, N) ->
  343:     receive 
  344: 	Mess -> get_mess(Time, Mess, N-1)
  345:     after Time
  346: 	  -> nor   % Not Received
  347:     end.
  348: 
  349: forever() ->
  350:     timer:sleep(1000),
  351:     forever().
  352: 
  353: 
  354: %
  355: % Testing for performance (on different implementations) of timers 
  356: % 
  357: 
  358: timer_perf(suite) -> [];
  359: timer_perf(Config) when is_list(Config) ->
  360:     Dog = ?t:timetrap(?t:minutes(10)),
  361:     Res = performance(timer),
  362:     ?t:timetrap_cancel(Dog),
  363:     Res.
  364: 
  365: performance(Mod) ->
  366:     process_flag(trap_exit, true),    
  367:     {Y,Mo,D} = date(),
  368:     {H,M,S} = time(),
  369:     io:format("Testing module '~p' Date: ~w/~w/~w ~w:~w:~w~n", 
  370: 	      [Mod,Y,Mo,D,H,M,S]),
  371:     Result = big_test(Mod),
  372:     report_result(Result).
  373: 
  374: big_test(M) ->
  375:     Load_Pids = start_nrev(20, M),   % Increase if more load wanted :)
  376:  
  377:     apply(M, sleep, [9000]),
  378:     LPids = spawn_timers(5, M, 10000, 5),
  379: 
  380:     apply(M, sleep, [4000]),
  381:     MPids = spawn_timers(10, M, 1000, 6),
  382: 
  383:     apply(M, sleep, [3500]),
  384:     SPids = spawn_timers(15, M, 100, 3),
  385: 
  386:     Res = wait(SPids ++ MPids ++ LPids, [], 0, M),
  387:     
  388:     lists:foreach(fun(Pid) -> exit(Pid, kill) end, Load_Pids),
  389:     Res.
  390: 
  391: wait([], Res, N, _) ->
  392:     {Res, N};
  393: wait(Pids, ResList, N, M) ->
  394:     receive
  395: 	{Pid, ok, Res, T} ->
  396: 	    wait(lists:delete(Pid, Pids), [{T, Res} | ResList], N, M);
  397: 	{Pid, Error}->
  398: 	    ?line test_server:fail(Error),
  399: 	    wait(lists:delete(Pid, Pids), ResList, N+1, M);
  400: 	{'EXIT', Pid, normal} ->
  401: 	    wait(lists:delete(Pid, Pids), ResList, N, M);
  402: 	{'EXIT', Pid, Reason} ->
  403:     	    ?line test_server:fail({Pid,Reason})
  404:     end.
  405: 
  406: spawn_timers(0, _, _, _) ->
  407:     [];
  408: spawn_timers(N, M, T, NumIter) ->
  409:     apply(M, sleep, [120*N]),
  410:     Pid1 = spawn_link(?MODULE, timer, [apply, M, T, self()]),
  411:     Pid2 = spawn_link(?MODULE, timer, [interval, M, T, self(), NumIter]),
  412:     [Pid1, Pid2 | spawn_timers(N-1, M, T, NumIter)].
  413: 
  414: timer(apply, Mod, T, Pid) ->
  415:     Before = system_time(),
  416:     {ok, Ref} = apply(Mod, apply_after, [T, ?MODULE, send, [self(), done]]),
  417:     receive 
  418: 	done ->
  419: 	    After = system_time(),
  420: 	    Pid ! {self(), ok, (After-Before) div 1000, T}
  421:     after T*3 + 300 ->   % Watch dog
  422: 	    io:format("WARNING TIMER WATCHDOG timed out: ~w ~n", [T]),
  423: 	    timer:cancel(Ref),
  424: 	    Pid ! {self(), watch_dog_timed_out}
  425:     end.
  426: 
  427: timer(interval, Mod, T, Pid, NumIter) ->
  428:     Before = system_time(),
  429:     {ok, Ref} = apply(Mod, apply_interval, [T, ?MODULE, send, [self(), done]]),
  430:     timer_irec(Before, T, {0, NumIter}, [], {Pid, Mod, Ref}).
  431: 
  432: timer_irec(_Start, T, {N, N}, Res, {Pid, Mod, Ref}) ->
  433:     apply(Mod, cancel, [Ref]),
  434:     Min = lists:min(Res),
  435:     Max = lists:max(Res),
  436:     Tot = lists:sum(Res),
  437:     Pid ! {self(), ok, {N, Tot, Tot div N, Min, Max}, T};
  438: timer_irec(Start, T, {N, Max}, Res, {Pid, Mod, Ref}) ->
  439:     receive
  440: 	done ->
  441: 	    Now = system_time(),
  442: 	    Elapsed = (Now - (Start + (N*T*1000))) div 1000,
  443: %	    io:format("~w Now ~w Started ~w Elap ~w~n", [T,Now,Start,Elapsed]),
  444: 	    timer_irec(Start, T,
  445: 		       {N+1, Max},
  446: 		       [Elapsed | Res],
  447: 		       {Pid, Mod, Ref})
  448:     after T*3 + 300 ->
  449: 	    apply(Mod, cancel, [Ref]),
  450: 	    io:format("WARNING: TIMER WATCHDOG timed out <Interval>~w~n",[T]),
  451: 	    Pid ! {self(), timer_watchdog_timed_out_in_interlval_test}  
  452:     end.
  453: 
  454: %% ------------------------------------------------------- %%
  455: %%  Small last generator
  456: 
  457: start_nrev(0, _) ->
  458:     [];
  459: 
  460: start_nrev(N, M) ->
  461:     Pid = spawn_link(?MODULE, do_nrev, [N, M]),
  462:     [Pid | start_nrev(N-1, M)].
  463: 
  464: do_nrev(Sleep, Mod) ->
  465:     apply(Mod, sleep, [50 * Sleep]),
  466:     test(1000,"abcdefghijklmnopqrstuvxyz1234"),
  467:     ok.
  468: 
  469: test(0,_) ->
  470:     true;
  471: test(N,L) ->
  472:     nrev(L),
  473:     test(N - 1, L).
  474: 
  475: nrev([]) ->
  476:     [];
  477: nrev([H|T]) ->
  478:     append(nrev(T), [H]).
  479:     
  480: append([H|T],Z) ->
  481: 	[H|append(T,Z)];
  482: append([],X) ->
  483: 	X.
  484: 
  485: system_time() ->    
  486:     {M,S,U} = erlang:now(),
  487:     1000000*(M*1000000 + S) + U.
  488: 
  489: %% ------------------------------------------------------- %%
  490: 
  491: report_result({Res, 0}) ->
  492: %    io:format("DEBUG0 all ~p ~n", [Res]),
  493:     {A_List, I_List} = split_list(Res, [], []),
  494:     A_val = calc_a_val(A_List),
  495:     I_val = calc_i_val(I_List),
  496:     print_report(A_val, I_val),
  497:     ok;
  498: 
  499: report_result({Head, N}) ->
  500:     io:format("Test Failed: Number of internal tmo ~w~n", [N]),
  501:     ?line test_server:fail({Head, N}).
  502: 
  503: split_list([], AL, IL) ->
  504:     {AL, IL};
  505: split_list([{T, {N, Tot, A, Min, Max}} | Rest], AL, IL) ->
  506:     split_list(Rest, AL, [{T, {N, Tot, A, Min, Max}} | IL]);
  507: split_list([Head | Rest], AL, IL) ->
  508:     split_list(Rest, [Head | AL], IL).
  509: 
  510: split([{T, Res} | R]) ->
  511:     split(R, {{T,[Res]}, {T*10,[]}, {T*100,[]}}).
  512: 
  513: split([{T, Res} | R], {{T,S}, M, L}) ->
  514:     split(R, {{T,[Res|S]}, M, L});
  515: 
  516: split([{T, Res} | R], {S, {T,M}, L}) ->
  517:     split(R, {S, {T, [Res|M]}, L});
  518: 
  519: split([{T, Res} | R], {S, M, {T,L}}) ->
  520:     split(R, {S, M, {T, [Res|L]}});
  521: 
  522: split(_Done, Vals) ->
  523:     Vals.
  524: 
  525: calc_a_val(List) ->
  526:     New = lists:sort(List),
  527:     {{T1, S}, {T2, M}, {T3, L}} = split(New),
  528:     S2 = {length(S), lists:max(S), lists:min(S), 
  529: 	  lists:sum(S) div length(S)},
  530:     M2 = {length(M), lists:max(M), lists:min(M), 
  531: 	  lists:sum(M) div length(M)},
  532:     L2 = {length(L), lists:max(L), lists:min(L), 
  533: 	  lists:sum(L) div length(L)},
  534:     [{T1, S2}, {T2, M2}, {T3, L2}].
  535: 
  536: calc_i_val(List) ->
  537:     New =  lists:sort(List),
  538:     {{T1, S}, {T2, M}, {T3, L}} = split(New),
  539:     S2 = get_ivals(S),
  540:     M2 = get_ivals(M),
  541:     L2 = get_ivals(L),
  542:     [{T1, S2}, {T2, M2}, {T3, L2}].
  543: 
  544: get_ivals(List) ->
  545:     Len = length(List),
  546:     Num = element(1, hd(List)), % Number of iterations
  547: 
  548:     LTot = lists:map(fun(X) -> element(2, X) end, List),
  549:     LMin = lists:map(fun(X) -> element(4, X) end, List),
  550:     LMax = lists:map(fun(X) -> element(5, X) end, List),
  551:     
  552:     MaxTot  = lists:max(LTot),
  553:     MinTot  = lists:min(LTot),
  554:     AverTot = lists:sum(LTot) div Len,
  555:     
  556:     IterMax = lists:max(LMax),
  557:     IterMin = lists:min(LMin),
  558:     IterAver= AverTot div Num,
  559: 
  560:     {Len, Num,
  561:      {MaxTot, MinTot, AverTot}, 
  562:      {IterMax, IterMin, IterAver}}.
  563: 
  564: 
  565: print_report(A_L, I_L) ->
  566:     io:format("~nRESULTS from timer test~n~n",[]),
  567:     io:format("Time out times for send_after~n~n", []),
  568:     io:format("Time No of tests  Max    Min  Average~n",[]),
  569:     print_aval(A_L),
  570:     io:format("Time out times for send_interval~n~n", []),
  571:     io:format("Time No.tests  No.intvals TotMax TotMin TotAver  MaxI   MinI  AverI~n", []),
  572:     print_ival(I_L).
  573: 
  574: print_aval([]) ->
  575:     io:format("~n~n", []);
  576: print_aval([{T, {L, Max, Min, Aver}}|R]) ->
  577:     io:format("~5w ~8w ~6w ~6w ~8w ~n", 
  578: 	      [T,L,Max,Min,Aver]),
  579:     print_aval(R).
  580: 
  581: print_ival([]) ->
  582:     io:format("~n", []);
  583: print_ival([{T, {Len, Num, 
  584: 		 {MaxT, MinT, AverT},
  585: 		 {MaxI, MinI, AverI}}}|R]) ->
  586:     io:format("~5w ~6w ~10w ~8w ~6w ~6w ~6w ~6w ~6w~n", 
  587: 	      [T,Len,Num,MaxT,MinT,AverT, MaxI, MinI, AverI]),
  588:     print_ival(R).
  589: 
  590: send(Pid, Msg) ->
  591:     Pid ! Msg.