aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src/ct_repeat.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/ct_repeat.erl')
-rw-r--r--lib/common_test/src/ct_repeat.erl263
1 files changed, 263 insertions, 0 deletions
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
new file mode 100644
index 0000000000..7ac6e045d7
--- /dev/null
+++ b/lib/common_test/src/ct_repeat.erl
@@ -0,0 +1,263 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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%
+%%
+
+%%% @doc Common Test Framework module that handles repeated test runs
+%%%
+%%% <p>This module exports functions for repeating tests. The following
+%%% script flags (or equivalent ct:run_test/1 options) are supported:
+%%% -until <StopTime>, StopTime = YYMoMoDDHHMMSS | HHMMSS
+%%% -duration <DurTime>, DurTime = HHMMSS
+%%% -force_stop
+%%% -repeat <N>, N = integer()</p>
+
+-module(ct_repeat).
+
+%% Script interface
+-export([loop_test/2]).
+-export([log_loop_info/1]).
+
+%%----------------------------------------------------------
+%% Flags:
+%%----------------------------------------------------------
+
+loop_test(If,Args) when is_list(Args) ->
+ {ok,Cwd} = file:get_cwd(),
+ case get_loop_info(Args) of
+ no_loop ->
+ false;
+ {error,E} ->
+ io:format("Common Test error: ~p\n\n",[E]),
+ file:set_cwd(Cwd),
+ E;
+ {repeat,N} ->
+ io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]),
+ Args1 = [{loop_info,[{repeat,1,N}]} | Args],
+ loop(If,repeat,0,N,undefined,Args1,undefined),
+ file:set_cwd(Cwd);
+ {stop_time,StopTime} ->
+ case remaining_time(StopTime) of
+ 0 ->
+ io:format("\nCommon Test: No time left to run tests.\n\n",[]),
+ ok;
+ Secs ->
+ io:format("\nCommon Test: Will repeat tests for ~s.\n\n",
+ [ts(Secs)]),
+ TPid =
+ case lists:keymember(force_stop,1,Args) of
+ true ->
+ CtrlPid = self(),
+ spawn(fun() -> stop_after(CtrlPid,Secs) end);
+ false ->
+ undefined
+ end,
+ Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args],
+ loop(If,stop_time,0,Secs,StopTime,Args1,TPid)
+ end,
+ file:set_cwd(Cwd)
+ end.
+
+loop(_,repeat,N,N,_,_Args,_) ->
+ ok;
+
+loop(If,Type,N,Data0,Data1,Args,TPid) ->
+ Pid = spawn_tester(If,self(),Args),
+ receive
+ {'EXIT',Pid,Reason} ->
+ io:format("Test run crashed! This could be an internal error "
+ "- please report!\n\n"
+ "~p\n\n",[Reason]),
+ cancel(TPid),
+ {error,Reason};
+ {Pid,{error,Reason}} ->
+ io:format("\nTest run failed!\nReason: ~p\n\n",[Reason]),
+ cancel(TPid),
+ {error,Reason};
+ {Pid,Result} ->
+ if Type == repeat ->
+ io:format("\nTest run ~w(~w) complete.\n\n",[N+1,Data0]),
+ lists:keydelete(loop_info,1,Args),
+ Args1 = [{loop_info,[{repeat,N+2,Data0}]} | Args],
+ loop(If,repeat,N+1,Data0,Data1,Args1,TPid);
+ Type == stop_time ->
+ case remaining_time(Data1) of
+ 0 ->
+ io:format("\nTest time (~s) has run out.\n\n",[ts(Data0)]),
+ cancel(TPid),
+ Result;
+ Secs ->
+ io:format("\n~s of test time remaining, "
+ "starting run #~w...\n\n",[ts(Secs),N+2]),
+ lists:keydelete(loop_info,1,Args),
+ ST = {stop_time,Data0,Data1,N+2},
+ Args1 = [{loop_info,[ST]} | Args],
+ loop(If,stop_time,N+1,Data0,Data1,Args1,TPid)
+ end
+ end
+ end.
+
+spawn_tester(script,Ctrl,Args) ->
+ spawn_link(fun() -> ct_run:script_start1(Ctrl,Args) end);
+
+spawn_tester(func,Ctrl,Opts) ->
+ Tester = fun() ->
+ case catch ct_run:run_test1(Opts) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ Result ->
+ Ctrl ! {self(),Result}
+ end
+ end,
+ spawn_link(Tester).
+
+remaining_time(StopTime) ->
+ Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ Diff = StopTime - Now,
+ if Diff > 0 ->
+ Diff;
+ true ->
+ 0
+ end.
+
+get_loop_info(Args) when is_list(Args) ->
+ case lists:keysearch(until,1,Args) of
+ {value,{until,Time}} ->
+ Time1 = delistify(Time),
+ case catch get_stop_time(until,Time1) of
+ {'EXIT',_} ->
+ {error,{bad_time_format,Time1}};
+ Stop ->
+ {stop_time,Stop}
+ end;
+ false ->
+ case lists:keysearch(duration,1,Args) of
+ {value,{duration,Time}} ->
+ Time1 = delistify(Time),
+ case catch get_stop_time(duration,Time1) of
+ {'EXIT',_} ->
+ {error,{bad_time_format,Time1}};
+ Stop ->
+ {stop_time,Stop}
+ end;
+ false ->
+ case lists:keysearch(repeat,1,Args) of
+ {value,{repeat,R}} ->
+ case R of
+ N when is_integer(N), N>0 ->
+ {repeat,N};
+ [Str] ->
+ case catch list_to_integer(Str) of
+ N when is_integer(N), N>0 ->
+ {repeat,N};
+ _ ->
+ {error,{invalid_repeat_value,Str}}
+ end;
+ _ ->
+ {error,{invalid_repeat_value,R}}
+ end;
+ false ->
+ no_loop
+ end
+ end
+ end.
+
+get_stop_time(until,[Y1,Y2,Mo1,Mo2,D1,D2,H1,H2,Mi1,Mi2,S1,S2]) ->
+ Date =
+ case [Mo1,Mo2] of
+ "00" ->
+ date();
+ _ ->
+ Y = list_to_integer([Y1,Y2]),
+ Mo = list_to_integer([Mo1,Mo2]),
+ D = list_to_integer([D1,D2]),
+ {YNow,_,_} = date(),
+ Dec = trunc(YNow/100),
+ Year =
+ if Y < (YNow-Dec*100) -> (Dec+1)*100 + Y;
+ true -> Dec*100 + Y
+ end,
+ {Year,Mo,D}
+ end,
+ Time = {list_to_integer([H1,H2]),
+ list_to_integer([Mi1,Mi2]),
+ list_to_integer([S1,S2])},
+ calendar:datetime_to_gregorian_seconds({Date,Time});
+
+get_stop_time(until,Time) ->
+ get_stop_time(until,"000000"++Time);
+
+get_stop_time(duration,[H1,H2,Mi1,Mi2,S1,S2]) ->
+ Secs =
+ list_to_integer([H1,H2]) * 3600 +
+ list_to_integer([Mi1,Mi2]) * 60 +
+ list_to_integer([S1,S2]),
+ calendar:datetime_to_gregorian_seconds(calendar:local_time()) + Secs.
+
+cancel(Pid) ->
+ catch exit(Pid,kill).
+
+%% After Secs, abort will make the test_server finish the current
+%% job, then empty the job queue and stop.
+stop_after(_CtrlPid,Secs) ->
+ timer:sleep(Secs*1000),
+ test_server_ctrl:abort().
+
+%% Callback from ct_run to print loop info to system log.
+log_loop_info(Args) ->
+ case lists:keysearch(loop_info,1,Args) of
+ false ->
+ ok;
+ {value,{_,[{repeat,C,N}]}} ->
+ ct_logs:log("Test loop info","Test run ~w of ~w",[C,N]);
+ {value,{_,[{stop_time,Secs0,StopTime,N}]}} ->
+ LogStr1 =
+ case lists:keysearch(duration,1,Args) of
+ {value,{_,Dur}} ->
+ io_lib:format("Specified test duration: ~s (~w secs)\n",
+ [delistify(Dur),Secs0]);
+ _ ->
+ case lists:keysearch(until,1,Args) of
+ {value,{_,Until}} ->
+ io_lib:format("Specified end time: ~s (duration ~w secs)\n",
+ [delistify(Until),Secs0]);
+ _ ->
+ ok
+ end
+ end,
+ LogStr2 = io_lib:format("Test run #~w\n", [N]),
+ Secs = remaining_time(StopTime),
+ LogStr3 =
+ io_lib:format("Test time remaining: ~w secs (~w%)\n",
+ [Secs,trunc((Secs/Secs0)*100)]),
+ LogStr4 =
+ case lists:keymember(force_stop,1,Args) of
+ true ->
+ io_lib:format("force_stop is enabled",[]);
+ _ ->
+ ""
+ end,
+ ct_logs:log("Test loop info",LogStr1++LogStr2++LogStr3++LogStr4,[])
+ end.
+
+ts(Secs) ->
+ integer_to_list(Secs) ++ " secs".
+
+delistify([X]) ->
+ X;
+delistify(X) ->
+ X.