diff options
Diffstat (limited to 'lib/common_test/src/ct_util.erl')
| -rw-r--r-- | lib/common_test/src/ct_util.erl | 211 |
1 files changed, 122 insertions, 89 deletions
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 4d3a2ae7e3..d8fd401a64 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ %% %CopyrightEnd% %% -%%% @doc Common Test Framework Utilities. +%%% Common Test Framework Utilities. %%% -%%% <p>This is a support module for the Common Test Framework. It +%%% This is a support module for the Common Test Framework. It %%% implements the process ct_util_server which acts like a data -%%% holder for suite, configuration and connection data.</p> +%%% holder for suite, configuration and connection data. %%% -module(ct_util). @@ -65,6 +65,9 @@ -export([warn_duplicates/1]). +-export([mark_process/0, mark_process/1, is_marked/1, is_marked/2, + remaining_test_procs/0]). + -export([get_profile_data/0, get_profile_data/1, get_profile_data/2, open_url/3]). @@ -80,20 +83,20 @@ %%%----------------------------------------------------------------- start() -> start(normal, ".", ?default_verbosity). -%%% @spec start(Mode) -> Pid | exit(Error) +%%% -spec start(Mode) -> Pid | exit(Error) %%% Mode = normal | interactive %%% Pid = pid() %%% -%%% @doc Start start the ct_util_server process +%%% Start start the ct_util_server process %%% (tool-internal use only). %%% -%%% <p>This function is called from ct_run.erl. It starts and initiates -%%% the <code>ct_util_server</code></p> +%%% This function is called from ct_run.erl. It starts and initiates +%%% the ct_util_server %%% -%%% <p>Returns the process identity of the -%%% <code>ct_util_server</code>.</p> +%%% Returns the process identity of the +%%% ct_util_server. %%% -%%% @see ct +%%% See ct. start(LogDir) when is_list(LogDir) -> start(normal, LogDir, ?default_verbosity); start(Mode) -> @@ -126,6 +129,7 @@ start(Mode, LogDir, Verbosity) -> do_start(Parent, Mode, LogDir, Verbosity) -> process_flag(trap_exit,true), register(ct_util_server,self()), + mark_process(), create_table(?conn_table,#conn.handle), create_table(?board_table,2), create_table(?suite_table,#suite_data.key), @@ -201,22 +205,14 @@ do_start(Parent, Mode, LogDir, Verbosity) -> ok -> Parent ! {self(),started}; {fail,CTHReason} -> - ErrorInfo = if is_atom(CTHReason) -> - io_lib:format("{~p,~p}", - [CTHReason, - erlang:get_stacktrace()]); - true -> - CTHReason - end, - ct_logs:tc_print('Suite Callback',ErrorInfo,[]), + ct_logs:tc_print('Suite Callback',CTHReason,[]), self() ! {{stop,{self(),{user_error,CTHReason}}}, {Parent,make_ref()}} catch - _:CTHReason -> + _:CTHReason:StackTrace -> ErrorInfo = if is_atom(CTHReason) -> - io_lib:format("{~p,~p}", - [CTHReason, - erlang:get_stacktrace()]); + io_lib:format("{~tp,~tp}", + [CTHReason, StackTrace]); true -> CTHReason end, @@ -497,7 +493,7 @@ loop(Mode,TestData,StartDir) -> ?MAX_IMPORTANCE, "CT Error Notification", "Connection process died: " - "Pid: ~w, Address: ~p, " + "Pid: ~w, Address: ~tp, " "Callback: ~w\n" "Reason: ~ts\n\n", [Pid,A,CB,ErrorHtml]), @@ -508,7 +504,7 @@ loop(Mode,TestData,StartDir) -> _ -> %% Let process crash in case of error, this shouldn't happen! io:format("\n\nct_util_server got EXIT " - "from ~w: ~p\n\n", [Pid,Reason]), + "from ~w: ~tp\n\n", [Pid,Reason]), ok = file:set_cwd(StartDir), exit(Reason) end @@ -524,19 +520,19 @@ get_key_from_name(Name)-> ct_config:get_key_from_name(Name). %%%----------------------------------------------------------------- -%%% @spec register_connection(TargetName,Address,Callback,Handle) -> +%%% -spec register_connection(TargetName,Address,Callback,Handle) -> %%% ok | {error,Reason} %%% TargetName = ct:target_name() %%% Address = term() %%% Callback = atom() %%% Handle = term %%% -%%% @doc Register a new connection (tool-internal use only). +%%% Register a new connection (tool-internal use only). %%% -%%% <p>This function can be called when a new connection is +%%% This function can be called when a new connection is %%% established. The connection data is stored in the connection %%% table, and ct_util will close all registered connections when the -%%% test is finished by calling <code>Callback:close/1</code>.</p> +%%% test is finished by calling Callback:close/1. register_connection(TargetName,Address,Callback,Handle) -> %% If TargetName is a registered alias for a config %% variable, use it as reference for the connection, @@ -557,28 +553,28 @@ register_connection(TargetName,Address,Callback,Handle) -> ok. %%%----------------------------------------------------------------- -%%% @spec unregister_connection(Handle) -> ok +%%% -spec unregister_connection(Handle) -> ok %%% Handle = term %%% -%%% @doc Unregister a connection (tool-internal use only). +%%% Unregister a connection (tool-internal use only). %%% -%%% <p>This function should be called when a registered connection is +%%% This function should be called when a registered connection is %%% closed. It removes the connection data from the connection -%%% table.</p> +%%% table. unregister_connection(Handle) -> ets:delete(?conn_table,Handle), ok. %%%----------------------------------------------------------------- -%%% @spec does_connection_exist(TargetName,Address,Callback) -> +%%% -spec does_connection_exist(TargetName,Address,Callback) -> %%% {ok,Handle} | false %%% TargetName = ct:target_name() %%% Address = address %%% Callback = atom() %%% Handle = term() %%% -%%% @doc Check if a connection already exists. +%%% Check if a connection already exists. does_connection_exist(TargetName,Address,Callback) -> case ct_config:get_key_from_name(TargetName) of {ok,_Key} -> @@ -598,7 +594,7 @@ does_connection_exist(TargetName,Address,Callback) -> end. %%%----------------------------------------------------------------- -%%% @spec get_connection(TargetName,Callback) -> +%%% -spec get_connection(TargetName,Callback) -> %%% {ok,Connection} | {error,Reason} %%% TargetName = ct:target_name() %%% Callback = atom() @@ -606,8 +602,8 @@ does_connection_exist(TargetName,Address,Callback) -> %%% Handle = term() %%% Address = term() %%% -%%% @doc Return the connection for <code>Callback</code> on the -%%% given target (<code>TargetName</code>). +%%% Return the connection for Callback on the +%%% given target (TargetName). get_connection(TargetName,Callback) -> %% check that TargetName is a registered alias case ct_config:get_key_from_name(TargetName) of @@ -628,7 +624,7 @@ get_connection(TargetName,Callback) -> end. %%%----------------------------------------------------------------- -%%% @spec get_connections(ConnPid) -> +%%% -spec get_connections(ConnPid) -> %%% {ok,Connections} | {error,Reason} %%% Connections = [Connection] %%% Connection = {TargetName,Handle,Callback,Address} @@ -637,8 +633,8 @@ get_connection(TargetName,Callback) -> %%% Callback = atom() %%% Address = term() %%% -%%% @doc Get data for all connections associated with a particular -%%% connection pid (see Callback:init/3). +%%% Get data for all connections associated with a particular +%%% connection pid (see Callback:init/3). get_connections(ConnPid) -> Conns = ets:tab2list(?conn_table), lists:flatmap(fun(#conn{targetref=TargetName, @@ -658,8 +654,7 @@ get_connections(ConnPid) -> end, Conns). %%%----------------------------------------------------------------- -%%% @hidden -%%% @equiv ct:get_target_name/1 +%%% Equivalent to ct:get_target_name/1 get_target_name(Handle) -> case ets:select(?conn_table,[{#conn{handle=Handle,targetref='$1',_='_'}, [], @@ -671,17 +666,14 @@ get_target_name(Handle) -> end. %%%----------------------------------------------------------------- -%%% @spec close_connections() -> ok +%%% -spec close_connections() -> ok %%% -%%% @doc Close all open connections. +%%% Close all open connections. close_connections() -> close_connections(ets:tab2list(?conn_table)), ok. %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc override_silence_all_connections() -> Protocols = [telnet,ftp,rpc,snmp,ssh], override_silence_connections(Protocols), @@ -742,12 +734,12 @@ reset_silent_connections() -> %%%----------------------------------------------------------------- -%%% @spec stop(Info) -> ok +%%% -spec stop(Info) -> ok %%% -%%% @doc Stop the ct_util_server and close all existing connections +%%% Stop the ct_util_server and close all existing connections %%% (tool-internal use only). %%% -%%% @see ct +%%% See ct. stop(Info) -> case whereis(ct_util_server) of undefined -> @@ -761,26 +753,25 @@ stop(Info) -> end. %%%----------------------------------------------------------------- -%%% @spec update_last_run_index() -> ok +%%% -spec update_last_run_index() -> ok %%% -%%% @doc Update <code>ct_run.<timestamp>/index.html</code> +%%% Update ct_run.<timestamp>/index.html %%% (tool-internal use only). update_last_run_index() -> call(update_last_run_index). %%%----------------------------------------------------------------- -%%% @spec get_mode() -> Mode +%%% -spec get_mode() -> Mode %%% Mode = normal | interactive %%% -%%% @doc Return the current mode of the ct_util_server +%%% Return the current mode of the ct_util_server %%% (tool-internal use only). get_mode() -> call(get_mode). %%%----------------------------------------------------------------- -%%% @hidden -%%% @equiv ct:listenv/1 +%%% Equivalent to ct:listenv/1 listenv(Telnet) -> case ct_telnet:send(Telnet,"listenv") of ok -> @@ -794,33 +785,32 @@ listenv(Telnet) -> end. %%%----------------------------------------------------------------- -%%% @hidden -%%% @equiv ct:parse_table/1 +%%% Equivalent to ct:parse_table/1 parse_table(Data) -> {Heading, Rest} = get_headings(Data), Lines = parse_row(Rest,[],size(Heading)), {Heading,Lines}. get_headings(["|" ++ Headings | Rest]) -> - {remove_space(string:tokens(Headings, "|"),[]), Rest}; + {remove_space(string:lexemes(Headings, "|"),[]), Rest}; get_headings([_ | Rest]) -> get_headings(Rest); get_headings([]) -> {{},[]}. parse_row(["|" ++ _ = Row | T], Rows, NumCols) when NumCols > 1 -> - case string:tokens(Row, "|") of + case string:lexemes(Row, "|") of Values when length(Values) =:= NumCols -> parse_row(T,[remove_space(Values,[])|Rows], NumCols); Values when length(Values) < NumCols -> parse_row([Row ++"\n"++ hd(T) | tl(T)], Rows, NumCols) end; -parse_row(["|" ++ _ = Row | T], Rows, 1 = NumCols) -> - case string:rchr(Row, $|) of - 1 -> +parse_row(["|" ++ X = Row | T], Rows, 1 = NumCols) -> + case string:find(X, [$|]) of + nomatch -> parse_row([Row ++"\n"++hd(T) | tl(T)], Rows, NumCols); _Else -> - parse_row(T, [remove_space(string:tokens(Row,"|"),[])|Rows], + parse_row(T, [remove_space(string:lexemes(Row,"|"),[])|Rows], NumCols) end; parse_row([_Skip | T], Rows, NumCols) -> @@ -829,22 +819,16 @@ parse_row([], Rows, _NumCols) -> lists:reverse(Rows). remove_space([Str|Rest],Acc) -> - remove_space(Rest,[string:strip(string:strip(Str),both,$')|Acc]); + remove_space(Rest,[string:trim(string:trim(Str,both,[$\s]),both,[$'])|Acc]); remove_space([],Acc) -> list_to_tuple(lists:reverse(Acc)). %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc is_test_dir(Dir) -> - lists:last(string:tokens(filename:basename(Dir), "_")) == "test". + lists:last(string:lexemes(filename:basename(Dir), "_")) == "test". %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc get_testdir(Dir, all) -> Abs = abs_name(Dir), case is_test_dir(Abs) of @@ -888,9 +872,6 @@ get_testdir(Dir, _) -> get_testdir(Dir, all). %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc get_attached(TCPid) -> case dbg_iserver:safe_call({get_attpid,TCPid}) of {ok,AttPid} when is_pid(AttPid) -> @@ -900,9 +881,6 @@ get_attached(TCPid) -> end. %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc kill_attached(undefined,_AttPid) -> ok; kill_attached(_TCPid,undefined) -> @@ -917,9 +895,6 @@ kill_attached(TCPid,AttPid) -> %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc warn_duplicates(Suites) -> Warn = fun(Mod) -> @@ -938,9 +913,67 @@ warn_duplicates(Suites) -> ok. %%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc +mark_process() -> + mark_process(system). + +mark_process(Type) -> + put(ct_process_type, Type). + +is_marked(Pid) -> + is_marked(Pid, system). + +is_marked(Pid, Type) -> + case process_info(Pid, dictionary) of + {dictionary,List} -> + Type == proplists:get_value(ct_process_type, List); + undefined -> + false + end. + +remaining_test_procs() -> + Procs = processes(), + {SharedGL,OtherGLs,Procs2} = + lists:foldl( + fun(Pid, ProcTypes = {Shared,Other,Procs1}) -> + case is_marked(Pid, group_leader) of + true -> + if not is_pid(Shared) -> + case test_server_io:get_gl(true) of + Pid -> + {Pid,Other, + lists:delete(Pid,Procs1)}; + _ -> + {Shared,[Pid|Other],Procs1} + end; + true -> % SharedGL already found + {Shared,[Pid|Other],Procs1} + end; + false -> + case is_marked(Pid) of + true -> + {Shared,Other,lists:delete(Pid,Procs1)}; + false -> + ProcTypes + end + end + end, {undefined,[],Procs}, Procs), + + AllGLs = [SharedGL | OtherGLs], + TestProcs = + lists:flatmap(fun(Pid) -> + case process_info(Pid, group_leader) of + {group_leader,GL} -> + case lists:member(GL, AllGLs) of + true -> [{Pid,GL}]; + false -> [] + end; + undefined -> + [] + end + end, Procs2), + {TestProcs, SharedGL, OtherGLs}. + +%%%----------------------------------------------------------------- get_profile_data() -> get_profile_data(all). @@ -984,12 +1017,12 @@ get_profile_data(Profile, Key, StartDir) -> end, case Result of {error,enoent} when Profile /= default -> - io:format(?def_gl, "~nERROR! Missing profile file ~p~n", [File]), + io:format(?def_gl, "~nERROR! Missing profile file ~tp~n", [File]), undefined; {error,enoent} when Profile == default -> undefined; {error,Reason} -> - io:format(?def_gl,"~nERROR! Error in profile file ~p: ~p~n", + io:format(?def_gl,"~nERROR! Error in profile file ~tp: ~tp~n", [WhichFile,Reason]), undefined; {ok,Data} -> @@ -1000,7 +1033,7 @@ get_profile_data(Profile, Key, StartDir) -> Data; _ -> io:format(?def_gl, - "~nERROR! Invalid profile data in ~p~n", + "~nERROR! Invalid profile data in ~tp~n", [WhichFile]), [] end, @@ -1084,7 +1117,7 @@ open_url(iexplore, Args, URL) -> _ = case win32reg:values(R) of {ok, Paths} -> Path = proplists:get_value(default, Paths), - [Cmd | _] = string:tokens(Path, "%"), + [Cmd | _] = string:lexemes(Path, "%"), Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL, io:format(?def_gl, "~nOpening ~ts with command:~n ~ts~n", [URL,Cmd1]), open_port({spawn,Cmd1}, []); |
