1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2003-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(ct). 22 23-include("ct.hrl"). 24-include("ct_util.hrl"). 25 26%% Command line user interface for running tests 27-export([install/1, run/1, run/2, run/3, 28 run_test/1, run_testspec/1, step/3, step/4, 29 start_interactive/0, stop_interactive/0]). 30 31%% Test suite API 32-export([require/1, require/2, 33 get_config/1, get_config/2, get_config/3, 34 reload_config/1, 35 escape_chars/1, escape_chars/2, 36 log/1, log/2, log/3, log/4, log/5, 37 print/1, print/2, print/3, print/4, print/5, 38 pal/1, pal/2, pal/3, pal/4, pal/5, 39 set_verbosity/2, get_verbosity/1, 40 capture_start/0, capture_stop/0, capture_get/0, capture_get/1, 41 fail/1, fail/2, comment/1, comment/2, make_priv_dir/0, 42 testcases/2, userdata/2, userdata/3, 43 timetrap/1, get_timetrap_info/0, sleep/1, 44 notify/2, sync_notify/2, 45 break/1, break/2, continue/0, continue/1]). 46 47%% New API for manipulating with config handlers 48-export([add_config/2, remove_config/2]). 49 50%% Other interface functions 51-export([get_status/0, abort_current_testcase/1, 52 get_event_mgr_ref/0, 53 get_testspec_terms/0, get_testspec_terms/1, 54 encrypt_config_file/2, encrypt_config_file/3, 55 decrypt_config_file/2, decrypt_config_file/3]). 56 57-export([get_target_name/1]). 58-export([get_progname/0]). 59-export([parse_table/1, listenv/1]). 60 61-export([remaining_test_procs/0]). 62 63%%---------------------------------------------------------------------- 64%% Exported types 65%%---------------------------------------------------------------------- 66%% For ct_gen_conn 67-export_type([config_key/0, 68 target_name/0, 69 key_or_name/0]). 70 71%% For cth_conn_log 72-export_type([conn_log_options/0, 73 conn_log_type/0, 74 conn_log_mod/0]). 75 76%%------------------------------------------------------------------ 77%% Type declarations 78%% ------------------------------------------------------------------ 79-type config_key() :: atom(). % Config key which exists in a config file 80-type target_name() :: atom().% Name associated to a config_key() though 'require' 81-type key_or_name() :: config_key() | target_name(). 82 83%% Types used when logging connections with the 'cth_conn_log' hook 84-type conn_log_options() :: [conn_log_option()]. 85-type conn_log_option() :: {log_type,conn_log_type()} | 86 {hosts,[key_or_name()]}. 87-type conn_log_type() :: raw | pretty | html | silent. 88-type conn_log_mod() :: ct_netconfc | ct_telnet. 89%%---------------------------------------------------------------------- 90 91 92install(Opts) -> 93 ct_run:install(Opts). 94 95run(TestDir,Suite,Cases) -> 96 ct_run:run(TestDir,Suite,Cases). 97 98run(TestDir,Suite) -> 99 ct_run:run(TestDir,Suite). 100 101run(TestDirs) -> 102 ct_run:run(TestDirs). 103 104run_test(Opts) -> 105 ct_run:run_test(Opts). 106 107run_testspec(TestSpec) -> 108 ct_run:run_testspec(TestSpec). 109 110step(TestDir,Suite,Case) -> 111 ct_run:step(TestDir,Suite,Case). 112 113step(TestDir,Suite,Case,Opts) -> 114 ct_run:step(TestDir,Suite,Case,Opts). 115 116start_interactive() -> 117 _ = ct_util:start(interactive), 118 ok. 119 120stop_interactive() -> 121 ct_util:stop(normal), 122 ok. 123 124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 125%%% MISC INTERFACE 126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 127 128require(Required) -> 129 ct_config:require(Required). 130 131require(Name,Required) -> 132 ct_config:require(Name,Required). 133 134get_config(Required) -> 135 ct_config:get_config(Required,undefined,[]). 136 137get_config(Required,Default) -> 138 ct_config:get_config(Required,Default,[]). 139 140get_config(Required,Default,Opts) -> 141 ct_config:get_config(Required,Default,Opts). 142 143reload_config(Required)-> 144 ct_config:reload_config(Required). 145 146get_testspec_terms() -> 147 case ct_util:get_testdata(testspec) of 148 undefined -> 149 undefined; 150 CurrSpecRec -> 151 ct_testspec:testspec_rec2list(CurrSpecRec) 152 end. 153 154get_testspec_terms(Tags) -> 155 case ct_util:get_testdata(testspec) of 156 undefined -> 157 undefined; 158 CurrSpecRec -> 159 ct_testspec:testspec_rec2list(Tags, CurrSpecRec) 160 end. 161 162escape_chars(IoList) -> 163 ct_logs:escape_chars(IoList). 164 165escape_chars(Format, Args) -> 166 try io_lib:format(Format, Args) of 167 IoList -> 168 ct_logs:escape_chars(IoList) 169 catch 170 _:Reason -> 171 {error,Reason} 172 end. 173 174log(Format) -> 175 log(default,?STD_IMPORTANCE,Format,[],[]). 176 177log(X1,X2) -> 178 {Category,Importance,Format,Args} = 179 if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; 180 is_integer(X1) -> {default,X1,X2,[]}; 181 is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} 182 end, 183 log(Category,Importance,Format,Args,[]). 184 185log(X1,X2,X3) -> 186 {Category,Importance,Format,Args,Opts} = 187 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[],[]}; 188 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,[]}; 189 is_integer(X1) -> {default,X1,X2,X3,[]}; 190 is_list(X1), is_list(X2) -> {default,?STD_IMPORTANCE,X1,X2,X3} 191 end, 192 log(Category,Importance,Format,Args,Opts). 193 194log(X1,X2,X3,X4) -> 195 {Category,Importance,Format,Args,Opts} = 196 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,X4,[]}; 197 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,X4}; 198 is_integer(X1) -> {default,X1,X2,X3,X4} 199 end, 200 log(Category,Importance,Format,Args,Opts). 201 202log(Category,Importance,Format,Args,Opts) -> 203 ct_logs:tc_log(Category,Importance,Format,Args,Opts). 204 205print(Format) -> 206 print(default,?STD_IMPORTANCE,Format,[],[]). 207 208print(X1,X2) -> 209 {Category,Importance,Format,Args} = 210 if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; 211 is_integer(X1) -> {default,X1,X2,[]}; 212 is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} 213 end, 214 print(Category,Importance,Format,Args,[]). 215 216print(X1,X2,X3) -> 217 {Category,Importance,Format,Args,Opts} = 218 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[],[]}; 219 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,[]}; 220 is_integer(X1) -> {default,X1,X2,X3,[]}; 221 is_list(X1), is_list(X2) -> {default,?STD_IMPORTANCE,X1,X2,X3} 222 end, 223 print(Category,Importance,Format,Args,Opts). 224 225print(X1,X2,X3,X4) -> 226 {Category,Importance,Format,Args,Opts} = 227 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,X4,[]}; 228 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,X4}; 229 is_integer(X1) -> {default,X1,X2,X3,X4} 230 end, 231 print(Category,Importance,Format,Args,Opts). 232 233print(Category,Importance,Format,Args,Opts) -> 234 ct_logs:tc_print(Category,Importance,Format,Args,Opts). 235 236pal(Format) -> 237 pal(default,?STD_IMPORTANCE,Format,[]). 238 239pal(X1,X2) -> 240 {Category,Importance,Format,Args} = 241 if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; 242 is_integer(X1) -> {default,X1,X2,[]}; 243 is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} 244 end, 245 pal(Category,Importance,Format,Args,[]). 246 247pal(X1,X2,X3) -> 248 {Category,Importance,Format,Args,Opts} = 249 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[],[]}; 250 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,[]}; 251 is_integer(X1) -> {default,X1,X2,X3,[]}; 252 is_list(X1), is_list(X2) -> {default,?STD_IMPORTANCE,X1,X2,X3} 253 end, 254 pal(Category,Importance,Format,Args,Opts). 255 256pal(X1,X2,X3,X4) -> 257 {Category,Importance,Format,Args,Opts} = 258 if is_atom(X1), is_integer(X2) -> {X1,X2,X3,X4,[]}; 259 is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3,X4}; 260 is_integer(X1) -> {default,X1,X2,X3,X4} 261 end, 262 pal(Category,Importance,Format,Args,Opts). 263 264pal(Category,Importance,Format,Args,Opts) -> 265 ct_logs:tc_pal(Category,Importance,Format,Args,Opts). 266 267set_verbosity(Category, Level) -> 268 ct_util:set_verbosity({Category,Level}). 269 270get_verbosity(Category) -> 271 ct_util:get_verbosity(Category). 272 273capture_start() -> 274 test_server:capture_start(). 275 276capture_stop() -> 277 test_server:capture_stop(). 278 279capture_get() -> 280 %% remove default log printouts (e.g. ct:log/2 printouts) 281 capture_get([default]). 282 283capture_get([ExclCat | ExclCategories]) -> 284 Strs = test_server:capture_get(), 285 CatsStr = [atom_to_list(ExclCat) | 286 [[$| | atom_to_list(EC)] || EC <- ExclCategories]], 287 {ok,MP} = re:compile("<div class=\"(" ++ lists:flatten(CatsStr) ++ ")\">.*", 288 [unicode]), 289 lists:flatmap(fun(Str) -> 290 case re:run(Str, MP) of 291 {match,_} -> []; 292 nomatch -> [Str] 293 end 294 end, Strs); 295 296capture_get([]) -> 297 test_server:capture_get(). 298 299fail(Reason) -> 300 try 301 exit({test_case_failed,Reason}) 302 catch 303 Class:R:S -> 304 case S of 305 [{?MODULE,fail,1,_}|Stk] -> ok; 306 Stk -> ok 307 end, 308 erlang:raise(Class, R, Stk) 309 end. 310 311fail(Format, Args) -> 312 try io_lib:format(Format, Args) of 313 Str -> 314 try 315 exit({test_case_failed,lists:flatten(Str)}) 316 catch 317 Class:R:S -> 318 case S of 319 [{?MODULE,fail,2,_}|Stk] -> ok; 320 Stk -> ok 321 end, 322 erlang:raise(Class, R, Stk) 323 end 324 catch 325 _:BadArgs -> 326 exit({BadArgs,{?MODULE,fail,[Format,Args]}}) 327 end. 328 329comment(Comment) when is_list(Comment) -> 330 Formatted = 331 case (catch io_lib:format("~ts",[Comment])) of 332 {'EXIT',_} -> % it's a list not a string 333 io_lib:format("~tp",[Comment]); 334 String -> 335 String 336 end, 337 send_html_comment(lists:flatten(Formatted)); 338comment(Comment) -> 339 Formatted = io_lib:format("~tp",[Comment]), 340 send_html_comment(lists:flatten(Formatted)). 341 342comment(Format, Args) when is_list(Format), is_list(Args) -> 343 Formatted = 344 case (catch io_lib:format(Format, Args)) of 345 {'EXIT',Reason} -> % bad args 346 exit({Reason,{?MODULE,comment,[Format,Args]}}); 347 String -> 348 lists:flatten(String) 349 end, 350 send_html_comment(Formatted). 351 352send_html_comment(Comment) -> 353 Html = "<font color=\"green\">" ++ Comment ++ "</font>", 354 ct_util:set_testdata({{comment,group_leader()},Html}), 355 test_server:comment(Html). 356 357make_priv_dir() -> 358 test_server:make_priv_dir(). 359 360get_target_name(Handle) -> 361 ct_util:get_target_name(Handle). 362 363-spec get_progname() -> string(). 364 365get_progname() -> 366 case init:get_argument(progname) of 367 {ok, [[Prog]]} -> 368 Prog; 369 _Other -> 370 "no_prog_name" 371 end. 372 373parse_table(Data) -> 374 ct_util:parse_table(Data). 375 376listenv(Telnet) -> 377 ct_util:listenv(Telnet). 378 379testcases(TestDir, Suite) -> 380 case make_and_load(TestDir, Suite) of 381 E = {error,_} -> 382 E; 383 _ -> 384 case (catch Suite:all()) of 385 {'EXIT',Reason} -> 386 {error,Reason}; 387 TCs -> 388 TCs 389 end 390 end. 391 392make_and_load(Dir, Suite) -> 393 EnvInclude = string:lexemes(os:getenv("CT_INCLUDE_PATH", ""), [$:,$ ,$,]), 394 StartInclude = 395 case init:get_argument(include) of 396 {ok,[Dirs]} -> Dirs; 397 _ -> [] 398 end, 399 UserInclude = EnvInclude ++ StartInclude, 400 case ct_run:run_make(Dir, Suite, UserInclude) of 401 MErr = {error,_} -> 402 MErr; 403 _ -> 404 TestDir = ct_util:get_testdir(Dir, Suite), 405 File = filename:join(TestDir, atom_to_list(Suite)), 406 case code:soft_purge(Suite) of 407 true -> 408 code:load_abs(File); 409 false -> % will use loaded 410 {module,Suite} 411 end 412 end. 413 414userdata(TestDir, Suite) -> 415 case make_and_load(TestDir, Suite) of 416 E = {error,_} -> 417 E; 418 _ -> 419 Info = (catch Suite:suite()), 420 get_userdata(Info, "suite/0") 421 end. 422 423get_userdata({'EXIT',{Undef,_}}, Spec) when Undef == undef; 424 Undef == function_clause -> 425 {error,list_to_atom(Spec ++ " is not defined")}; 426get_userdata({'EXIT',Reason}, Spec) -> 427 {error,{list_to_atom("error in " ++ Spec),Reason}}; 428get_userdata(List, _) when is_list(List) -> 429 Fun = fun({userdata,Data}, Acc) -> [Data | Acc]; 430 (_, Acc) -> Acc 431 end, 432 case lists:foldl(Fun, [], List) of 433 Terms -> 434 lists:flatten(lists:reverse(Terms)) 435 end; 436get_userdata(_BadTerm, Spec) -> 437 {error,list_to_atom(Spec ++ " must return a list")}. 438 439userdata(TestDir, Suite, {group,GroupName}) -> 440 case make_and_load(TestDir, Suite) of 441 E = {error,_} -> 442 E; 443 _ -> 444 Info = (catch apply(Suite, group, [GroupName])), 445 get_userdata(Info, "group("++atom_to_list(GroupName)++")") 446 end; 447 448userdata(TestDir, Suite, Case) when is_atom(Case) -> 449 case make_and_load(TestDir, Suite) of 450 E = {error,_} -> 451 E; 452 _ -> 453 Info = (catch apply(Suite, Case, [])), 454 get_userdata(Info, atom_to_list(Case)++"/0") 455 end. 456 457get_status() -> 458 case get_testdata(curr_tc) of 459 {ok,TestCase} -> 460 case get_testdata(stats) of 461 {ok,{Ok,Failed,Skipped={UserSkipped,AutoSkipped}}} -> 462 [{current,TestCase}, 463 {successful,Ok}, 464 {failed,Failed}, 465 {skipped,Skipped}, 466 {total,Ok+Failed+UserSkipped+AutoSkipped}]; 467 Err1 -> Err1 468 end; 469 Err2 -> Err2 470 end. 471 472get_testdata(Key) -> 473 case catch ct_util:get_testdata(Key) of 474 {error,ct_util_server_not_running} -> 475 no_tests_running; 476 Error = {error,_Reason} -> 477 Error; 478 {'EXIT',_Reason} -> 479 no_tests_running; 480 undefined -> 481 {error,no_testdata}; 482 [CurrTC] when Key == curr_tc -> 483 {ok,CurrTC}; 484 Data -> 485 {ok,Data} 486 end. 487 488abort_current_testcase(Reason) -> 489 test_server_ctrl:abort_current_testcase(Reason). 490 491get_event_mgr_ref() -> 492 ?CT_EVMGR_REF. 493 494encrypt_config_file(SrcFileName, EncryptFileName) -> 495 ct_config:encrypt_config_file(SrcFileName, EncryptFileName). 496 497encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) -> 498 ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile). 499 500decrypt_config_file(EncryptFileName, TargetFileName) -> 501 ct_config:decrypt_config_file(EncryptFileName, TargetFileName). 502 503decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) -> 504 ct_config:decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile). 505 506add_config(Callback, Config)-> 507 ct_config:add_config(Callback, Config). 508 509remove_config(Callback, Config) -> 510 ct_config:remove_config(Callback, Config). 511 512timetrap(Time) -> 513 test_server:timetrap_cancel(), 514 test_server:timetrap(Time). 515 516get_timetrap_info() -> 517 test_server:get_timetrap_info(). 518 519sleep({hours,Hs}) -> 520 sleep(trunc(Hs * 1000 * 60 * 60)); 521sleep({minutes,Ms}) -> 522 sleep(trunc(Ms * 1000 * 60)); 523sleep({seconds,Ss}) -> 524 sleep(trunc(Ss * 1000)); 525sleep(Time) -> 526 test_server:adjusted_sleep(Time). 527 528notify(Name,Data) -> 529 ct_event:notify(Name, Data). 530 531sync_notify(Name,Data) -> 532 ct_event:sync_notify(Name, Data). 533 534break(Comment) -> 535 case {ct_util:get_testdata(starter), 536 ct_util:get_testdata(release_shell)} of 537 {ct,ReleaseSh} when ReleaseSh /= true -> 538 Warning = "ct:break/1 can only be used if release_shell == true.\n", 539 ct_logs:log("Warning!", Warning, []), 540 io:format(user, "Warning! " ++ Warning, []), 541 {error,'enable break with release_shell option'}; 542 _ -> 543 case get_testdata(curr_tc) of 544 {ok,{_,_TestCase}} -> 545 test_server:break(?MODULE, Comment); 546 {ok,Cases} when is_list(Cases) -> 547 {error,{'multiple cases running', 548 [TC || {_,TC} <- Cases]}}; 549 Error = {error,_} -> 550 Error; 551 Error -> 552 {error,Error} 553 end 554 end. 555 556break(TestCase, Comment) -> 557 case {ct_util:get_testdata(starter), 558 ct_util:get_testdata(release_shell)} of 559 {ct,ReleaseSh} when ReleaseSh /= true -> 560 Warning = "ct:break/2 can only be used if release_shell == true.\n", 561 ct_logs:log("Warning!", Warning, []), 562 io:format(user, "Warning! " ++ Warning, []), 563 {error,'enable break with release_shell option'}; 564 _ -> 565 case get_testdata(curr_tc) of 566 {ok,Cases} when is_list(Cases) -> 567 case lists:keymember(TestCase, 2, Cases) of 568 true -> 569 test_server:break(?MODULE, TestCase, Comment); 570 false -> 571 {error,'test case not running'} 572 end; 573 {ok,{_,TestCase}} -> 574 test_server:break(?MODULE, TestCase, Comment); 575 Error = {error,_} -> 576 Error; 577 Error -> 578 {error,Error} 579 end 580 end. 581 582continue() -> 583 test_server:continue(). 584 585continue(TestCase) -> 586 test_server:continue(TestCase). 587 588 589remaining_test_procs() -> 590 ct_util:remaining_test_procs(). 591