%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2011-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(rb_SUITE). -include_lib("common_test/include/ct.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, show_other, list, rescan, start_stop_log, grep, filter_filter, filter_date, filter_filter_and_date, filter_re_no ]}]. init_per_suite(Config) -> PrivDir = ?config(priv_dir,Config), RbDir = filename:join(PrivDir,rb), 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 log_mf_h RbDir = ?config(rb_dir,Config), ok = application:set_env(sasl,error_logger_mf_dir,RbDir), ok = application:set_env(sasl,error_logger_mf_maxbytes,5000), ok = application:set_env(sasl,error_logger_mf_maxfiles,2), restart_sasl(), Config. end_per_group(running_error_logger,_Config) -> %% Remove log_mf_h??? ok. init_per_testcase(_Case,Config) -> case whereis(?SUP) of undefined -> ok; Sup -> Server = whereis(?MODULE), exit(Sup,kill), wait_for_down([Server,Sup]) end, empty_error_logs(Config), Config. wait_for_down([]) -> ok; wait_for_down([undefined|Rest]) -> wait_for_down(Rest); wait_for_down([Pid|Rest]) -> Ref = erlang:monitor(process,Pid), receive {'DOWN', Ref, process, Pid, _Info} -> ok end, wait_for_down(Rest). end_per_testcase(Case,Config) -> try apply(?MODULE,Case,[cleanup,Config]) catch error:undef -> ok end, ok. %%%----------------------------------------------------------------- %%% Test cases help(_Config) -> Help = capture(fun() -> rb:h() end), %% Check that first and last line is there true = lists:member("Report Browser Tool - usage", Help), true = lists:member("rb:stop - stop the rb_server", Help), ok. %% Test that all three sasl env vars must be set for a successful start of rb %% Then stop rb. start_error_stop(Config) -> RbDir = ?config(rb_dir,Config), {error,{"cannot locate report directory",_}} = rb:start(), ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"), ok = application:set_env(sasl,error_logger_mf_maxbytes,1000), ok = application:set_env(sasl,error_logger_mf_maxfiles,2), restart_sasl(), {error,{"cannot read the index file",_}} = rb:start(), ok = application:set_env(sasl,error_logger_mf_dir,RbDir), restart_sasl(), {ok,_} = rb:start(), ok = rb:stop(), ok. show(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb init_error_logs(), ok = start_rb(OutFile), %% Show all reports All = check_report(fun() -> rb:show() end,OutFile), %% Show by number [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile), {1,First} = lists:keyfind(1,1,All), %% Show by type [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile), true = contains(CR,"rb_test_crash"), [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile), true = contains(EC,"rb_test_crash"), true = contains(EM,"rb_test_error_msg"), [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile), true = contains(ER,"rb_test_error"), [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile), true = contains(IR,"rb_test_info"), [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile), true = contains(IM,"rb_test_info_msg"), [_|_] = check_report(fun() -> rb:show(progress) end,OutFile), [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end, OutFile), true = contains(SR,"child_terminated"), true = contains(SR,"{rb_SUITE,rb_test_crash}"), ok. show_other(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb error_logger:info_report([rb_test_term_in_list]), error_logger:info_report(rb_test_term_no_list), ok = start_rb(OutFile), %% Show by type and check content [{_,I1},{_,I2}] = check_report(fun() -> rb:show(info_report) end,OutFile), true = contains(I1,"rb_test_term_no_list"), true = contains(I2,"rb_test_term_in_list"), ok. list(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb init_error_logs(), ok = start_rb(OutFile), All = capture(fun() -> rb:list() end), [{crash_report,[_]=CR}, {error,[_,_]=EM}, {error_report,[_]=ER}, {info_msg,[_]=IM}, {info_report,[_]=IR}, {progress,[_|_]=P}, {supervisor_report,[_]=SR}] = sort_list(All), [{crash_report,CR}] = sort_list(capture(fun() -> rb:list(crash_report) end)), [{error,EM}] = sort_list(capture(fun() -> rb:list(error) end)), [{error_report,ER}] = sort_list(capture(fun() -> rb:list(error_report) end)), [{info_msg,IM}] = sort_list(capture(fun() -> rb:list(info_msg) end)), [{info_report,IR}] = sort_list(capture(fun() -> rb:list(info_report) end)), [{progress,P}] = sort_list(capture(fun() -> rb:list(progress) end)), [{supervisor_report,SR}] = sort_list(capture(fun() -> rb:list(supervisor_report) end)), ok. grep(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb init_error_logs(), ok = start_rb(OutFile), [{_,S}, {_,CR}, {_,EC}, {_,IM}, {_,IR}, {_,EM}, {_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile), true = contains(S, "rb_test_crash"), true = contains(CR, "rb_test_crash"), true = contains(EC, "rb_test_crash"), true = contains(IM, "rb_test_info_msg"), true = contains(IR, "rb_test_info"), true = contains(EM, "rb_test_error_msg"), true = contains(ER, "rb_test_error"), ok. filter_filter(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb init_error_logs(), ok = start_rb(OutFile), All = check_report(fun() -> rb:show() end,OutFile), ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile), [] = rb_filter([{rb_SUITE,rb_test}],OutFile), _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile), {_,AllRep} = lists:unzip(All), {_,ERRep} = lists:unzip(ER), {_,AllButERRep} = lists:unzip(AllButER), AllButERRep = AllRep -- ERRep, ok. filter_date(Config) -> PrivDir = ?config(priv_dir,Config), 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(), ok = start_rb(OutFile), 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), All = rb_filter([],{Before,from},OutFile), All = rb_filter([],{After,to},OutFile), [] = rb_filter([],{Before,to},OutFile), [] = rb_filter([],{After,from},OutFile), All = rb_filter([],{Before,After},OutFile), %%?t:format("~p~n",[All]), AllButLast = [{N-1,R} || {N,R} <- tl(All)], AllButLast = rb_filter([],{Before,Between1},OutFile), Last = hd(All), [Last] = rb_filter([],{Between2,After},OutFile), ok. filter_filter_and_date(Config) -> PrivDir = ?config(priv_dir,Config), 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(), error_logger:error_report([{rb_SUITE,rb_test_filter}]), 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), All = check_report(fun() -> rb:show() end,OutFile), Last = hd(All), [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile), [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile), [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile), [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile), [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile), [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile), {_,Str} = Last, false = contains(Str,"rb_test_filter"), ok. filter_re_no(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Insert some reports in the error log and start rb init_error_logs(), ok = start_rb(OutFile), All = check_report(fun() -> rb:show() end,OutFile), E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile), {_,AllRep} = lists:unzip(All), {_,ERep} = lists:unzip(E), {_,AllButERep} = lists:unzip(AllButE), AllButERep = AllRep -- ERep, ok. rescan(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), %% Start rb 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. AllBefore = capture(fun() -> rb:list() end), error_logger:error_report([{rb_SUITE,rb_test_rescan}]), ok = rb:rescan(), AllAfter = capture(fun() -> rb:list() end), Diff = length(AllAfter) - length(AllBefore), true = (Diff >= 1), ok. start_stop_log(Config) -> PrivDir = ?config(priv_dir,Config), OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), ok = file:write_file(OutFile,[]), %% Start rb and check that show is printed to standard_io ok = start_rb(), StdioResult = [_|_] = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), %% Start log and check that show is printed to log and not to standard_io ok = rb:start_log(OutFile), [] = capture(fun() -> rb:show(1) end), {ok,Bin} = file:read_file(OutFile), true = (Bin =/= <<>>), %% Start log with atom standard_io and check that show is printed to standard_io ok = rb:stop_log(), ok = file:write_file(OutFile,[]), ok = rb:start_log(standard_io), StdioResult = [_|_] = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), %% Start log and check that show is printed to iodevice log and not to standard_io ok = rb:stop_log(), ok = file:write_file(OutFile,[]), {ok, IoOutFile} = file:open(OutFile,[write]), ok = rb:start_log(IoOutFile), [] = capture(fun() -> rb:show(1) end), {ok,Bin} = file:read_file(OutFile), true = (Bin =/= <<>>), ok = file:close(IoOutFile), %% Stop log and check that show is printed to standard_io and not to log ok = rb:stop_log(), ok = file:write_file(OutFile,[]), StdioResult = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), %% Start log and check that list is printed to log and not to standard_io ok = file:write_file(OutFile,[]), ok = rb:start_log(OutFile), [] = capture(fun() -> rb:log_list() end), {ok,Bin2} = file:read_file(OutFile), true = (Bin2 =/= <<>>), %% Stop log and check that list is printed to standard_io and not to log ok = rb:stop_log(), ok = file:write_file(OutFile,[]), StdioResult2 = capture(fun() -> rb:log_list() end), {ok,<<>>} = file:read_file(OutFile), %% Test that standard_io is used if log file cannot be opened ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), StdioResult = capture(fun() -> rb:show(1) end), {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() -> error_logger:error_report([{rb_SUITE,rb_test_error}]), error_logger:error_msg("rb_test_error_msg"), error_logger:info_report([{rb_SUITE,rb_test_info}]), error_logger:info_msg("rb_test_info_msg"), _Pid = start(), Ref = erlang:monitor(process,?MODULE), gen_server:cast(?MODULE,crash), 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, erlang:demonitor(Ref), 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.