%% %% %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.