1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2006-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(observer_SUITE).
22-include_lib("common_test/include/ct.hrl").
23-include_lib("wx/include/wx.hrl").
24-include_lib("observer/src/observer_tv.hrl").
25
26-define(ID_LOGVIEW, 5).
27
28%% Test server specific exports
29-export([all/0, suite/0,groups/0]).
30-export([init_per_testcase/2, end_per_testcase/2,
31	 init_per_group/2, end_per_group/2,
32	 init_per_suite/1, end_per_suite/1
33	]).
34
35%% Test cases
36-export([app_file/1, appup_file/1,
37	 basic/1, process_win/1, table_win/1,
38         port_win_when_tab_not_initiated/1
39	]).
40
41%% Default timetrap timeout (set in init_per_testcase)
42-define(default_timeout, ?t:minutes(2)).
43
44suite() -> [{timetrap, {minutes, 5}},
45            {ct_hooks,[ts_install_cth]}].
46
47all() ->
48    [app_file, appup_file, {group, gui}].
49
50groups() ->
51    [{gui, [],
52      [basic,
53       process_win,
54       table_win,
55       port_win_when_tab_not_initiated
56      ]
57     }].
58
59init_per_suite(Config) ->
60    Config.
61
62end_per_suite(_Config) ->
63    ok.
64
65init_per_testcase(_Case, Config) ->
66    Dog = ?t:timetrap(?default_timeout),
67    [{watchdog, Dog} | Config].
68
69end_per_testcase(_Case, Config) ->
70    Dog = ?config(watchdog, Config),
71    ?t:timetrap_cancel(Dog),
72    ok.
73
74init_per_group(gui, Config) ->
75    try
76	case os:type() of
77	    {unix,darwin} ->
78		exit("Can not test on MacOSX");
79	    {unix, _} ->
80		io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
81		case ct:get_config(xserver, none) of
82		    none -> ignore;
83		    Server -> os:putenv("DISPLAY", Server)
84		end;
85	    _ -> ignore
86	end,
87	wx:new(),
88	wx:destroy(),
89	Config
90    catch
91	_:undef ->
92	    {skipped, "No wx compiled for this platform"};
93	_:Reason ->
94	    SkipReason = io_lib:format("Start wx failed: ~p", [Reason]),
95	    {skipped, lists:flatten(SkipReason)}
96    end.
97end_per_group(_, _) ->
98    ok.
99
100app_file(suite) ->
101    [];
102app_file(doc) ->
103    ["Testing .app file"];
104app_file(Config) when is_list(Config) ->
105    ?line ok = ?t:app_test(observer),
106    ok.
107
108%% Testing .appup file
109appup_file(Config) when is_list(Config) ->
110    ok = ?t:appup_test(observer).
111
112-define(DBG(Foo), io:format("~p: ~p~n",[?LINE, catch Foo])).
113
114basic(suite) -> [];
115basic(doc) -> [""];
116basic(Config) when is_list(Config) ->
117    %% Start these before
118    wx:new(),
119    wx:destroy(),
120    timer:send_after(100, "foobar"),
121    {foo, node@machine} ! dummy_msg,  %% start distribution stuff
122    %% Otherwise ever lasting servers gets added to procs
123    ProcsBefore = processes(),
124    ProcInfoBefore = [{P,process_info(P)} || P <- ProcsBefore],
125    NumProcsBefore = length(ProcsBefore),
126
127    ok = observer:start(),
128    Notebook = setup_whitebox_testing(),
129
130    io:format("Notebook ~p~n",[Notebook]),
131    Count = wxNotebook:getPageCount(Notebook),
132    true = Count >= 6,
133    0 = wxNotebook:getSelection(Notebook),
134    timer:sleep(500),
135    Check = fun(N, TestMore) ->
136		    TestMore andalso
137			test_page(wxNotebook:getPageText(Notebook, N),
138				  wxNotebook:getCurrentPage(Notebook)),
139		    timer:sleep(200),
140		    ok = wxNotebook:advanceSelection(Notebook)
141	    end,
142    %% Just verify that we can toggle through all pages
143    [_|_] = [Check(N, false) || N <- lists:seq(1, Count)],
144    %% Cause it to resize
145    Frame = get_top_level_parent(Notebook),
146    {W,H} = wxWindow:getSize(Frame),
147    wxWindow:setSize(Frame, W+10, H+10),
148    [_|_] = [Check(N, true) || N <- lists:seq(0, Count-1)],
149
150    ok = observer:stop(),
151    timer:sleep(2000), %% stop is async
152    ProcsAfter = processes(),
153    NumProcsAfter = length(ProcsAfter),
154    if NumProcsAfter=/=NumProcsBefore ->
155            BeforeNotAfter = ProcsBefore -- ProcsAfter,
156	    ct:log("Before but not after:~n~p~n",
157		   [[{P,I} || {P,I} <- ProcInfoBefore,
158                              lists:member(P,BeforeNotAfter)]]),
159	    ct:log("After but not before:~n~p~n",
160		   [[{P,process_info(P)} || P <- ProcsAfter -- ProcsBefore]]),
161	    ct:fail("leaking processes");
162       true ->
163	    ok
164    end,
165    ok.
166
167test_page("Load Charts" ++ _, _Window) ->
168    %% Just let it display some info and hopefully it doesn't crash
169    timer:sleep(2000),
170    ok;
171test_page("Applications" ++ _, _Window) ->
172    ok = application:start(mnesia),
173    timer:sleep(1000),  %% Give it time to refresh
174    Active = get_active(),
175    FakeEv = #wx{event=#wxCommand{type=command_listbox_selected, cmdString="mnesia"}},
176    Active ! FakeEv,
177    timer:sleep(1000),  %% Give it time to refresh
178    ok = application:stop(mnesia),
179    timer:sleep(1000),  %% Give it time to refresh
180    ok;
181
182test_page("Processes" ++ _, _Window) ->
183    timer:sleep(500),  %% Give it time to refresh
184    Active = get_active(),
185    Active ! refresh_interval,
186    ChangeSort = fun(N) ->
187			 FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
188			 Active ! FakeEv,
189			 timer:sleep(200)
190		 end,
191    [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
192    Focus = #wx{event=#wxList{type=command_list_item_focused, itemIndex=2}},
193    Active ! Focus,
194    Activate = #wx{event=#wxList{type=command_list_item_activated}},
195    Active ! Activate,
196    timer:sleep(1000),  %% Give it time to refresh
197    ok;
198
199test_page("Ports" ++ _, _Window) ->
200    timer:sleep(500),  %% Give it time to refresh
201    Active = get_active(),
202    Active ! refresh_interval,
203    ChangeSort = fun(N) ->
204			 FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
205			 Active ! FakeEv,
206			 timer:sleep(200)
207		 end,
208    [ChangeSort(N) || N <- lists:seq(1,4) ++ [0]],
209    Activate = #wx{event=#wxList{type=command_list_item_activated,
210				 itemIndex=2}},
211    Active ! Activate,
212    timer:sleep(1000),  %% Give it time to refresh
213    ok;
214
215test_page("Table" ++ _, _Window) ->
216    Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
217    Table = lists:nth(3, Tables),
218    ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
219
220    Active = get_active(),
221    Active ! refresh_interval,
222    ChangeSort = fun(N) ->
223			 FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
224			 Active ! FakeEv,
225			 timer:sleep(200)
226		 end,
227    [ChangeSort(N) || N <- lists:seq(1,5) ++ [0]],
228    timer:sleep(1000),
229    Focus = #wx{event=#wxList{type=command_list_item_selected, itemIndex=2}},
230    Active ! Focus,
231    Activate = #wx{event=#wxList{type=command_list_item_activated, itemIndex=2}},
232    Active ! Activate,
233
234    Info = 407, %% whitebox...
235    Active ! #wx{id=Info},
236    timer:sleep(1000),
237    ok;
238
239test_page("Trace Overview" ++ _, _Window) ->
240    timer:sleep(500),  %% Give it time to refresh
241    Active = get_active(),
242    Active ! refresh_interval,
243    timer:sleep(1000),  %% Give it time to refresh
244    ok;
245
246test_page(Title, Window) ->
247    io:format("Page ~p: ~p~n", [Title, Window]),
248    %% Just let it display some info and hopefully it doesn't crash
249    timer:sleep(1000),
250    ok.
251
252
253process_win(suite) -> [];
254process_win(doc) -> [""];
255process_win(Config) when is_list(Config) ->
256    % Stop SASL if already started
257    SaslStart = case whereis(sasl_sup) of
258                  undefined -> false;
259                  _         -> application:stop(sasl),
260                               true
261                end,
262    % Define custom sasl and log_mf_h app vars
263    Privdir=?config(priv_dir,Config),
264    application:set_env(sasl, sasl_error_logger, tty),
265    application:set_env(sasl, error_logger_mf_dir, Privdir),
266    application:set_env(sasl, error_logger_mf_maxbytes, 1000),
267    application:set_env(sasl, error_logger_mf_maxfiles, 5),
268    application:start(sasl),
269    ok = observer:start(),
270    ObserverNB = setup_whitebox_testing(),
271    Parent = get_top_level_parent(ObserverNB),
272    % Activate log view
273    whereis(observer) ! #wx{id = ?ID_LOGVIEW, event = #wxCommand{type = command_menu_selected}},
274    timer:sleep(1000),
275    % Process window tests (use sasl_sup for a non empty Log tab)
276    Frame = observer_procinfo:start(whereis(sasl_sup), Parent, self()),
277    PIPid = wx_object:get_pid(Frame),
278    PIPid ! {get_debug_info, self()},
279    Notebook = receive {procinfo_debug, NB} -> NB end,
280    Count = wxNotebook:getPageCount(Notebook),
281    Check = fun(_N) ->
282		    ok = wxNotebook:advanceSelection(Notebook),
283		    timer:sleep(400)
284	    end,
285    [_|_] = [Check(N) || N <- lists:seq(1, Count)],
286    PIPid ! #wx{event=#wxClose{type=close_window}},
287    observer:stop(),
288    application:stop(sasl),
289    case SaslStart of
290         true  -> application:start(sasl);
291         false -> ok
292    end,
293    ok.
294
295table_win(suite) -> [];
296table_win(doc) -> [""];
297table_win(Config) when is_list(Config) ->
298    Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
299    Table = lists:nth(3, Tables),
300    ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
301    ok = observer:start(),
302    Notebook = setup_whitebox_testing(),
303    Parent = get_top_level_parent(Notebook),
304    TObj = observer_tv_table:start_link(Parent, [{node,node()}, {type,ets}, {table,#tab{name=foo, id=Table}}]),
305    %% Modal cannot test edit..
306    %% TPid = wx_object:get_pid(TObj),
307    %% TPid ! #wx{event=#wxList{type=command_list_item_activated, itemIndex=12}},
308    timer:sleep(3000),
309    wx_object:get_pid(TObj) ! #wx{event=#wxClose{type=close_window}},
310    observer:stop(),
311    ok.
312
313%% Test PR-1296/OTP-14151
314%% Clicking a link to a port before the port tab has been activated the
315%% first time crashes observer.
316port_win_when_tab_not_initiated(_Config) ->
317    {ok,Port} = gen_tcp:listen(0,[]),
318    ok = observer:start(),
319    _Notebook = setup_whitebox_testing(),
320    observer ! {open_link,erlang:port_to_list(Port)},
321    timer:sleep(1000),
322    observer:stop(),
323    ok.
324
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326
327get_top_level_parent(Window) ->
328    Parent =  wxWindow:getParent(Window),
329    case wx:is_null(Parent) of
330	true -> Window;
331	false -> get_top_level_parent(Parent)
332    end.
333
334setup_whitebox_testing() ->
335    %% So that if we die observer exists
336    link(whereis(observer)),
337    {Env, Notebook, _Active} = get_observer_debug(),
338    wx:set_env(Env),
339    Notebook.
340
341get_active() ->
342    {_, _, Active} = get_observer_debug(),
343    Active.
344
345get_observer_debug() ->
346    observer ! {get_debug_info, self()},
347    receive
348	{observer_debug, Env, Notebook, Active} ->
349	    {Env, Notebook, Active}
350    end.
351