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