aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/timer_simple_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/timer_simple_SUITE.erl')
-rw-r--r--lib/stdlib/test/timer_simple_SUITE.erl551
1 files changed, 551 insertions, 0 deletions
diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl
new file mode 100644
index 0000000000..021a22c61b
--- /dev/null
+++ b/lib/stdlib/test/timer_simple_SUITE.erl
@@ -0,0 +1,551 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%% Purpose : Test the timer module a simpler/faster test than timer_SUITE
+
+-module(timer_simple_SUITE).
+
+%% external
+-export([all/1,
+ init_per_testcase/2,
+ apply_after/1,
+ send_after1/1,
+ send_after2/1,
+ send_after3/1,
+ exit_after1/1,
+ exit_after2/1,
+ kill_after1/1,
+ kill_after2/1,
+ apply_interval/1,
+ send_interval1/1,
+ send_interval2/1,
+ send_interval3/1,
+ send_interval4/1,
+ cancel1/1,
+ cancel2/1,
+ tc/1,
+ unique_refs/1,
+ timer_perf/1]).
+
+%% internal
+-export([forever/0,
+ do_nrev/2,
+ send/2,
+ timer/4,
+ timer/5]).
+
+-include("test_server.hrl").
+
+-define(MAXREF, (1 bsl 18)).
+-define(REFMARG, 30).
+
+all(doc) -> "Test of the timer module.";
+all(suite) ->
+ [apply_after,
+ send_after1,
+ send_after2,
+ send_after3,
+ exit_after1,
+ exit_after2,
+ kill_after1,
+ kill_after2,
+ apply_interval,
+ send_interval1,
+ send_interval2,
+ send_interval3,
+ send_interval4,
+ cancel1,
+ cancel2,
+ tc,
+ unique_refs,
+ timer_perf].
+
+init_per_testcase(_, Config) when is_list(Config) ->
+ timer:start(),
+ Config.
+
+%% Testing timer interface!!
+
+apply_after(doc) -> "Test of apply_after, with sending of message.";
+apply_after(suite) -> [];
+apply_after(Config) when is_list(Config) ->
+ ?line timer:apply_after(500, ?MODULE, send, [self(), ok_apply]),
+ ?line ok = get_mess(1000, ok_apply).
+
+send_after1(doc) -> "Test of send_after with time = 0.";
+send_after1(suite) -> [];
+send_after1(Config) when is_list(Config) ->
+ ?line timer:send_after(0, ok_send1),
+ ?line ok = get_mess(1000, ok_send1).
+
+send_after2(doc) -> "Test of send_after with time = 500.";
+send_after2(suite) -> [];
+send_after2(Config) when is_list(Config) ->
+ ?line timer:send_after(500, self(), ok_send2),
+ ?line ok = get_mess(2000, ok_send2).
+
+send_after3(doc) -> "Test of send_after with time = 500, with receiver "
+ "a registered process. [OTP-2735]";
+send_after3(suite) -> [];
+send_after3(Config) when is_list(Config) ->
+ ?line Name = list_to_atom(pid_to_list(self())),
+ ?line register(Name, self()),
+ ?line timer:send_after(500, Name, ok_send3),
+ ?line ok = get_mess(2000, ok_send3),
+ ?line unregister(Name).
+
+exit_after1(doc) -> "Test of exit_after with time = 1000.";
+exit_after1(suite) -> [];
+exit_after1(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Pid = spawn_link(?MODULE, forever, []),
+ ?line timer:exit_after(1000, Pid, exit_test1),
+ ?line ok = get_mess(5000, {'EXIT', Pid, exit_test1}).
+
+exit_after2(doc) -> "Test of exit_after with time = 1000. The process to "
+ "exit is the name of a registered process. "
+ "[OTP-2735]";
+exit_after2(suite) -> [];
+exit_after2(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Pid = spawn_link(?MODULE, forever, []),
+ ?line Name = list_to_atom(pid_to_list(Pid)),
+ ?line register(Name, Pid),
+ ?line timer:exit_after(1000, Name, exit_test2),
+ ?line ok = get_mess(2000, {'EXIT', Pid, exit_test2}).
+
+kill_after1(doc) -> "Test of kill_after with time = 1000.";
+kill_after1(suite) -> [];
+kill_after1(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Pid = spawn_link(?MODULE, forever, []),
+ ?line timer:kill_after(1000, Pid),
+ ?line ok = get_mess(2000, {'EXIT', Pid, killed}).
+
+kill_after2(doc) -> "Test of kill_after with time = 1000. The process to "
+ "exit is the name of a registered process. "
+ "[OTP-2735]";
+kill_after2(suite) -> [];
+kill_after2(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Pid = spawn_link(?MODULE, forever, []),
+ ?line Name = list_to_atom(pid_to_list(Pid)),
+ ?line register(Name, Pid),
+ ?line timer:kill_after(1000, Name),
+ ?line ok = get_mess(2000, {'EXIT', Pid, killed}).
+
+apply_interval(doc) -> "Test of apply_interval by sending messages. Receive "
+ "3 messages, cancel the timer, and check that we do "
+ "not get any more messages.";
+apply_interval(suite) -> [];
+apply_interval(Config) when is_list(Config) ->
+ ?line {ok, Ref} = timer:apply_interval(1000, ?MODULE, send,
+ [self(), apply_int]),
+ ?line ok = get_mess(1500, apply_int, 3),
+ ?line timer:cancel(Ref),
+ ?line nor = get_mess(1000, apply_int).
+
+send_interval1(doc) -> "Test of send_interval/2. Receive 5 messages, cancel "
+ "the timer, and check that we do not get any more "
+ "messages.";
+send_interval1(suite) -> [];
+send_interval1(Config) when is_list(Config) ->
+ {ok, Ref} = timer:send_interval(1000, send_int),
+ ?line ok = get_mess(1500, send_int, 5),
+ timer:cancel(Ref),
+ ?line nor = get_mess(1000, send_int). % We should receive only five
+
+send_interval2(doc) -> "Test of send_interval/3. Receive 2 messages, cancel "
+ "the timer, and check that we do not get any more "
+ "messages.";
+send_interval2(suite) -> [];
+send_interval2(Config) when is_list(Config) ->
+ {ok, Ref} = timer:send_interval(1000, self(), send_int2),
+ ?line ok = get_mess(1500, send_int2, 2),
+ timer:cancel(Ref),
+ ?line nor = get_mess(1000, send_int2). % We should receive only two
+
+send_interval3(doc) -> "Test of send_interval/3. Receive 2 messages, cancel "
+ "the timer, and check that we do not get any more "
+ "messages. The receiver is the name of a registered "
+ "process. [OTP-2735]";
+send_interval3(suite) -> [];
+send_interval3(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Name = list_to_atom(pid_to_list(self())),
+ ?line register(Name, self()),
+ ?line {ok, Ref} = timer:send_interval(1000, Name, send_int3),
+ ?line ok = get_mess(1500, send_int3, 2),
+ timer:cancel(Ref),
+ ?line nor = get_mess(1000, send_int3), % We should receive only two
+ ?line unregister(Name).
+
+send_interval4(doc) -> "Test that send interval stops sending msg when the "
+ "receiving process terminates.";
+send_interval4(suite) -> [];
+send_interval4(Config) when is_list(Config) ->
+ ?line timer:send_interval(500, one_time_only),
+ receive
+ one_time_only -> ok
+ end,
+ ?line timer_server ! {'EXIT', self(), normal}, % Should remove the timer
+ ?line timer:send_after(600, send_intv_ok),
+ ?line send_intv_ok = receive
+ Msg -> Msg
+ end.
+
+cancel1(doc) -> "Test that we can cancel a timer.";
+cancel1(suite) -> [];
+cancel1(Config) when is_list(Config) ->
+ ?line {ok, Ref} = timer:send_after(1000, this_should_be_canceled),
+ ?line timer:cancel(Ref),
+ ?line nor = get_mess(2000, this_should_be_canceled). % We should rec 0 msgs
+
+cancel2(doc) -> "Test cancel/1 with bad argument.";
+cancel2(suite) -> [];
+cancel2(Config) when is_list(Config) ->
+ ?line {error, badarg} = timer:cancel(no_reference).
+
+tc(doc) -> "Test sleep/1 and tc/3.";
+tc(suite) -> [];
+tc(Config) when is_list(Config) ->
+ % This should both sleep and tc
+ ?line {Res, ok} = timer:tc(timer, sleep, [500]),
+ ?line ok = if
+ Res < 500*1000 -> {too_early, Res}; % Too early
+ Res > 800*1000 -> {too_late, Res}; % Too much time
+ true -> ok
+ end,
+
+ ?line Sec = timer:seconds(4),
+ ?line Min = timer:minutes(4),
+ ?line Hour = timer:hours(4),
+ ?line MyRes = 4*1000 + 4*60*1000 + 4*60*60*1000,
+ ?line if MyRes == Sec + Min + Hour -> ok end,
+ ?line TimerRes = timer:hms(4,4,4),
+ ?line if MyRes == TimerRes -> ok end,
+ ok.
+
+unique_refs(doc) ->
+ "Tests that cancellations of one-shot timers do not accidentally "
+ "cancel interval timers [OTP-2771].";
+unique_refs(suite) ->
+ [];
+unique_refs(Config) when is_list(Config) ->
+ ?line ITimers = repeat_send_interval(10), % 10 interval timers
+ ?line eat_refs(?MAXREF - ?REFMARG),
+ ?line set_and_cancel_one_shots(?REFMARG),
+ ?line NumLeft = num_timers(),
+ ?line io:format("~w timers left, should be 10\n", [NumLeft]),
+ ?line cancel(ITimers),
+ ?line receive_nisse(),
+ ?line 10 = NumLeft.
+
+
+repeat_send_interval(0) ->
+ [];
+repeat_send_interval(M) ->
+ ?line {ok, Ref} = timer:send_interval(6000,self(), nisse),
+ ?line [Ref| repeat_send_interval(M - 1)].
+
+eat_refs(0) ->
+ 0;
+eat_refs(N) ->
+ _ = make_ref(),
+ eat_refs(N-1).
+
+set_and_cancel_one_shots(0) ->
+ 0;
+set_and_cancel_one_shots(N) ->
+ {ok, Ref} = timer:send_after(7000, self(), kalle),
+ %% Cancel twice
+ timer:cancel(Ref),
+ timer:cancel(Ref),
+ set_and_cancel_one_shots(N-1).
+
+cancel([T| Ts]) ->
+ ?line timer:cancel(T),
+ ?line cancel(Ts);
+cancel([]) ->
+ ok.
+
+num_timers() ->
+ {{_, TotalTimers},{_, _IntervalTimers}} = timer:get_status(),
+ TotalTimers.
+
+receive_nisse() ->
+ receive
+ nisse ->
+ receive_nisse()
+ after 0 ->
+ ok
+ end.
+
+
+get_mess(Time, Mess) -> get_mess(Time, Mess, 1).
+get_mess(_, _, 0) -> ok; % Received
+get_mess(Time, Mess, N) ->
+ receive
+ Mess -> get_mess(Time, Mess, N-1)
+ after Time
+ -> nor % Not Received
+ end.
+
+forever() ->
+ timer:sleep(1000),
+ forever().
+
+
+%
+% Testing for performance (on different implementations) of timers
+%
+
+timer_perf(suite) -> [];
+timer_perf(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(10)),
+ Res = performance(timer),
+ ?t:timetrap_cancel(Dog),
+ Res.
+
+performance(Mod) ->
+ process_flag(trap_exit, true),
+ {Y,Mo,D} = date(),
+ {H,M,S} = time(),
+ io:format("Testing module '~p' Date: ~w/~w/~w ~w:~w:~w~n",
+ [Mod,Y,Mo,D,H,M,S]),
+ Result = big_test(Mod),
+ report_result(Result).
+
+big_test(M) ->
+ Load_Pids = start_nrev(20, M), % Increase if more load wanted :)
+
+ apply(M, sleep, [9000]),
+ LPids = spawn_timers(5, M, 10000, 5),
+
+ apply(M, sleep, [4000]),
+ MPids = spawn_timers(10, M, 1000, 6),
+
+ apply(M, sleep, [3500]),
+ SPids = spawn_timers(15, M, 100, 3),
+
+ Res = wait(SPids ++ MPids ++ LPids, [], 0, M),
+
+ lists:foreach(fun(Pid) -> exit(Pid, kill) end, Load_Pids),
+ Res.
+
+wait([], Res, N, _) ->
+ {Res, N};
+wait(Pids, ResList, N, M) ->
+ receive
+ {Pid, ok, Res, T} ->
+ wait(lists:delete(Pid, Pids), [{T, Res} | ResList], N, M);
+ {Pid, Error}->
+ ?line test_server:fail(Error),
+ wait(lists:delete(Pid, Pids), ResList, N+1, M);
+ {'EXIT', Pid, normal} ->
+ wait(lists:delete(Pid, Pids), ResList, N, M);
+ {'EXIT', Pid, Reason} ->
+ ?line test_server:fail({Pid,Reason})
+ end.
+
+spawn_timers(0, _, _, _) ->
+ [];
+spawn_timers(N, M, T, NumIter) ->
+ apply(M, sleep, [120*N]),
+ Pid1 = spawn_link(?MODULE, timer, [apply, M, T, self()]),
+ Pid2 = spawn_link(?MODULE, timer, [interval, M, T, self(), NumIter]),
+ [Pid1, Pid2 | spawn_timers(N-1, M, T, NumIter)].
+
+timer(apply, Mod, T, Pid) ->
+ Before = system_time(),
+ {ok, Ref} = apply(Mod, apply_after, [T, ?MODULE, send, [self(), done]]),
+ receive
+ done ->
+ After = system_time(),
+ Pid ! {self(), ok, (After-Before) div 1000, T}
+ after T*3 + 300 -> % Watch dog
+ io:format("WARNING TIMER WATCHDOG timed out: ~w ~n", [T]),
+ timer:cancel(Ref),
+ Pid ! {self(), watch_dog_timed_out}
+ end.
+
+timer(interval, Mod, T, Pid, NumIter) ->
+ Before = system_time(),
+ {ok, Ref} = apply(Mod, apply_interval, [T, ?MODULE, send, [self(), done]]),
+ timer_irec(Before, T, {0, NumIter}, [], {Pid, Mod, Ref}).
+
+timer_irec(_Start, T, {N, N}, Res, {Pid, Mod, Ref}) ->
+ apply(Mod, cancel, [Ref]),
+ Min = lists:min(Res),
+ Max = lists:max(Res),
+ Tot = lists:sum(Res),
+ Pid ! {self(), ok, {N, Tot, Tot div N, Min, Max}, T};
+timer_irec(Start, T, {N, Max}, Res, {Pid, Mod, Ref}) ->
+ receive
+ done ->
+ Now = system_time(),
+ Elapsed = (Now - (Start + (N*T*1000))) div 1000,
+% io:format("~w Now ~w Started ~w Elap ~w~n", [T,Now,Start,Elapsed]),
+ timer_irec(Start, T,
+ {N+1, Max},
+ [Elapsed | Res],
+ {Pid, Mod, Ref})
+ after T*3 + 300 ->
+ apply(Mod, cancel, [Ref]),
+ io:format("WARNING: TIMER WATCHDOG timed out <Interval>~w~n",[T]),
+ Pid ! {self(), timer_watchdog_timed_out_in_interlval_test}
+ end.
+
+%% ------------------------------------------------------- %%
+%% Small last generator
+
+start_nrev(0, _) ->
+ [];
+
+start_nrev(N, M) ->
+ Pid = spawn_link(?MODULE, do_nrev, [N, M]),
+ [Pid | start_nrev(N-1, M)].
+
+do_nrev(Sleep, Mod) ->
+ apply(Mod, sleep, [50 * Sleep]),
+ test(1000,"abcdefghijklmnopqrstuvxyz1234"),
+ ok.
+
+test(0,_) ->
+ true;
+test(N,L) ->
+ nrev(L),
+ test(N - 1, L).
+
+nrev([]) ->
+ [];
+nrev([H|T]) ->
+ append(nrev(T), [H]).
+
+append([H|T],Z) ->
+ [H|append(T,Z)];
+append([],X) ->
+ X.
+
+system_time() ->
+ {M,S,U} = erlang:now(),
+ 1000000*(M*1000000 + S) + U.
+
+%% ------------------------------------------------------- %%
+
+report_result({Res, 0}) ->
+% io:format("DEBUG0 all ~p ~n", [Res]),
+ {A_List, I_List} = split_list(Res, [], []),
+ A_val = calc_a_val(A_List),
+ I_val = calc_i_val(I_List),
+ print_report(A_val, I_val),
+ ok;
+
+report_result({Head, N}) ->
+ io:format("Test Failed: Number of internal tmo ~w~n", [N]),
+ ?line test_server:fail({Head, N}).
+
+split_list([], AL, IL) ->
+ {AL, IL};
+split_list([{T, {N, Tot, A, Min, Max}} | Rest], AL, IL) ->
+ split_list(Rest, AL, [{T, {N, Tot, A, Min, Max}} | IL]);
+split_list([Head | Rest], AL, IL) ->
+ split_list(Rest, [Head | AL], IL).
+
+split([{T, Res} | R]) ->
+ split(R, {{T,[Res]}, {T*10,[]}, {T*100,[]}}).
+
+split([{T, Res} | R], {{T,S}, M, L}) ->
+ split(R, {{T,[Res|S]}, M, L});
+
+split([{T, Res} | R], {S, {T,M}, L}) ->
+ split(R, {S, {T, [Res|M]}, L});
+
+split([{T, Res} | R], {S, M, {T,L}}) ->
+ split(R, {S, M, {T, [Res|L]}});
+
+split(_Done, Vals) ->
+ Vals.
+
+calc_a_val(List) ->
+ New = lists:sort(List),
+ {{T1, S}, {T2, M}, {T3, L}} = split(New),
+ S2 = {length(S), lists:max(S), lists:min(S),
+ lists:sum(S) div length(S)},
+ M2 = {length(M), lists:max(M), lists:min(M),
+ lists:sum(M) div length(M)},
+ L2 = {length(L), lists:max(L), lists:min(L),
+ lists:sum(L) div length(L)},
+ [{T1, S2}, {T2, M2}, {T3, L2}].
+
+calc_i_val(List) ->
+ New = lists:sort(List),
+ {{T1, S}, {T2, M}, {T3, L}} = split(New),
+ S2 = get_ivals(S),
+ M2 = get_ivals(M),
+ L2 = get_ivals(L),
+ [{T1, S2}, {T2, M2}, {T3, L2}].
+
+get_ivals(List) ->
+ Len = length(List),
+ Num = element(1, hd(List)), % Number of iterations
+
+ LTot = lists:map(fun(X) -> element(2, X) end, List),
+ LMin = lists:map(fun(X) -> element(4, X) end, List),
+ LMax = lists:map(fun(X) -> element(5, X) end, List),
+
+ MaxTot = lists:max(LTot),
+ MinTot = lists:min(LTot),
+ AverTot = lists:sum(LTot) div Len,
+
+ IterMax = lists:max(LMax),
+ IterMin = lists:min(LMin),
+ IterAver= AverTot div Num,
+
+ {Len, Num,
+ {MaxTot, MinTot, AverTot},
+ {IterMax, IterMin, IterAver}}.
+
+
+print_report(A_L, I_L) ->
+ io:format("~nRESULTS from timer test~n~n",[]),
+ io:format("Time out times for send_after~n~n", []),
+ io:format("Time No of tests Max Min Average~n",[]),
+ print_aval(A_L),
+ io:format("Time out times for send_interval~n~n", []),
+ io:format("Time No.tests No.intvals TotMax TotMin TotAver MaxI MinI AverI~n", []),
+ print_ival(I_L).
+
+print_aval([]) ->
+ io:format("~n~n", []);
+print_aval([{T, {L, Max, Min, Aver}}|R]) ->
+ io:format("~5w ~8w ~6w ~6w ~8w ~n",
+ [T,L,Max,Min,Aver]),
+ print_aval(R).
+
+print_ival([]) ->
+ io:format("~n", []);
+print_ival([{T, {Len, Num,
+ {MaxT, MinT, AverT},
+ {MaxI, MinI, AverI}}}|R]) ->
+ io:format("~5w ~6w ~10w ~8w ~6w ~6w ~6w ~6w ~6w~n",
+ [T,Len,Num,MaxT,MinT,AverT, MaxI, MinI, AverI]),
+ print_ival(R).
+
+send(Pid, Msg) ->
+ Pid ! Msg.