aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/test')
-rw-r--r--lib/observer/test/crashdump_helper.erl4
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl109
-rw-r--r--lib/observer/test/observer_SUITE.erl248
3 files changed, 319 insertions, 42 deletions
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index 40dbe28d46..0eb4a92c53 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -35,7 +35,9 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
register(aaaaaaaa,self()),
process_flag(save_calls,3),
ets:new(cdv_test_ordset_table,[ordered_set]),
- erlang:send_after(1000000,self(),cdv_test_timer_message),
+ erlang:send_after(1000000,self(),cdv_test_timer_message1),
+ erlang:send_after(1000000,aaaaaaaa,cdv_test_timer_message2),
+ erlang:send_after(1000000,noexistproc,cdv_test_timer_message3),
Port = hd(erlang:ports()),
Fun = fun() -> ok end,
Ref = make_ref(),
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 7a582436b4..03ab0c20e1 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -120,27 +120,63 @@ start_stop(Config) when is_list(Config) ->
Dump = hd(?config(dumps,Config)),
timer:sleep(1000),
- ProcsBefore = length(processes()),
+ ProcsBefore = processes(),
+ NumProcsBefore = length(ProcsBefore),
ok = crashdump_viewer:start(Dump),
- true = is_pid(whereis(crashdump_viewer_server)),
- true = is_pid(whereis(cdv_wx)),
- true = is_pid(whereis(cdv_proc_cb)),
- true = is_pid(whereis(cdv_port_cb)),
- true = is_pid(whereis(cdv_ets_cb)),
- true = is_pid(whereis(cdv_timer_cb)),
- true = is_pid(whereis(cdv_fun_cb)),
- true = is_pid(whereis(cdv_atom_cb)),
- true = is_pid(whereis(cdv_dist_cb)),
- true = is_pid(whereis(cdv_mod_cb)),
+ ExpectedRegistered = [crashdump_viewer_server,
+ cdv_wx,
+ cdv_proc_cb,
+ cdv_proc_cb__holder,
+ cdv_port_cb,
+ cdv_port_cb__holder,
+ cdv_ets_cb,
+ cdv_ets_cb__holder,
+ cdv_timer_cb,
+ cdv_timer_cb__holder,
+ cdv_fun_cb,
+ cdv_fun_cb__holder,
+ cdv_atom_cb,
+ cdv_atom_cb__holder,
+ cdv_dist_cb,
+ cdv_dist_cb__holder,
+ cdv_mod_cb,
+ cdv_mod_cb__holder],
+ Regs=[begin
+ P=whereis(N),
+ {P,N,erlang:monitor(process,P)}
+ end || N <- ExpectedRegistered],
+ ct:log("CDV procs: ~n~p~n",[Regs]),
+ [true=is_pid(P) || {P,_,_} <- Regs],
timer:sleep(5000), % give some time to live
ok = crashdump_viewer:stop(),
- timer:sleep(1000), % give some time to stop
- undefined = whereis(crashdump_viewer_server),
- undefined = whereis(cdv_wx),
- ProcsAfter=length(processes()),
- ProcsAfter=ProcsBefore,
+ recv_downs(Regs),
+ timer:sleep(2000),
+ ProcsAfter = processes(),
+ NumProcsAfter = length(ProcsAfter),
+ if NumProcsAfter=/=NumProcsBefore ->
+ ct:log("Before but not after:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsBefore -- ProcsAfter]]),
+ ct:log("After but not before:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
+ ct:fail("leaking processes");
+ true ->
+ ok
+ end,
ok.
+recv_downs([]) ->
+ ok;
+recv_downs(Regs) ->
+ receive
+ {'DOWN',Ref,process,_Pid,_} ->
+ ct:log("Got 'DOWN' for process ~n~p~n",[_Pid]),
+ recv_downs(lists:keydelete(Ref,3,Regs))
+ after 30000 ->
+ ct:log("Timeout waiting for down:~n~p~n",
+ [[{Reg,process_info(P)} || {P,_,_}=Reg <- Regs]]),
+ ct:log("Message queue:~n~p~n",[process_info(self(),messages)])
+ end.
+
%% Try to load nonexisting file
non_existing(Config) when is_list(Config) ->
ExpectedReason = "non-existing-file is not an Erlang crash dump\n",
@@ -265,10 +301,12 @@ wait_for_progress_done() ->
%%%-----------------------------------------------------------------
%%% General check of what is displayed for a dump
browse_file(File) ->
- io:format("Browsing file: ~s~n",[File]),
+ io:format("~nBrowsing file: ~s",[File]),
ok = start_backend(File),
+ io:format(" backend started",[]),
+
{ok,_GI=#general_info{},_GenTW} = crashdump_viewer:general_info(),
{ok,Procs,_ProcsTW} = crashdump_viewer:processes(),
{ok,Ports,_PortsTW} = crashdump_viewer:ports(),
@@ -285,10 +323,16 @@ browse_file(File) ->
{ok,_HashTabs,_HashTabsTW} = crashdump_viewer:hash_tables(),
{ok,_IndexTabs,_IndexTabsTW} = crashdump_viewer:index_tables(),
+ io:format(" info read",[]),
+
lookat_all_pids(Procs),
+ io:format(" pids ok",[]),
lookat_all_ports(Ports),
+ io:format(" ports ok",[]),
lookat_all_mods(Mods),
+ io:format(" mods ok",[]),
lookat_all_nodes(Nodes),
+ io:format(" nodes ok",[]),
Procs. % used as second arg to special/2
@@ -303,6 +347,7 @@ special(File,Procs) ->
[#proc{pid=Pid0}|_Rest] = lists:keysort(#proc.name,Procs),
Pid = pid_to_list(Pid0),
{ok,ProcDetails=#proc{},[]} = crashdump_viewer:proc_details(Pid),
+ io:format(" process details ok",[]),
#proc{dict=Dict} = ProcDetails,
@@ -314,6 +359,7 @@ special(File,Procs) ->
['#CDVBin',SOffset,SSize,SPos] = proplists:get_value(sub_bin,Dict),
{ok,<<_:SSize/binary>>} =
crashdump_viewer:expand_binary({SOffset,SSize,SPos}),
+ io:format(" expand binary ok",[]),
['#CDVPid',X1,Y1,Z1] = proplists:get_value(ext_pid,Dict),
ChannelStr1 = integer_to_list(X1),
@@ -323,22 +369,34 @@ special(File,Procs) ->
integer_to_list(Z1) ++ ">",
{error,{other_node,ChannelStr1}} =
crashdump_viewer:proc_details(ExtPid),
+ io:format(" process details external ok",[]),
['#CDVPort',X2,Y2] = proplists:get_value(port,Dict),
ChannelStr2 = integer_to_list(X2),
Port = "#Port<"++ChannelStr2++"."++integer_to_list(Y2)++">",
{ok,_PortDetails=#port{},[]} = crashdump_viewer:port(Port),
+ io:format(" port details ok",[]),
['#CDVPort',X3,Y3] = proplists:get_value(ext_port,Dict),
ChannelStr3 = integer_to_list(X3),
ExtPort = "#Port<"++ChannelStr3++"."++integer_to_list(Y3)++">",
{error,{other_node,ChannelStr3}} = crashdump_viewer:port(ExtPort),
+ io:format(" port details external ok",[]),
{ok,[_Ets=#ets_table{}],[]} = crashdump_viewer:ets_tables(Pid),
- {ok,[_Timer=#timer{}],[]} = crashdump_viewer:timers(Pid),
+ io:format(" ets tables ok",[]),
+
+ {ok,[#timer{pid=Pid0,name=undefined},
+ #timer{pid=Pid0,name="aaaaaaaa"}],[]} =
+ crashdump_viewer:timers(Pid),
+ {ok,AllTimers,_TimersTW} = crashdump_viewer:timers(all),
+ #timer{name="noexistproc"} =
+ lists:keyfind(undefined,#timer.pid,AllTimers),
+ io:format(" timers ok:",[]),
{ok,Mod1=#loaded_mod{},[]} =
crashdump_viewer:loaded_mod_details(atom_to_list(?helper_mod)),
+ io:format(" modules ok",[]),
#loaded_mod{current_size=CS, old_size=OS,
old_attrib=A,old_comp_info=C}=Mod1,
true = is_integer(CS),
@@ -347,6 +405,7 @@ special(File,Procs) ->
true = (C=/=undefined),
{ok,Mod2=#loaded_mod{},[]} =
crashdump_viewer:loaded_mod_details("application"),
+ io:format(" module details ok",[]),
#loaded_mod{old_size="No old code exists",
old_attrib=undefined,
old_comp_info=undefined}=Mod2,
@@ -428,8 +487,10 @@ do_create_dumps(DataDir,Rel) ->
end.
-%% Create a dump which has two visible nodes, one hidden and one
+%% Create a dump which has three visible nodes, one hidden and one
%% not connected node, and with monitors and links between nodes.
+%% One of the visible nodes is stopped and started again in order to
+%% get multiple creations.
full_dist_dump(DataDir,Rel) ->
Opt = rel_opt(Rel),
Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"",
@@ -450,6 +511,15 @@ full_dist_dump(DataDir,Rel) ->
get_response(P4),
get_response(P1),
+ %% start, stop and start a node in order to get multiple 'creations'
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P51 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P51),
+ ?t:stop_node(N5),
+ {ok,N5} = ?t:start_node(n5,peer,Opt ++ PzOpt),
+ P52 = rpc:call(N5,?helper_mod,remote_proc,[P1,Creator]),
+ get_response(P52),
+
{aaaaaaaa,N1} ! {hello,from,other,node}, % distribution message
?t:stop_node(N3),
@@ -458,6 +528,7 @@ full_dist_dump(DataDir,Rel) ->
?t:stop_node(N2),
?t:stop_node(N4),
+ ?t:stop_node(N5),
CD.
get_response(P) ->
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index c076c5e81e..5cf719acb1 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2014. 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
@@ -19,34 +19,35 @@
-module(observer_SUITE).
-include_lib("test_server/include/test_server.hrl").
+-include_lib("wx/include/wx.hrl").
+-include_lib("observer/src/observer_tv.hrl").
%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0]).
+-export([init_per_testcase/2, end_per_testcase/2,
+ init_per_group/2, end_per_group/2,
+ init_per_suite/1, end_per_suite/1
+ ]).
%% Test cases
--export([app_file/1, appup_file/1]).
+-export([app_file/1, appup_file/1,
+ basic/1, process_win/1, table_win/1
+ ]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
-init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?default_timeout),
- [{watchdog, Dog} | Config].
-
-end_per_testcase(_Case, Config) ->
- Dog = ?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- ok.
-
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [app_file, appup_file].
+all() ->
+ [app_file, appup_file, {group, gui}].
-groups() ->
- [].
+groups() ->
+ [{gui, [],
+ [basic
+ , process_win, table_win
+ ]
+ }].
init_per_suite(Config) ->
Config.
@@ -54,12 +55,40 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+init_per_group(gui, Config) ->
+ try
+ case os:type() of
+ {unix,darwin} ->
+ exit("Can not test on MacOSX");
+ {unix, _} ->
+ io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
+ case ct:get_config(xserver, none) of
+ none -> ignore;
+ Server -> os:putenv("DISPLAY", Server)
+ end;
+ _ -> ignore
+ end,
+ wx:new(),
+ wx:destroy(),
+ Config
+ catch
+ _:undef ->
+ {skipped, "No wx compiled for this platform"};
+ _:Reason ->
+ SkipReason = io_lib:format("Start wx failed: ~p", [Reason]),
+ {skipped, lists:flatten(SkipReason)}
+ end.
+end_per_group(_, _) ->
+ ok.
app_file(suite) ->
[];
@@ -72,3 +101,178 @@ app_file(Config) when is_list(Config) ->
%% Testing .appup file
appup_file(Config) when is_list(Config) ->
ok = ?t:appup_test(observer).
+
+-define(DBG(Foo), io:format("~p: ~p~n",[?LINE, catch Foo])).
+
+basic(suite) -> [];
+basic(doc) -> [""];
+basic(Config) when is_list(Config) ->
+ timer:send_after(100, "foobar"), %% Otherwise the timer sever gets added to procs
+ ProcsBefore = processes(),
+ NumProcsBefore = length(ProcsBefore),
+
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+
+ io:format("Notebook ~p~n",[Notebook]),
+ Count = wxNotebook:getPageCount(Notebook),
+ true = Count >= 6,
+ 0 = wxNotebook:getSelection(Notebook),
+ timer:sleep(500),
+ Check = fun(N, TestMore) ->
+ TestMore andalso
+ test_page(wxNotebook:getPageText(Notebook, N),
+ wxNotebook:getCurrentPage(Notebook)),
+ timer:sleep(200),
+ ok = wxNotebook:advanceSelection(Notebook)
+ end,
+ %% Just verify that we can toogle trough all pages
+ [_|_] = [Check(N, false) || N <- lists:seq(1, Count)],
+ %% Cause it to resize
+ Frame = get_top_level_parent(Notebook),
+ {W,H} = wxWindow:getSize(Frame),
+ wxWindow:setSize(Frame, W+10, H+10),
+ [_|_] = [Check(N, true) || N <- lists:seq(0, Count-1)],
+
+ ok = observer:stop(),
+ timer:sleep(2000), %% stop is async
+ ProcsAfter = processes(),
+ NumProcsAfter = length(ProcsAfter),
+ if NumProcsAfter=/=NumProcsBefore ->
+ ct:log("Before but not after:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsBefore -- ProcsAfter]]),
+ ct:log("After but not before:~n~p~n",
+ [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
+ ct:fail("leaking processes");
+ true ->
+ ok
+ end,
+ ok.
+
+test_page("Load Charts" ++ _, _Window) ->
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(2000),
+ ok;
+test_page("Applications" ++ _, _Window) ->
+ ok = application:start(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ Active = get_active(),
+ FakeEv = #wx{event=#wxCommand{type=command_listbox_selected, cmdString="mnesia"}},
+ Active ! FakeEv,
+ timer:sleep(1000), %% Give it time to refresh
+ ok = application:stop(mnesia),
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Processes" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ Focus = #wx{event=#wxList{type=command_list_item_focused, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated}},
+ Active ! Activate,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page(_Title = "Table" ++ _, _Window) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+
+ Active = get_active(),
+ Active ! refresh_interval,
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
+ timer:sleep(1000),
+ Focus = #wx{event=#wxList{type=command_list_item_selected, itemIndex=2}},
+ Active ! Focus,
+ Activate = #wx{event=#wxList{type=command_list_item_activated, itemIndex=2}},
+ Active ! Activate,
+
+ Info = 407, %% whitebox...
+ Active ! #wx{id=Info},
+ timer:sleep(1000),
+ ok;
+
+test_page(Title, Window) ->
+ io:format("Page ~p: ~p~n", [Title, Window]),
+ %% Just let it display some info and hopefully it doesn't crash
+ timer:sleep(1000),
+ ok.
+
+
+process_win(suite) -> [];
+process_win(doc) -> [""];
+process_win(Config) when is_list(Config) ->
+ ok = observer:start(),
+ ObserverNB = setup_whitebox_testing(),
+ Parent = get_top_level_parent(ObserverNB),
+ Frame = observer_procinfo:start(self(), Parent, self()),
+ PIPid = wx_object:get_pid(Frame),
+ PIPid ! {get_debug_info, self()},
+ Notebook = receive {procinfo_debug, NB} -> NB end,
+ Count = wxNotebook:getPageCount(Notebook),
+ Check = fun(_N) ->
+ ok = wxNotebook:advanceSelection(Notebook),
+ timer:sleep(400)
+ end,
+ [_|_] = [Check(N) || N <- lists:seq(1, Count)],
+ PIPid ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+table_win(suite) -> [];
+table_win(doc) -> [""];
+table_win(Config) when is_list(Config) ->
+ Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
+ Table = lists:nth(3, Tables),
+ ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
+ ok = observer:start(),
+ Notebook = setup_whitebox_testing(),
+ Parent = get_top_level_parent(Notebook),
+ TObj = observer_tv_table:start_link(Parent, [{node,node()}, {type,ets}, {table,#tab{name=foo, id=Table}}]),
+ %% Modal can not test edit..
+ %% TPid = wx_object:get_pid(TObj),
+ %% TPid ! #wx{event=#wxList{type=command_list_item_activated, itemIndex=12}},
+ timer:sleep(3000),
+ wx_object:get_pid(TObj) ! #wx{event=#wxClose{type=close_window}},
+ observer:stop(),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_top_level_parent(Window) ->
+ Parent = wxWindow:getParent(Window),
+ case wx:is_null(Parent) of
+ true -> Window;
+ false -> get_top_level_parent(Parent)
+ end.
+
+setup_whitebox_testing() ->
+ %% So that if we die observer exists
+ link(whereis(observer)),
+ {Env, Notebook, _Active} = get_observer_debug(),
+ wx:set_env(Env),
+ Notebook.
+
+get_active() ->
+ {_, _, Active} = get_observer_debug(),
+ Active.
+
+get_observer_debug() ->
+ observer ! {get_debug_info, self()},
+ receive
+ {observer_debug, Env, Notebook, Active} ->
+ {Env, Notebook, Active}
+ end.