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