%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011. 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%
%%
-module(rb_SUITE).
-include("test_server.hrl").
-compile(export_all).
-define(SUP,rb_SUITE_sup).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
no_group_cases() ++ [{group,running_error_logger}].
no_group_cases() ->
[help,
start_error_stop].
groups() ->
[{running_error_logger,[shuffle],[show,
list,
rescan,
start_stop_log,
grep,
filter_filter,
filter_date,
filter_filter_and_date,
filter_re_no
]}].
all(suite) ->
no_group_cases() ++
[{conf,
install_mf_h,
element(3,lists:keyfind(running_error_logger,1,groups())),
remove_mf_h}
].
init_per_suite(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line RbDir = filename:join(PrivDir,rb),
?line ok = file:make_dir(RbDir),
NewConfig = [{rb_dir,RbDir}|Config],
reset_sasl(NewConfig),
NewConfig.
end_per_suite(_Config) ->
ok.
init_per_group(running_error_logger,Config) ->
install_mf_h(Config).
end_per_group(running_error_logger,Config) ->
remove_mf_h(Config).
init_per_testcase(_Case,Config) ->
case whereis(?SUP) of
undefined -> ok;
Pid -> kill(Pid)
end,
empty_error_logs(Config),
Config.
kill(Pid) ->
Ref = erlang:monitor(process,Pid),
exit(Pid,kill),
receive {'DOWN', Ref, process, Pid, _Info} -> ok end.
end_per_testcase(Case,Config) ->
try apply(?MODULE,Case,[cleanup,Config])
catch error:undef -> ok
end,
ok.
%%%-----------------------------------------------------------------
help() -> help(suite).
help(suite) -> [];
help(_Config) ->
?line Help = capture(fun() -> rb:h() end),
?line "Report Browser Tool - usage" = hd(Help),
?line "rb:stop - stop the rb_server" = lists:last(Help),
ok.
start_error_stop() -> start_error_stop(suite).
start_error_stop(suite) -> [];
start_error_stop(Config) ->
?line RbDir = ?config(rb_dir,Config),
?line {error,{"cannot locate report directory",_}} = rb:start(),
?line ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"),
?line ok = application:set_env(sasl,error_logger_mf_maxbytes,1000),
?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2),
?line restart_sasl(),
?line {error,{"cannot read the index file",_}} = rb:start(),
?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir),
?line restart_sasl(),
?line {ok,_} = rb:start(),
?line ok = rb:stop(),
ok.
%% start_opts(suite) -> [];
%% start_opts(Config) ->
%% PrivDir = ?config(priv_dir,Config),
%% RbDir = filename:join(PrivDir,rb_opts),
%% ok = file:make_dir(RbDir),
install_mf_h(Config) ->
?line RbDir = ?config(rb_dir,Config),
?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir),
?line ok = application:set_env(sasl,error_logger_mf_maxbytes,5000),
?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2),
?line restart_sasl(),
Config.
remove_mf_h(_Config) ->
ok.
show() -> show(suite).
show(suite) -> [];
show(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
?line ok = start_rb(OutFile),
%% Show all reports
?line All = check_report(fun() -> rb:show() end,OutFile),
%% Show by number
?line [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile),
?line {1,First} = lists:keyfind(1,1,All),
%% Show by type
?line [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile),
?line true = contains(CR,"rb_test_crash"),
?line [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile),
?line true = contains(EC,"rb_test_crash"),
?line true = contains(EM,"rb_test_error_msg"),
?line [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile),
?line true = contains(ER,"rb_test_error"),
?line [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile),
?line true = contains(IR,"rb_test_info"),
?line [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile),
?line true = contains(IM,"rb_test_info_msg"),
?line [_|_] = check_report(fun() -> rb:show(progress) end,OutFile),
?line [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end,
OutFile),
?line true = contains(SR,"child_terminated"),
?line true = contains(SR,"{rb_SUITE,rb_test_crash}"),
ok.
list() -> list(suite).
list(suite) -> [];
list(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
?line ok = start_rb(OutFile),
?line All = capture(fun() -> rb:list() end),
?line [{crash_report,[_]=CR},
{error,[_,_]=EM},
{error_report,[_]=ER},
{info_msg,[_]=IM},
{info_report,[_]=IR},
{progress,[_|_]=P},
{supervisor_report,[_]=SR}] = sort_list(All),
?line [{crash_report,CR}] =
sort_list(capture(fun() -> rb:list(crash_report) end)),
?line [{error,EM}] =
sort_list(capture(fun() -> rb:list(error) end)),
?line [{error_report,ER}] =
sort_list(capture(fun() -> rb:list(error_report) end)),
?line [{info_msg,IM}] =
sort_list(capture(fun() -> rb:list(info_msg) end)),
?line [{info_report,IR}] =
sort_list(capture(fun() -> rb:list(info_report) end)),
?line [{progress,P}] =
sort_list(capture(fun() -> rb:list(progress) end)),
?line [{supervisor_report,SR}] =
sort_list(capture(fun() -> rb:list(supervisor_report) end)),
ok.
grep() -> grep(suite).
grep(suite) -> [];
grep(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
?line ok = start_rb(OutFile),
?line [{_,S},
{_,CR},
{_,EC},
{_,IM},
{_,IR},
{_,EM},
{_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile),
?line true = contains(S, "rb_test_crash"),
?line true = contains(CR, "rb_test_crash"),
?line true = contains(EC, "rb_test_crash"),
?line true = contains(IM, "rb_test_info_msg"),
?line true = contains(IR, "rb_test_info"),
?line true = contains(EM, "rb_test_error_msg"),
?line true = contains(ER, "rb_test_error"),
ok.
filter_filter() -> filter_filter(suite).
filter_filter(suite) -> [];
filter_filter(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
?line ok = start_rb(OutFile),
?line All = check_report(fun() -> rb:show() end,OutFile),
?line ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile),
?line [] = rb_filter([{rb_SUITE,rb_test}],OutFile),
?line _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile),
?line AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile),
{_,AllRep} = lists:unzip(All),
{_,ERRep} = lists:unzip(ER),
{_,AllButERRep} = lists:unzip(AllButER),
?line AllButERRep = AllRep -- ERRep,
ok.
filter_date() -> filter_date(suite).
filter_date(suite) -> [];
filter_date(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
Between1 = calendar:local_time(),
timer:sleep(1000),
Between2 = calendar:local_time(),
?line ok = start_rb(OutFile),
?line All = check_report(fun() -> rb:show() end,OutFile),
Before = calendar:gregorian_seconds_to_datetime(
calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10),
After = calendar:gregorian_seconds_to_datetime(
calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1),
?line All = rb_filter([],{Before,from},OutFile),
?line All = rb_filter([],{After,to},OutFile),
?line [] = rb_filter([],{Before,to},OutFile),
?line [] = rb_filter([],{After,from},OutFile),
?line All = rb_filter([],{Before,After},OutFile),
%%?t:format("~p~n",[All]),
?line AllButLast = [{N-1,R} || {N,R} <- tl(All)],
?line AllButLast = rb_filter([],{Before,Between1},OutFile),
?line Last = hd(All),
?line [Last] = rb_filter([],{Between2,After},OutFile),
ok.
filter_filter_and_date() -> filter_filter_and_date(suite).
filter_filter_and_date(suite) -> [];
filter_filter_and_date(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
Between1 = calendar:local_time(),
timer:sleep(1000),
Between2 = calendar:local_time(),
?line error_logger:error_report([{rb_SUITE,rb_test_filter}]),
?line ok = start_rb(OutFile),
Before = calendar:gregorian_seconds_to_datetime(
calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10),
After = calendar:gregorian_seconds_to_datetime(
calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1),
?line All = check_report(fun() -> rb:show() end,OutFile),
?line Last = hd(All),
?line [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile),
?line [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile),
?line [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile),
?line [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile),
?line [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile),
?line [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile),
?line {_,Str} = Last,
?line false = contains(Str,"rb_test_filter"),
ok.
filter_re_no() -> filter_re_no(suite).
filter_re_no(suite) -> [];
filter_re_no(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Insert some reports in the error log and start rb
init_error_logs(),
?line ok = start_rb(OutFile),
?line All = check_report(fun() -> rb:show() end,OutFile),
?line E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile),
?line AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile),
{_,AllRep} = lists:unzip(All),
{_,ERep} = lists:unzip(E),
{_,AllButERep} = lists:unzip(AllButE),
?line AllButERep = AllRep -- ERep,
ok.
rescan() -> rescan(suite).
rescan(suite) -> [];
rescan(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
%% Start rb
?line ok = start_rb(OutFile),
%% Insert one more report and check that the list is longer. Note
%% that there might be two more reports, since the progress report
%% from starting rb_server might not be included before the rescan.
?line AllBefore = capture(fun() -> rb:list() end),
?line error_logger:error_report([{rb_SUITE,rb_test_rescan}]),
?line ok = rb:rescan(),
?line AllAfter = capture(fun() -> rb:list() end),
?line Diff = length(AllAfter) - length(AllBefore),
?line true = (Diff >= 1),
ok.
start_stop_log() -> start_stop_log(suite).
start_stop_log(suite) -> [];
start_stop_log(Config) ->
?line PrivDir = ?config(priv_dir,Config),
?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"),
?line ok = file:write_file(OutFile,[]),
%% Start rb and check that show is printed to standard_io
?line ok = start_rb(),
?line StdioResult = [_|_] = capture(fun() -> rb:show(1) end),
?line {ok,<<>>} = file:read_file(OutFile),
%% Start log and check that show is printed to log and not to standad_io
?line ok = rb:start_log(OutFile),
?line [] = capture(fun() -> rb:show(1) end),
?line {ok,Bin} = file:read_file(OutFile),
?line true = (Bin =/= <<>>),
%% Stop log and check that show is printed to standard_io and not to log
?line ok = rb:stop_log(),
?line ok = file:write_file(OutFile,[]),
?line StdioResult = capture(fun() -> rb:show(1) end),
?line {ok,<<>>} = file:read_file(OutFile),
%% Test that standard_io is used if log file can not be opened
?line ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")),
?line StdioResult = capture(fun() -> rb:show(1) end),
?line {ok,<<>>} = file:read_file(OutFile),
ok.
%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
restart_sasl() ->
application:stop(sasl),
ok = application:start(sasl),
wait_for_sasl().
reset_sasl(Config) ->
application:unset_env(sasl,error_logger_mf_dir),
application:unset_env(sasl,error_logger_mf_maxbytes),
application:unset_env(sasl,error_logger_mf_maxfiles),
empty_error_logs(Config).
empty_error_logs(Config) ->
application:stop(sasl),
catch delete_content(?config(rb_dir, Config)),
ok = application:start(sasl),
wait_for_sasl().
wait_for_sasl() ->
wait_for_sasl(50).
wait_for_sasl(0) ->
?t:fail("sasl application did not start within 5 seconds");
wait_for_sasl(N) ->
case lists:keymember(sasl,1,application:which_applications()) of
true ->
ok;
false ->
timer:sleep(100),
wait_for_sasl(N-1)
end.
start_rb(OutFile) ->
do_start_rb([{start_log,OutFile}]).
start_rb() ->
do_start_rb([]).
do_start_rb(Opts) ->
{ok,Pid} = rb:start(Opts),
%% Wait for process to started, then wait a little bit more
sys:get_status(Pid),
timer:sleep(500),
%% Make sure printouts (e.g. from rb:list(), come to the test log,
%% and that they can be captured.
group_leader(group_leader(),Pid),
ok.
delete_tree(Dir) ->
case filelib:is_dir(Dir) of
true ->
delete_content(Dir),
file:del_dir(Dir);
false ->
ok = file:delete(Dir)
end.
delete_content(Dir) ->
{ok,Files} = file:list_dir(Dir),
lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end,
Files).
init_error_logs() ->
?line error_logger:error_report([{rb_SUITE,rb_test_error}]),
?line error_logger:error_msg("rb_test_error_msg"),
?line error_logger:info_report([{rb_SUITE,rb_test_info}]),
?line error_logger:info_msg("rb_test_info_msg"),
?line _Pid = start(),
?line Ref = erlang:monitor(process,?MODULE),
?line gen_server:cast(?MODULE,crash),
?line receive {'DOWN',Ref,process,_,{rb_SUITE,rb_test_crash}} -> ok
after 2000 ->
?t:format("Got: ~p~n",[process_info(self(),messages)]),
?t:fail("rb_SUITE server never died")
end,
?line erlang:demonitor(Ref),
?line wait_for_server(),
ok.
wait_for_server() ->
case whereis(?MODULE) of
undefined ->
wait_for_server();
Pid ->
timer:sleep(100), % allow the supervisor report to be written
Pid
end.
capture(Fun) ->
?t:capture_start(),
ok = Fun(),
timer:sleep(1000),
?t:capture_stop(),
string:tokens(lists:append(?t:capture_get()),"\n").
rb_filter(Filter,OutFile) ->
check_report(fun() -> rb:filter(Filter) end, OutFile).
rb_filter(Filter,Dates,OutFile) ->
check_report(fun() -> rb:filter(Filter,Dates) end, OutFile).
%% This function first empties the given report file, then executes
%% the fun and returns a list of {N,Report}, where Report is a report
%% read from the file and N is an integer. The newest report has the
%% lowest number.
%% If the fun was a call to rb:show() (i.e. with no arguments), then
%% the numbering (N) will be the same as rb's own numbering (as shown
%% by rb:list()).
check_report(Fun,File) ->
file:delete(File),
rb:rescan([{start_log,File}]),
ok = Fun(),
{ok,Bin} = file:read_file(File),
Reports = split_reports(binary_to_list(Bin),[],[]),
lists:zip(lists:seq(1,length(Reports)),Reports).
-define(report_header_line,"\n===============================================================================\n").
split_reports([],Report,Reports) ->
add_report(Report,Reports);
split_reports(Text,Report,Reports) ->
case Text of
?report_header_line++Rest ->
{Heading,PrevReport} = lists:splitwith(fun($\n) -> false;
(_) -> true
end,
Report),
split_reports(Rest,
?report_header_line++Heading,
add_report(PrevReport,Reports));
[Ch|Rest] ->
split_reports(Rest,[Ch|Report],Reports)
end.
add_report(Report,Reports) ->
case string:strip(Report,both,$\n) of
[] -> Reports;
Report1 -> [lists:reverse(Report1)|Reports]
end.
%% Returns true if Substr is a substring of Str.
contains(Str,Substr) ->
0 =/= string:str(Str,Substr).
%% Sort the result of rb_list after report type
sort_list(List) ->
sort_list(List,dict:new()).
sort_list([H|T],D) ->
case re:run(H,"\s+[0-9]+\s+([a-z_]+)",[{capture,all_but_first,list}]) of
nomatch ->
sort_list(T,D);
{match,[TypeStr]} ->
sort_list(T,dict:append(list_to_atom(TypeStr),H,D))
end;
sort_list([],D) ->
lists:sort(dict:to_list(D)).
%%%-----------------------------------------------------------------
%%% A dummy supervisor and gen_server used for creating crash- and
%%% supervisor reports
start() ->
{ok,Pid} =
supervisor:start_link({local, ?SUP}, ?MODULE, i_am_supervisor),
unlink(Pid),
Pid.
start_server() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, i_am_server, []).
init(i_am_server) ->
{ok, state};
init(i_am_supervisor) ->
AChild = {?SUP,{?MODULE,start_server,[]},
permanent,2000,worker,[?MODULE]},
{ok,{{one_for_all,1,1}, [AChild]}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(crash, State) ->
exit({rb_SUITE,rb_test_crash}),
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.