aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src/ct_util.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/ct_util.erl')
-rw-r--r--lib/common_test/src/ct_util.erl859
1 files changed, 239 insertions, 620 deletions
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index ba3d789f8d..3b6ad6f98d 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -30,15 +30,14 @@
-export([register_connection/4,unregister_connection/1,
does_connection_exist/3,get_key_from_name/1]).
--export([require/1, require/2, get_config/1, get_config/2, get_config/3,
- set_default_config/2, set_default_config/3, delete_default_config/1,
- get_all_config/0, update_config/2,
- release_allocated/0, close_connections/0]).
+-export([close_connections/0]).
--export([save_suite_data/3, save_suite_data/2, read_suite_data/1,
+-export([save_suite_data/3, save_suite_data/2,
+ save_suite_data_async/3, save_suite_data_async/2,
+ read_suite_data/1,
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1,
- update_testdata/2]).
+ set_testdata_async/1, update_testdata/2]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
@@ -46,7 +45,9 @@
silence_all_connections/0, silence_connections/1, is_silenced/1,
reset_silent_connections/0]).
--export([set_cwd/1, reset_cwd/0]).
+-export([get_mode/0, create_table/3, read_opts/0]).
+
+-export([set_cwd/1, reset_cwd/0, get_start_dir/0]).
-export([parse_table/1]).
@@ -56,23 +57,18 @@
-export([is_test_dir/1, get_testdir/2]).
--export([encrypt_config_file/2, encrypt_config_file/3,
- decrypt_config_file/2, decrypt_config_file/3]).
-
--export([kill_attached/2, get_attached/1]).
+-export([kill_attached/2, get_attached/1, ct_make_ref/0]).
-export([warn_duplicates/1]).
+-export([get_profile_data/0, get_profile_data/1,
+ get_profile_data/2, open_url/3]).
+
-include("ct_event.hrl").
-include("ct_util.hrl").
--record(ct_conf,{key,value,ref,name='_UNDEF',default=false}).
-%% default = {true,suite} | {true,testcase} | false
-
-record(suite_data, {key,name,value}).
--define(cryptfile, ".ct_config.crypt").
-
%%%-----------------------------------------------------------------
%%% @spec start(Mode) -> Pid | exit(Error)
%%% Mode = normal | interactive
@@ -103,7 +99,8 @@ start(Mode,LogDir) ->
Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end),
receive
{Pid,started} -> Pid;
- {Pid,Error} -> exit(Error)
+ {Pid,Error} -> exit(Error);
+ {_Ref,{Pid,Error}} -> exit(Error)
end;
Pid ->
case get_mode() of
@@ -119,7 +116,6 @@ start(Mode,LogDir) ->
do_start(Parent,Mode,LogDir) ->
process_flag(trap_exit,true),
register(ct_util_server,self()),
- create_table(?attr_table,bag,#ct_conf.key),
create_table(?conn_table,#conn.handle),
create_table(?board_table,2),
create_table(?suite_table,#suite_data.key),
@@ -128,12 +124,13 @@ do_start(Parent,Mode,LogDir) ->
ok -> ok;
E -> exit(E)
end,
+ DoExit = fun(Reason) -> file:set_cwd(StartDir), exit(Reason) end,
Opts = case read_opts() of
{ok,Opts1} ->
Opts1;
Error ->
Parent ! {self(),Error},
- exit(Error)
+ DoExit(Error)
end,
%% start an event manager (if not already started by master)
@@ -148,38 +145,55 @@ do_start(Parent,Mode,LogDir) ->
ct_event:add_handler([{vts,VtsPid}])
end
end,
- case read_config_files(Opts) of
- ok ->
- %% add user handlers
- case lists:keysearch(event_handler,1,Opts) of
- {value,{_,Handlers}} ->
- Add = fun({H,Args}) ->
- case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of
- ok -> ok;
- {'EXIT',Why} -> exit(Why);
- Other -> exit({event_handler,Other})
- end
- end,
- case catch lists:foreach(Add,Handlers) of
- {'EXIT',Reason} ->
- Parent ! {self(),Reason};
- _ ->
- ok
- end;
- false ->
+
+ %% start ct_config server
+ try ct_config:start(Mode) of
+ _ -> ok
+ catch
+ _Class:CfgError ->
+ DoExit(CfgError)
+ end,
+
+ %% add user event handlers
+ case lists:keysearch(event_handler,1,Opts) of
+ {value,{_,Handlers}} ->
+ Add = fun({H,Args}) ->
+ case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of
+ ok -> ok;
+ {'EXIT',Why} -> DoExit(Why);
+ Other -> DoExit({event_handler,Other})
+ end
+ end,
+ case catch lists:foreach(Add,Handlers) of
+ {'EXIT',Reason} ->
+ Parent ! {self(),Reason};
+ _ ->
ok
- end,
- {StartTime,TestLogDir} = ct_logs:init(Mode),
- ct_event:notify(#event{name=test_start,
- node=node(),
- data={StartTime,
- lists:flatten(TestLogDir)}}),
- Parent ! {self(),started},
- loop(Mode,[],StartDir);
- ReadError ->
- Parent ! {self(),ReadError},
- exit(ReadError)
- end.
+ end;
+ false ->
+ ok
+ end,
+ {StartTime,TestLogDir} = ct_logs:init(Mode),
+
+ ct_event:notify(#event{name=test_start,
+ node=node(),
+ data={StartTime,
+ lists:flatten(TestLogDir)}}),
+ %% Initialize ct_hooks
+ try ct_hooks:init(Opts) of
+ ok ->
+ Parent ! {self(),started};
+ {fail,CTHReason} ->
+ ct_logs:tc_print('Suite Callback',CTHReason,[]),
+ self() ! {{stop,{self(),{user_error,CTHReason}}},
+ {Parent,make_ref()}}
+ catch
+ _:CTHReason ->
+ ct_logs:tc_print('Suite Callback',CTHReason,[]),
+ self() ! {{stop,{self(),{user_error,CTHReason}}},
+ {Parent,make_ref()}}
+ end,
+ loop(Mode,[],StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -197,105 +211,6 @@ read_opts() ->
{error,{bad_installation,Error}}
end.
-read_config_files(Opts) ->
- ConfigFiles =
- lists:foldl(fun({config,Files},Acc) ->
- Acc ++ Files;
- (_,Acc) ->
- Acc
- end,[],Opts),
- read_config_files1(ConfigFiles).
-
-read_config_files1([ConfigFile|Files]) ->
- case file:consult(ConfigFile) of
- {ok,Config} ->
- set_config(Config),
- read_config_files1(Files);
- {error,enoent} ->
- {user_error,{config_file_error,ConfigFile,enoent}};
- {error,Reason} ->
- Key =
- case application:get_env(common_test, decrypt) of
- {ok,KeyOrFile} ->
- case KeyOrFile of
- {key,K} ->
- K;
- {file,F} ->
- get_crypt_key_from_file(F)
- end;
- _ ->
- get_crypt_key_from_file()
- end,
- case Key of
- {error,no_crypt_file} ->
- {user_error,{config_file_error,ConfigFile,Reason}};
- {error,CryptError} ->
- {user_error,{decrypt_file_error,ConfigFile,CryptError}};
- _ when is_list(Key) ->
- case decrypt_config_file(ConfigFile, undefined, {key,Key}) of
- {ok,CfgBin} ->
- case read_config_terms(CfgBin) of
- {error,ReadFail} ->
- {user_error,{config_file_error,ConfigFile,ReadFail}};
- Config ->
- set_config(Config),
- read_config_files1(Files)
- end;
- {error,DecryptFail} ->
- {user_error,{decrypt_config_error,ConfigFile,DecryptFail}}
- end;
- _ ->
- {user_error,{bad_decrypt_key,ConfigFile,Key}}
- end
- end;
-read_config_files1([]) ->
- ok.
-
-read_config_terms(Bin) when is_binary(Bin) ->
- case catch binary_to_list(Bin) of
- {'EXIT',_} ->
- {error,invalid_textfile};
- Lines ->
- read_config_terms(Lines)
- end;
-read_config_terms(Lines) when is_list(Lines) ->
- read_config_terms1(erl_scan:tokens([], Lines, 0), 1, [], []).
-
-read_config_terms1({done,{ok,Ts,EL},Rest}, L, Terms, _) ->
- case erl_parse:parse_term(Ts) of
- {ok,Term} when Rest == [] ->
- lists:reverse([Term|Terms]);
- {ok,Term} ->
- read_config_terms1(erl_scan:tokens([], Rest, 0),
- EL+1, [Term|Terms], Rest);
- _ ->
- {error,{bad_term,{L,EL}}}
- end;
-read_config_terms1({done,{eof,_},_}, _, Terms, Rest) when Rest == [] ->
- lists:reverse(Terms);
-read_config_terms1({done,{eof,EL},_}, L, _, _) ->
- {error,{bad_term,{L,EL}}};
-read_config_terms1({done,{error,Info,EL},_}, L, _, _) ->
- {error,{Info,{L,EL}}};
-read_config_terms1({more,_}, L, Terms, Rest) ->
- case string:tokens(Rest, [$\n,$\r,$\t]) of
- [] ->
- lists:reverse(Terms);
- _ ->
- {error,{bad_term,L}}
- end.
-
-set_default_config(NewConfig, Scope) ->
- call({set_default_config, {NewConfig, Scope}}).
-
-set_default_config(Name, NewConfig, Scope) ->
- call({set_default_config, {Name, NewConfig, Scope}}).
-
-delete_default_config(Scope) ->
- call({delete_default_config, Scope}).
-
-update_config(Name, Config) ->
- call({update_config, {Name, Config}}).
save_suite_data(Key, Value) ->
call({save_suite_data, {Key, undefined, Value}}).
@@ -303,6 +218,12 @@ save_suite_data(Key, Value) ->
save_suite_data(Key, Name, Value) ->
call({save_suite_data, {Key, Name, Value}}).
+save_suite_data_async(Key, Value) ->
+ save_suite_data_async(Key, undefined, Value).
+
+save_suite_data_async(Key, Name, Value) ->
+ cast({save_suite_data, {Key, Name, Value}}).
+
read_suite_data(Key) ->
call({read_suite_data, Key}).
@@ -327,6 +248,9 @@ update_testdata(Key, Fun) ->
set_testdata(TestData) ->
call({set_testdata, TestData}).
+set_testdata_async(TestData) ->
+ cast({set_testdata, TestData}).
+
get_testdata(Key) ->
call({get_testdata, Key}).
@@ -336,32 +260,15 @@ set_cwd(Dir) ->
reset_cwd() ->
call(reset_cwd).
+get_start_dir() ->
+ call(get_start_dir).
+
loop(Mode,TestData,StartDir) ->
receive
{update_last_run_index,From} ->
ct_logs:make_last_run_index(),
return(From,ok),
loop(Mode,TestData,StartDir);
- {{require,Name,Tag,SubTags},From} ->
- Result = do_require(Name,Tag,SubTags),
- return(From,Result),
- loop(Mode,TestData,StartDir);
- {{set_default_config,{Config,Scope}},From} ->
- set_config(Config,{true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{set_default_config,{Name,Config,Scope}},From} ->
- set_config(Name,Config,{true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{delete_default_config,Scope},From} ->
- delete_config({true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{update_config,{Name,NewConfig}},From} ->
- update_conf(Name,NewConfig),
- return(From,ok),
- loop(Mode,TestData,StartDir);
{{save_suite_data,{Key,Name,Value}},From} ->
ets:insert(?suite_table, #suite_data{key=Key,
name=Name,
@@ -403,6 +310,9 @@ loop(Mode,TestData,StartDir) ->
TestData1 = lists:keydelete(Key,1,TestData),
return(From,ok),
loop(Mode,[New|TestData1],StartDir);
+ {{get_testdata, all}, From} ->
+ return(From, TestData),
+ loop(From, TestData, StartDir);
{{get_testdata,Key},From} ->
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
@@ -429,20 +339,30 @@ loop(Mode,TestData,StartDir) ->
{reset_cwd,From} ->
return(From,file:set_cwd(StartDir)),
loop(From,TestData,StartDir);
- {{stop,How},From} ->
+ {get_start_dir,From} ->
+ return(From,StartDir),
+ loop(From,TestData,StartDir);
+ {{stop,Info},From} ->
Time = calendar:local_time(),
ct_event:sync_notify(#event{name=test_done,
node=node(),
data=Time}),
- ets:delete(?attr_table),
+ Callbacks = ets:lookup_element(?suite_table,
+ ct_hooks,
+ #suite_data.value),
+ ct_hooks:terminate(Callbacks),
close_connections(ets:tab2list(?conn_table)),
ets:delete(?conn_table),
ets:delete(?board_table),
ets:delete(?suite_table),
- ct_logs:close(How),
- file:set_cwd(StartDir),
+ ct_logs:close(Info, StartDir),
ct_event:stop(),
- return(From,ok);
+ ct_config:stop(),
+ file:set_cwd(StartDir),
+ return(From, Info);
+ {Ref, _Msg} when is_reference(Ref) ->
+ %% This clause is used when doing cast operations.
+ loop(Mode,TestData,StartDir);
{get_mode,From} ->
return(From,Mode),
loop(Mode,TestData,StartDir);
@@ -463,6 +383,8 @@ close_connections([#conn{handle=Handle,callback=CB}|Conns]) ->
close_connections([]) ->
ok.
+get_key_from_name(Name)->
+ ct_config:get_key_from_name(Name).
%%%-----------------------------------------------------------------
%%% @spec register_connection(TargetName,Address,Callback,Handle) ->
@@ -480,7 +402,7 @@ close_connections([]) ->
%%% test is finished by calling <code>Callback:close/1</code>.</p>
register_connection(TargetName,Address,Callback,Handle) ->
TargetRef =
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,Ref} ->
Ref;
_ ->
@@ -518,7 +440,7 @@ unregister_connection(Handle) ->
%%%
%%% @doc Check if a connection already exists.
does_connection_exist(TargetName,Address,Callback) ->
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,TargetRef} ->
case ets:select(?conn_table,[{#conn{handle='$1',
targetref=TargetRef,
@@ -548,7 +470,7 @@ does_connection_exist(TargetName,Address,Callback) ->
%%% @doc Return all connections for the <code>Callback</code> on the
%%% given target (<code>TargetName</code>).
get_connections(TargetName,Callback) ->
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,Ref} ->
{ok,ets:select(?conn_table,[{#conn{handle='$1',
address='$2',
@@ -568,250 +490,11 @@ get_target_name(ConnPid) ->
[],
['$1']}]) of
[TargetRef] ->
- get_name_from_ref(TargetRef);
+ ct_config:get_name_from_ref(TargetRef);
[] ->
{error,{unknown_connection,ConnPid}}
end.
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:require/1
-require(Key) when is_atom(Key) ->
- require({Key,[]});
-require({Key,SubKeys}) when is_atom(Key) ->
- allocate('_UNDEF',Key,to_list(SubKeys));
-require(Key) ->
- {error,{invalid,Key}}.
-
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:require/2
-require(Name,Key) when is_atom(Key) ->
- require(Name,{Key,[]});
-require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) ->
- call({require,Name,Key,to_list(SubKeys)});
-require(Name,Keys) ->
- {error,{invalid,{Name,Keys}}}.
-
-to_list(X) when is_list(X) -> X;
-to_list(X) -> [X].
-
-do_require(Name,Key,SubKeys) when is_list(SubKeys) ->
- case get_key_from_name(Name) of
- {error,_} ->
- allocate(Name,Key,SubKeys);
- {ok,Key} ->
- %% already allocated - check that it has all required subkeys
- Vals = [Val || {_Ref,Val} <- lookup_name(Name)],
- case get_subconfig(SubKeys,Vals) of
- {ok,_SubMapped} ->
- ok;
- Error ->
- Error
- end;
- {ok,OtherKey} ->
- {error,{name_in_use,Name,OtherKey}}
- end.
-
-allocate(Name,Key,SubKeys) ->
- case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
- [] ->
- {error,{not_available,Key}};
- Available ->
- case allocate_subconfig(Name,SubKeys,Available,false) of
- ok ->
- ok;
- Error ->
- Error
- end
- end.
-
-allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,_SubMapped} ->
- ets:insert(?attr_table,C#ct_conf{name=Name}),
- allocate_subconfig(Name,SubKeys,Rest,true);
- _Error ->
- allocate_subconfig(Name,SubKeys,Rest,Found)
- end;
-allocate_subconfig(_Name,_SubKeys,[],true) ->
- ok;
-allocate_subconfig(_Name,SubKeys,[],false) ->
- {error,{not_available,SubKeys}}.
-
-
-
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/1
-get_config(KeyOrName) ->
- get_config(KeyOrName,undefined,[]).
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/2
-get_config(KeyOrName,Default) ->
- get_config(KeyOrName,Default,[]).
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/3
-get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- [{_Ref,Val}|_] = Vals ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)];
- {true,false} ->
- [V || {_R,V} <- lists:sort(Vals)];
- {false,true} ->
- {KeyOrName,Val};
- {false,false} ->
- Val
- end
- end;
-
-get_config({KeyOrName,SubKey},Default,Opts) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- Vals ->
- Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of
- Result=[L|_] when is_list(L) ->
- case L of
- [{_,_}|_] ->
- Result;
- _ ->
- []
- end;
- _ ->
- []
- end,
- case get_subconfig([SubKey],Vals1,[],Opts) of
- {ok,[{_,SubVal}|_]=SubVals} ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals];
- {true,false} ->
- [Val || {_SubKey,Val} <- SubVals];
- {false,true} ->
- {{KeyOrName,SubKey},SubVal};
- {false,false} ->
- SubVal
- end;
- _ ->
- Default
- end
- end.
-
-
-get_subconfig(SubKeys,Values) ->
- get_subconfig(SubKeys,Values,[],[]).
-
-get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,SubMapped} ->
- case lists:member(all,Opts) of
- true ->
- get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts);
- false ->
- {ok,SubMapped}
- end;
- _Error ->
- get_subconfig(SubKeys,Rest,Mapped,Opts)
- end;
-get_subconfig(SubKeys,[],[],_) ->
- {error,{not_available,SubKeys}};
-get_subconfig(_SubKeys,[],Mapped,_) ->
- {ok,Mapped}.
-
-do_get_config([Key|Required],Available,Mapped) ->
- case lists:keysearch(Key,1,Available) of
- {value,{Key,Value}} ->
- NewAvailable = lists:keydelete(Key,1,Available),
- NewMapped = [{Key,Value}|Mapped],
- do_get_config(Required,NewAvailable,NewMapped);
- false ->
- {error,{not_available,Key}}
- end;
-do_get_config([],_Available,Mapped) ->
- {ok,lists:reverse(Mapped)}.
-
-get_all_config() ->
- ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
- default='$4',_='_'},
- [],
- [{{'$1','$2','$3','$4'}}]}]).
-
-lookup_config(KeyOrName) ->
- case lookup_name(KeyOrName) of
- [] ->
- lookup_key(KeyOrName);
- Values ->
- Values
- end.
-
-lookup_name(Name) ->
- ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
- [],
- [{{'$1','$2'}}]}]).
-lookup_key(Key) ->
- ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
- [],
- [{{'$1','$2'}}]}]).
-
-set_config(Config) ->
- set_config('_UNDEF',Config,false).
-
-set_config(Config,Default) ->
- set_config('_UNDEF',Config,Default).
-
-set_config(Name,Config,Default) ->
- [ets:insert(?attr_table,
- #ct_conf{key=Key,value=Val,ref=ct_make_ref(),
- name=Name,default=Default}) ||
- {Key,Val} <- Config].
-
-delete_config(Default) ->
- ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}),
- ok.
-
-
-%%%-----------------------------------------------------------------
-%%% @spec release_allocated() -> ok
-%%%
-%%% @doc Release all allocated resources, but don't take down any
-%%% connections.
-release_allocated() ->
- Allocated = ets:select(?attr_table,[{#ct_conf{name='$1',_='_'},
- [{'=/=','$1','_UNDEF'}],
- ['$_']}]),
- release_allocated(Allocated).
-release_allocated([H|T]) ->
- ets:delete_object(?attr_table,H),
- ets:insert(?attr_table,H#ct_conf{name='_UNDEF'}),
- release_allocated(T);
-release_allocated([]) ->
- ok.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-update_conf(Name, NewConfig) ->
- Old = ets:select(?attr_table,[{#ct_conf{name=Name,_='_'},[],['$_']}]),
- lists:foreach(fun(OldElem) ->
- NewElem = OldElem#ct_conf{value=NewConfig},
- ets:delete_object(?attr_table, OldElem),
- ets:insert(?attr_table, NewElem)
- end, Old),
- ok.
-
%%%-----------------------------------------------------------------
%%% @spec close_connections() -> ok
%%%
@@ -879,16 +562,16 @@ reset_silent_connections() ->
%%%-----------------------------------------------------------------
-%%% @spec stop(How) -> ok
+%%% @spec stop(Info) -> ok
%%%
%%% @doc Stop the ct_util_server and close all existing connections
%%% (tool-internal use only).
%%%
%%% @see ct
-stop(How) ->
+stop(Info) ->
case whereis(ct_util_server) of
undefined -> ok;
- _ -> call({stop,How})
+ _ -> call({stop,Info})
end.
%%%-----------------------------------------------------------------
@@ -928,10 +611,37 @@ listenv(Telnet) ->
%%% @hidden
%%% @equiv ct:parse_table/1
parse_table(Data) ->
- [Heading|Lines]=
- [remove_space(string:tokens(L, "|"),[]) || L <- Data, hd(L)==$|],
+ {Heading, Rest} = get_headings(Data),
+ Lines = parse_row(Rest,[],size(Heading)),
{Heading,Lines}.
+get_headings(["|" ++ Headings | Rest]) ->
+ {remove_space(string:tokens(Headings, "|"),[]), Rest};
+get_headings([_ | Rest]) ->
+ get_headings(Rest);
+get_headings([]) ->
+ {{},[]}.
+
+parse_row(["|" ++ _ = Row | T], Rows, NumCols) when NumCols > 1 ->
+ case string:tokens(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([Row ++"\n"++hd(T) | tl(T)], Rows, NumCols);
+ _Else ->
+ parse_row(T, [remove_space(string:tokens(Row,"|"),[])|Rows],
+ NumCols)
+ end;
+parse_row([_Skip | T], Rows, NumCols) ->
+ parse_row(T, Rows, NumCols);
+parse_row([], Rows, _NumCols) ->
+ lists:reverse(Rows).
+
remove_space([Str|Rest],Acc) ->
remove_space(Rest,[string:strip(string:strip(Str),both,$')|Acc]);
remove_space([],Acc) ->
@@ -995,167 +705,6 @@ get_testdir(Dir, _) ->
%%% @spec
%%%
%%% @doc
-encrypt_config_file(SrcFileName, EncryptFileName) ->
- case get_crypt_key_from_file() of
- {error,_} = E ->
- E;
- Key ->
- encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
- end.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) ->
- case get_crypt_key_from_file(KeyFile) of
- {error,_} = E ->
- E;
- Key ->
- encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
- end;
-
-encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) ->
- crypto:start(),
- {K1,K2,K3,IVec} = make_crypto_key(Key),
- case file:read_file(SrcFileName) of
- {ok,Bin0} ->
- Bin1 = term_to_binary({SrcFileName,Bin0}),
- Bin2 = case byte_size(Bin1) rem 8 of
- 0 -> Bin1;
- N -> list_to_binary([Bin1,random_bytes(8-N)])
- end,
- EncBin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin2),
- case file:write_file(EncryptFileName, EncBin) of
- ok ->
- io:format("~s --(encrypt)--> ~s~n",
- [SrcFileName,EncryptFileName]),
- ok;
- {error,Reason} ->
- {error,{Reason,EncryptFileName}}
- end;
- {error,Reason} ->
- {error,{Reason,SrcFileName}}
- end.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-decrypt_config_file(EncryptFileName, TargetFileName) ->
- case get_crypt_key_from_file() of
- {error,_} = E ->
- E;
- Key ->
- decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
- end.
-
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) ->
- case get_crypt_key_from_file(KeyFile) of
- {error,_} = E ->
- E;
- Key ->
- decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
- end;
-
-decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) ->
- crypto:start(),
- {K1,K2,K3,IVec} = make_crypto_key(Key),
- case file:read_file(EncryptFileName) of
- {ok,Bin} ->
- DecBin = crypto:des3_cbc_decrypt(K1, K2, K3, IVec, Bin),
- case catch binary_to_term(DecBin) of
- {'EXIT',_} ->
- {error,bad_file};
- {_SrcFile,SrcBin} ->
- case TargetFileName of
- undefined ->
- {ok,SrcBin};
- _ ->
- case file:write_file(TargetFileName, SrcBin) of
- ok ->
- io:format("~s --(decrypt)--> ~s~n",
- [EncryptFileName,TargetFileName]),
- ok;
- {error,Reason} ->
- {error,{Reason,TargetFileName}}
- end
- end
- end;
- {error,Reason} ->
- {error,{Reason,EncryptFileName}}
- end.
-
-
-get_crypt_key_from_file(File) ->
- case file:read_file(File) of
- {ok,Bin} ->
- case catch string:tokens(binary_to_list(Bin), [$\n,$\r]) of
- [Key] ->
- Key;
- _ ->
- {error,{bad_crypt_file,File}}
- end;
- {error,Reason} ->
- {error,{Reason,File}}
- end.
-
-get_crypt_key_from_file() ->
- CwdFile = filename:join(".",?cryptfile),
- {Result,FullName} =
- case file:read_file(CwdFile) of
- {ok,Bin} ->
- {Bin,CwdFile};
- _ ->
- case init:get_argument(home) of
- {ok,[[Home]]} ->
- HomeFile = filename:join(Home,?cryptfile),
- case file:read_file(HomeFile) of
- {ok,Bin} ->
- {Bin,HomeFile};
- _ ->
- {{error,no_crypt_file},noent}
- end;
- _ ->
- {{error,no_crypt_file},noent}
- end
- end,
- case FullName of
- noent ->
- Result;
- _ ->
- case catch string:tokens(binary_to_list(Result), [$\n,$\r]) of
- [Key] ->
- io:format("~nCrypt key file: ~s~n", [FullName]),
- Key;
- _ ->
- {error,{bad_crypt_file,FullName}}
- end
- end.
-
-make_crypto_key(String) ->
- <<K1:8/binary,K2:8/binary>> = First = erlang:md5(String),
- <<K3:8/binary,IVec:8/binary>> = erlang:md5([First|lists:reverse(String)]),
- {K1,K2,K3,IVec}.
-
-random_bytes(N) ->
- {A,B,C} = now(),
- random:seed(A, B, C),
- random_bytes_1(N, []).
-
-random_bytes_1(0, Acc) -> Acc;
-random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]).
-
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
get_attached(TCPid) ->
case dbg_iserver:safe_call({get_attpid,TCPid}) of
{ok,AttPid} when is_pid(AttPid) ->
@@ -1201,6 +750,79 @@ warn_duplicates(Suites) ->
lists:foreach(Warn, Suites),
ok.
+%%%-----------------------------------------------------------------
+%%% @spec
+%%%
+%%% @doc
+get_profile_data() ->
+ get_profile_data(all).
+
+get_profile_data(KeyOrStartDir) ->
+ if is_atom(KeyOrStartDir) ->
+ get_profile_data(KeyOrStartDir, get_start_dir());
+ is_list(KeyOrStartDir) ->
+ get_profile_data(all, KeyOrStartDir)
+ end.
+
+get_profile_data(Key, StartDir) ->
+ Profile = case application:get_env(common_test, profile) of
+ {ok,undefined} -> default;
+ {ok,Prof} -> Prof;
+ _ -> default
+ end,
+ get_profile_data(Profile, Key, StartDir).
+
+get_profile_data(Profile, Key, StartDir) ->
+ File = case Profile of
+ default ->
+ ?ct_profile_file;
+ _ when is_list(Profile) ->
+ ?ct_profile_file ++ "." ++ Profile;
+ _ when is_atom(Profile) ->
+ ?ct_profile_file ++ "." ++ atom_to_list(Profile)
+ end,
+ FullNameWD = filename:join(StartDir, File),
+ {WhichFile,Result} =
+ case file:consult(FullNameWD) of
+ {error,enoent} ->
+ case init:get_argument(home) of
+ {ok,[[HomeDir]]} ->
+ FullNameHome = filename:join(HomeDir, File),
+ {FullNameHome,file:consult(FullNameHome)};
+ _ ->
+ {File,{error,enoent}}
+ end;
+ Consulted ->
+ {FullNameWD,Consulted}
+ end,
+ case Result of
+ {error,enoent} when Profile /= default ->
+ io:format(user, "~nERROR! Missing profile file ~p~n", [File]),
+ undefined;
+ {error,enoent} when Profile == default ->
+ undefined;
+ {error,Reason} ->
+ io:format(user,"~nERROR! Error in profile file ~p: ~p~n",
+ [WhichFile,Reason]),
+ undefined;
+ {ok,Data} ->
+ Data1 = case Data of
+ [List] when is_list(List) ->
+ List;
+ _ when is_list(Data) ->
+ Data;
+ _ ->
+ io:format(user,
+ "~nERROR! Invalid profile data in ~p~n",
+ [WhichFile]),
+ []
+ end,
+ if Key == all ->
+ Data1;
+ true ->
+ proplists:get_value(Key, Data)
+ end
+ end.
%%%-----------------------------------------------------------------
%%% Internal functions
@@ -1210,7 +832,7 @@ call(Msg) ->
ct_util_server ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{ct_util_server_down,Reason}}
@@ -1219,6 +841,9 @@ call(Msg) ->
return({To,Ref},Result) ->
To ! {Ref, Result}.
+cast(Msg) ->
+ ct_util_server ! {Msg, {ct_util_server, make_ref()}}.
+
seconds(T) ->
test_server:seconds(T).
@@ -1244,37 +869,6 @@ ct_make_ref_loop(N) ->
From ! {self(),N},
ct_make_ref_loop(N+1)
end.
-
-get_ref_from_name(Name) ->
- case ets:select(?attr_table,[{#ct_conf{name=Name,ref='$1',_='_'},
- [],
- ['$1']}]) of
- [Ref] ->
- {ok,Ref};
- _ ->
- {error,{no_such_name,Name}}
- end.
-
-get_name_from_ref(Ref) ->
- case ets:select(?attr_table,[{#ct_conf{name='$1',ref=Ref,_='_'},
- [],
- ['$1']}]) of
- [Name] ->
- {ok,Name};
- _ ->
- {error,{no_such_ref,Ref}}
- end.
-
-get_key_from_name(Name) ->
- case ets:select(?attr_table,[{#ct_conf{name=Name,key='$1',_='_'},
- [],
- ['$1']}]) of
- [Key|_] ->
- {ok,Key};
- _ ->
- {error,{no_such_name,Name}}
- end.
-
abs_name(Dir0) ->
Abs = filename:absname(Dir0),
@@ -1301,3 +895,28 @@ abs_name2([H|T],Acc) ->
abs_name2(T,[H|Acc]);
abs_name2([],Acc) ->
filename:join(lists:reverse(Acc)).
+
+open_url(iexplore, Args, URL) ->
+ {ok,R} = win32reg:open([read]),
+ ok = win32reg:change_key(R,"applications\\iexplore.exe\\shell\\open\\command"),
+ case win32reg:values(R) of
+ {ok, Paths} ->
+ Path = proplists:get_value(default, Paths),
+ [Cmd | _] = string:tokens(Path, "%"),
+ Cmd1 = Cmd ++ " " ++ Args ++ " " ++ URL,
+ io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd1]),
+ open_port({spawn,Cmd1}, []);
+ _ ->
+ io:format("~nNo path to iexplore.exe~n",[])
+ end,
+ win32reg:close(R),
+ ok;
+
+open_url(Prog, Args, URL) ->
+ ProgStr = if is_atom(Prog) -> atom_to_list(Prog);
+ is_list(Prog) -> Prog
+ end,
+ Cmd = ProgStr ++ " " ++ Args ++ " " ++ URL,
+ io:format(user, "~nOpening ~s with command:~n ~s~n", [URL,Cmd]),
+ open_port({spawn,Cmd},[]),
+ ok.