1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2009-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-module(et_test_lib).
21-compile([export_all, nowarn_export_all]).
22
23-include("et_test_lib.hrl").
24
25%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26
27init_per_suite(Config) when is_list(Config)->
28    incr_timetrap(Config, 5).
29
30end_per_suite(Config) when is_list(Config)->
31    ok.
32
33incr_timetrap(Config, Times) ->
34    Key = tc_timeout,
35    KeyPos = 1,
36    NewTime =
37	case lists:keysearch(Key, KeyPos, Config) of
38	    {value, {Key, OldTime}} ->
39		(timer:minutes(1) + OldTime) * Times;
40	    false ->
41		timer:minutes(1) * Times
42	end,
43    lists:keystore(Key, KeyPos, Config, {Key, NewTime}).
44
45set_kill_timer(Config) ->
46    case init:get_argument(et_test_timeout) of
47	{ok, _} ->
48	    Config;
49	_ ->
50	    Time =
51		case lookup_config(tc_timeout, Config) of
52		    [] ->
53			timer:minutes(5);
54		    ConfigTime when is_integer(ConfigTime) ->
55			ConfigTime
56		end,
57	    WatchDog = test_server:timetrap(Time),
58	    [{kill_timer, WatchDog} | Config]
59    end.
60
61reset_kill_timer(Config) ->
62    DogKiller =
63	case get(et_test_server) of
64	    true ->
65		fun(P) when is_pid(P) -> P ! stop;
66		   (_) -> ok
67		end;
68	    _ ->
69		fun(Ref) -> test_server:timetrap_cancel(Ref) end
70	end,
71    case lists:keysearch(kill_timer, 1, Config) of
72	{value, {kill_timer, WatchDog}} ->
73	    DogKiller(WatchDog),
74	    lists:keydelete(kill_timer, 1, Config);
75	_ ->
76	    Config
77    end.
78
79lookup_config(Key,Config) ->
80    case lists:keysearch(Key, 1, Config) of
81	{value,{Key,Val}} ->
82	    Val;
83	_ ->
84	    []
85    end.
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87
88wx_init_per_suite(Config) ->
89    {_Pid, Ref} =
90	spawn_monitor(fun() ->
91			      %% Avoid test case crash if wx master process dies
92			      process_flag(trap_exit, true),
93			      try
94				  case os:type() of
95				      {unix,darwin} ->
96					  exit({skipped, "Can not test on MacOSX"});
97				      {unix, _} ->
98					  io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
99					  case ct:get_config(xserver, none) of
100					      none   -> ignore;
101					      Server -> os:putenv("DISPLAY", Server)
102					  end;
103				      _ ->
104					  ignore
105				  end,
106				  wx:new(),
107				  wx:destroy()
108			      catch
109				  error:undef ->
110				      exit({skipped, "No wx compiled for this platform"});
111				    _:Reason ->
112				      exit({skipped, lists:flatten(io_lib:format("Start wx failed: ~p", [Reason]))})
113			      end,
114			      exit(normal)
115		      end),
116    receive
117	{'DOWN', Ref, _, _, normal} ->
118	    init_per_suite(Config);
119	{'DOWN', Ref, _, _, {skipped, _} = Skipped} ->
120	    Skipped;
121	{'DOWN', Ref, _, _, Reason} ->
122	    exit({wx_init_per_suite, Reason})
123    after timer:minutes(1) ->
124	    exit({wx_init_per_suite, timeout})
125    end.
126
127wx_end_per_suite(Config) ->
128    end_per_suite(Config).
129
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131
132init_per_testcase(_Func, Config) when is_list(Config) ->
133    set_kill_timer(Config),
134    global:register_name(et_global_logger, group_leader()),
135    Config.
136
137end_per_testcase(_Func, Config) when is_list(Config) ->
138    global:unregister_name(et_global_logger),
139    reset_kill_timer(Config),
140    Config.
141
142%% Backwards compatible with test_server
143tc_info(suite) -> [];
144tc_info(doc) -> "".
145
146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147
148%% Use ?log(Format, Args) as wrapper
149log(Format, Args, LongFile, Line) ->
150    File = filename:basename(LongFile),
151    Format2 = lists:concat([File, "(", Line, ")", ": ", Format]),
152    log(Format2, Args).
153
154log(Format, Args) ->
155    case global:whereis_name(et_global_logger) of
156	undefined ->
157	    io:format(user, Format, Args);
158	Pid ->
159	    io:format(Pid, Format, Args)
160    end.
161
162verbose(Format, Args, File, Line) ->
163    Arg = et_test_verbose,
164    case get(Arg) of
165	false ->
166	    ok;
167	true ->
168	    log(Format, Args, File, Line);
169	undefined ->
170	    case init:get_argument(Arg) of
171		{ok, List} when is_list(List) ->
172		    case lists:last(List) of
173			["true"] ->
174			    put(Arg, true),
175			    log(Format, Args, File, Line);
176			_ ->
177			    put(Arg, false),
178			    ok
179		    end;
180		_ ->
181		    put(Arg, false),
182		    ok
183	    end
184    end.
185
186error(Format, Args, File, Line) ->
187    global:send(et_global_logger, {failed, File, Line}),
188    Fail = {filename:basename(File),Line,Args},
189    case global:whereis_name(et_test_case_sup) of
190	undefined -> ignore;
191	Pid -> Pid ! Fail
192	    %% 	    global:send(et_test_case_sup, Fail),
193    end,
194    log("<ERROR>~n" ++ Format, Args, File, Line).
195
196
197pick_msg() ->
198    receive
199	Message -> Message
200    after 4000 -> timeout
201    end.
202
203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204%% Utility functions
205
206user_available(Config) ->
207    false /= proplists:get_value(user, Config, false).
208
209
210wx_destroy(Frame, Config) ->
211    case proplists:get_value(user, Config, false) of
212	false ->
213	    timer:sleep(100),
214	    ?m(ok, wxFrame:destroy(Frame)),
215	    ?m(ok, wx:destroy());
216	true ->
217	    timer:sleep(500),
218	    ?m(ok, wxFrame:destroy(Frame)),
219	    ?m(ok, wx:destroy());
220	step -> %% Wait for user to close window
221	    ?m(ok, wxEvtHandler:connect(Frame, close_window, [{skip,true}])),
222	    wait_for_close()
223    end.
224
225wait_for_close() ->
226    receive
227	#wx{event=#wxClose{}} ->
228	    ?log("Got close~n",[]),
229	    ?m(ok, wx:destroy());
230	#wx{obj=Obj, event=Event} ->
231	    try
232		Name = wxTopLevelWindow:getTitle(Obj),
233		?log("~p Event: ~p~n", [Name, Event])
234	    catch _:_ ->
235		?log("Event: ~p~n", [Event])
236	    end,
237	    wait_for_close();
238	Other ->
239	    ?log("Unexpected: ~p~n", [Other]),
240	    wait_for_close()
241    end.
242
243
244
245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246%% A small test server, which can be run standalone in a shell
247
248run_test(Test = {_,_},Config) ->
249    run_test([Test],Config);
250run_test([{Module, TC} | Rest], Config) ->
251    log("\n\n=== Eval test suite: ~w ===~n", [Module]),
252    case catch Module:init_per_suite(Config) of
253	{skipped, Reason} ->
254	    log("Test suite skipped: ~s~n", [Reason]),
255	    [{skipped, Reason}];
256	NewConfig when is_list(NewConfig) ->
257	    Res =
258		if
259		    TC =:= all ->
260			[do_run_test(Module, Test, NewConfig) || Test <- Module:all()];
261		    is_list(TC) ->
262			[do_run_test(Module, Test, NewConfig) || Test <- TC];
263		    true ->
264			[do_run_test(Module, TC, NewConfig)]
265		end,
266	    Module:end_per_suite(NewConfig),
267	    Res ++ run_test(Rest, NewConfig);
268	Error ->
269	    ?error("Test suite skipped: ~w~n", [Error]),
270	    [{skipped, Error}]
271    end;
272run_test([], _Config) ->
273    [].
274
275do_run_test(Module, all, Config) ->
276    All = [{Module, Test} || Test <- Module:all()],
277    run_test(All, Config);
278do_run_test(Module, TestCase, Config) ->
279    log("Eval test case: ~w~n", [{Module, TestCase}]),
280    Sec = timer:seconds(1) * 1000,
281    {T, Res} =
282	timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]),
283    log("Tested ~w in ~w sec~n", [TestCase, T div Sec]),
284    {T div Sec, Res}.
285
286eval_test_case(Mod, Fun, Config) ->
287    flush(),
288    global:register_name(et_test_case_sup, self()),
289    Flag = process_flag(trap_exit, true),
290    Pid = spawn_link(?MODULE, test_case_evaluator, [Mod, Fun, [Config]]),
291    R = wait_for_evaluator(Pid, Mod, Fun, Config),
292    global:unregister_name(et_test_case_sup),
293    process_flag(trap_exit, Flag),
294    R.
295
296test_case_evaluator(Mod, Fun, [Config]) ->
297    NewConfig = Mod:init_per_testcase(Fun, Config),
298    R = apply(Mod, Fun, [NewConfig]),
299    Mod:end_per_testcase(Fun, NewConfig),
300    exit({test_case_ok, R}).
301
302wait_for_evaluator(Pid, Mod, Fun, Config) ->
303    receive
304	{'EXIT', Pid, {test_case_ok, _PidRes}} ->
305	    Errors = flush(),
306	    Res =
307		case Errors of
308		    [] -> ok;
309		    Errors -> failed
310		end,
311	    {Res, {Mod, Fun}, Errors};
312	{'EXIT', Pid, {skipped, Reason}} ->
313	    log("<WARNING> Test case ~w skipped, because ~p~n",
314		[{Mod, Fun}, Reason]),
315	    Mod:end_per_testcase(Fun, Config),
316	    {skip, {Mod, Fun}, Reason};
317	{'EXIT', Pid, Reason} ->
318	    log("<ERROR> Eval process ~w exited, because\n\t~p~n",
319		[{Mod, Fun}, Reason]),
320	    Mod:end_per_testcase(Fun, Config),
321	    {crash, {Mod, Fun}, Reason}
322    end.
323
324flush() ->
325    receive Msg -> [Msg | flush()]
326    after 0 -> []
327    end.
328
329
330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331