From c9c5497f7e7665b49062c33253c6d693e2a5e654 Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Fri, 19 Feb 2010 14:59:39 +0100 Subject: Add support for user config in common_test Added: 1. ct_config, ct_config_plain and ct_config_xml modules. 2. support for {userconfig, {Callback, ConfigFiles}} parameter to ct:run_test/1 3. support for "-userconfig Callback ConfigFiles and OtherCallback ConfigFiles" parameter to the run_test script --- lib/common_test/src/ct_config.erl | 117 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 lib/common_test/src/ct_config.erl (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl new file mode 100755 index 0000000000..f344813128 --- /dev/null +++ b/lib/common_test/src/ct_config.erl @@ -0,0 +1,117 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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% +%%---------------------------------------------------------------------- +%% File : ct_config.erl +%% Description : CT module for reading and manipulating of configuration +%% data +%% +%% Created : 15 February 2010 +%%---------------------------------------------------------------------- +-module(ct_config). + +-export([read_config_files/1, + set_config/1, set_config/2, set_config/3, + delete_config/1, + get_config_file_list/1]). + +-include("ct_event.hrl"). +-include("ct_util.hrl"). + +-record(ct_conf,{key,value,ref,name='_UNDEF',default=false}). +%% default = {true,suite} | {true,testcase} | false + +read_config_files(Opts) -> + AddCallback = fun(CallBack, Files)-> + lists:map(fun(X)-> {CallBack, X} end, Files) + end, + ConfigFiles = case lists:keyfind(config, 1, Opts) of + {config, ConfigLists}-> + lists:foldr(fun({Callback,Files}, Acc)-> + AddCallback(Callback,Files) ++ Acc + end, + [], + ConfigLists); + false-> + [] + end, + read_config_files_int(ConfigFiles). + +read_config_files_int([{Callback, File}|Files])-> + case Callback:read_config_file(File) of + {ok, Config}-> + set_config(Config), + read_config_files_int(Files); + {error, ErrorName, ErrorDetail}-> + {user_error, {ErrorName, File, ErrorDetail}} + end; +read_config_files_int([])-> + ok. + +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_util:ct_make_ref(), + name=Name,default=Default}) || + {Key,Val} <- Config]. + +delete_config(Default) -> + ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}), + ok. + +process_user_configs(Opts, Acc)-> + case lists:keytake(userconfig, 1, Opts) of + false-> + Acc; + {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when + is_list(File)-> + process_user_configs(NewOpts, [{Callback, Files} | Acc]); + {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when + is_integer(C)-> + process_user_configs(NewOpts, [{Callback, [File]} | Acc]); + {value, {userconfig, {_Callback, []}}, NewOpts}-> + process_user_configs(NewOpts, Acc) + end. + +process_default_configs(Opts)-> + case lists:keysearch(config, 1, Opts) of + {value,{_,Files=[File|_]}} when is_list(File) -> + Files; + {value,{_,File=[C|_]}} when is_integer(C) -> + [File]; + {value,{_,[]}} -> + []; + false -> + [] + end. + +get_config_file_list(Opts)-> + DefaultConfigs = process_default_configs(Opts), + CfgFiles = + if + DefaultConfigs == []-> + []; + true-> + [{ct_config_plain, DefaultConfigs}] + end ++ + process_user_configs(Opts, []), + CfgFiles. -- cgit v1.2.3 From 8c79c17611c37217345e969aa2c344fe34e0375e Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Tue, 23 Feb 2010 15:31:12 +0100 Subject: Remove configuration handling from ct_util completely --- lib/common_test/src/ct_config.erl | 579 +++++++++++++++++++++++++++++++++++++- 1 file changed, 575 insertions(+), 4 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index f344813128..9a1f23201f 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -24,16 +24,169 @@ %%---------------------------------------------------------------------- -module(ct_config). +% start of the config server +-export([start/0, start/1, start/2, stop/0]). + +% manipulating with config files -export([read_config_files/1, - set_config/1, set_config/2, set_config/3, - delete_config/1, get_config_file_list/1]). --include("ct_event.hrl"). +% require +-export([require/1, require/2]). + +% get config data +-export([get_config/1, get_config/2, get_config/3, + get_all_config/0]). + +% set config data +-export([set_config/1, set_config/2, set_config/3, + set_default_config/2, set_default_config/3]). + +% delete config data +-export([delete_config/1, delete_default_config/1]). + +% update and reload config +-export([%reload_config/1, + update_config/2]). + +% ? +-export([release_allocated/0]). + +-export([encrypt_config_file/2, encrypt_config_file/3, + decrypt_config_file/2, decrypt_config_file/3, + get_crypt_key_from_file/0, get_crypt_key_from_file/1]). + +% references +-export([get_ref_from_name/1, get_name_from_ref/1, get_key_from_name/1]). + -include("ct_util.hrl"). +-include("ct_event.hrl"). + +-define(cryptfile, ".ct_config.crypt"). +% TODO: add handler field here -record(ct_conf,{key,value,ref,name='_UNDEF',default=false}). -%% default = {true,suite} | {true,testcase} | false + +%%%----------------------------------------------------------------- +%%% @spec start(Mode) -> Pid | exit(Error) +%%% Mode = normal | interactive +%%% Pid = pid() +%%% +%%% @doc Start start the ct_config_server process +%%% (tool-internal use only). +%%% +%%%

This function is called from ct_run.erl. It starts and initiates +%%% the ct_config_server

+%%% +%%%

Returns the process identity of the +%%% ct_config_server.

+%%% +%%% @see ct +start() -> + start(normal,"."). + +start(LogDir) when is_list(LogDir) -> + start(normal,LogDir); +start(Mode) -> + start(Mode,"."). + +start(Mode,LogDir) -> + case whereis(ct_config_server) of + undefined -> + Me = self(), + Pid = spawn_link(fun() -> do_start(Me) end), + receive + {Pid,started} -> Pid; + {Pid,Error} -> exit(Error) + end; + Pid -> + case ct_util:get_mode() of + interactive when Mode==interactive -> + Pid; + interactive -> + {error,interactive_mode}; + _OtherMode -> + Pid + end + end. + +do_start(Parent) -> + process_flag(trap_exit,true), + register(ct_config_server,self()), + ct_util:create_table(?attr_table,bag,#ct_conf.key), + {ok,StartDir} = file:get_cwd(), + Opts = case ct_util:read_opts() of + {ok,Opts1} -> + Opts1; + Error -> + Parent ! {self(),Error}, + exit(Error) + end, + case read_config_files(Opts) of + ok -> + Parent ! {self(),started}, + loop(StartDir); + ReadError -> + Parent ! {self(),ReadError}, + exit(ReadError) + end. + +%%%----------------------------------------------------------------- +%%% @spec stop() -> ok +%%% +%%% @doc Stop the ct_config_server and close all existing connections +%%% (tool-internal use only). +%%% +%%% @see ct +stop() -> + case whereis(ct_config_server) of + undefined -> ok; + _ -> call({stop}) + end. + +call(Msg) -> + MRef = erlang:monitor(process, whereis(ct_config_server)), + Ref = make_ref(), + ct_config_server ! {Msg,{self(),Ref}}, + receive + {Ref, Result} -> + erlang:demonitor(MRef), + Result; + {'DOWN',MRef,process,_,Reason} -> + {error,{ct_util_server_down,Reason}} + end. + +return({To,Ref},Result) -> + To ! {Ref, Result}. + + +loop(StartDir) -> + receive + {{require,Name,Tag,SubTags},From} -> + Result = do_require(Name,Tag,SubTags), + return(From,Result), + loop(StartDir); + {{set_default_config,{Config,Scope}},From} -> + ct_config:set_config(Config,{true,Scope}), + return(From,ok), + loop(StartDir); + {{set_default_config,{Name,Config,Scope}},From} -> + ct_config:set_config(Name,Config,{true,Scope}), + return(From,ok), + loop(StartDir); + {{delete_default_config,Scope},From} -> + ct_config:delete_config({true,Scope}), + return(From,ok), + loop(StartDir); + {{update_config,{Name,NewConfig}},From} -> + update_conf(Name,NewConfig), + return(From,ok), + loop(StartDir); + {{stop},From} -> + ets:delete(?attr_table), + file:set_cwd(StartDir), + return(From,ok) + end. read_config_files(Opts) -> AddCallback = fun(CallBack, Files)-> @@ -74,6 +227,180 @@ set_config(Name,Config,Default) -> name=Name,default=Default}) || {Key,Val} <- Config]. +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. + + +%%%----------------------------------------------------------------- +%%% @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 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. + +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) -> + ct_config:get_config(KeyOrName,undefined,[]). + +%%%----------------------------------------------------------------- +%%% @hidden +%%% @equiv ct:get_config/2 +get_config(KeyOrName,Default) -> + ct_config:get_config(KeyOrName,Default,[]). + +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'}}]}]). + delete_config(Default) -> ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}), ok. @@ -115,3 +442,247 @@ get_config_file_list(Opts)-> end ++ process_user_configs(Opts, []), CfgFiles. + +%%%----------------------------------------------------------------- +%%% @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. + +%%%----------------------------------------------------------------- +%%% @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. + + +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. + +%%%----------------------------------------------------------------- +%%% @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. + + +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}}). + +%%%----------------------------------------------------------------- +%%% @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) -> + <> = First = erlang:md5(String), + <> = 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]). -- cgit v1.2.3 From f2dcd87634cdcee65f0a930731a45005b4c20d08 Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Wed, 24 Feb 2010 15:15:41 +0100 Subject: Implement reloading of the config data --- lib/common_test/src/ct_config.erl | 380 ++++++++++++++++++-------------------- 1 file changed, 180 insertions(+), 200 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 9a1f23201f..dbb0e0b152 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -24,73 +24,37 @@ %%---------------------------------------------------------------------- -module(ct_config). -% start of the config server --export([start/0, start/1, start/2, stop/0]). +-export([start/1, stop/0]). -% manipulating with config files -export([read_config_files/1, get_config_file_list/1]). -% require -export([require/1, require/2]). -% get config data -export([get_config/1, get_config/2, get_config/3, get_all_config/0]). -% set config data --export([set_config/1, set_config/2, set_config/3, - set_default_config/2, set_default_config/3]). +-export([set_default_config/2, set_default_config/3]). -% delete config data -export([delete_config/1, delete_default_config/1]). -% update and reload config --export([%reload_config/1, - update_config/2]). +-export([reload_config/1, update_config/2]). -% ? -export([release_allocated/0]). -export([encrypt_config_file/2, encrypt_config_file/3, decrypt_config_file/2, decrypt_config_file/3, get_crypt_key_from_file/0, get_crypt_key_from_file/1]). -% references -export([get_ref_from_name/1, get_name_from_ref/1, get_key_from_name/1]). -include("ct_util.hrl"). --include("ct_event.hrl"). -define(cryptfile, ".ct_config.crypt"). -% TODO: add handler field here --record(ct_conf,{key,value,ref,name='_UNDEF',default=false}). - -%%%----------------------------------------------------------------- -%%% @spec start(Mode) -> Pid | exit(Error) -%%% Mode = normal | interactive -%%% Pid = pid() -%%% -%%% @doc Start start the ct_config_server process -%%% (tool-internal use only). -%%% -%%%

This function is called from ct_run.erl. It starts and initiates -%%% the ct_config_server

-%%% -%%%

Returns the process identity of the -%%% ct_config_server.

-%%% -%%% @see ct -start() -> - start(normal,"."). - -start(LogDir) when is_list(LogDir) -> - start(normal,LogDir); -start(Mode) -> - start(Mode,"."). +-record(ct_conf,{key,value,handler,config,ref,name='_UNDEF',default=false}). -start(Mode,LogDir) -> +start(Mode) -> case whereis(ct_config_server) of undefined -> Me = self(), @@ -131,13 +95,6 @@ do_start(Parent) -> exit(ReadError) end. -%%%----------------------------------------------------------------- -%%% @spec stop() -> ok -%%% -%%% @doc Stop the ct_config_server and close all existing connections -%%% (tool-internal use only). -%%% -%%% @see ct stop() -> case whereis(ct_config_server) of undefined -> ok; @@ -159,7 +116,6 @@ call(Msg) -> return({To,Ref},Result) -> To ! {Ref, Result}. - loop(StartDir) -> receive {{require,Name,Tag,SubTags},From} -> @@ -167,11 +123,11 @@ loop(StartDir) -> return(From,Result), loop(StartDir); {{set_default_config,{Config,Scope}},From} -> - ct_config:set_config(Config,{true,Scope}), + set_config(Config,{true,Scope}), return(From,ok), loop(StartDir); {{set_default_config,{Name,Config,Scope}},From} -> - ct_config:set_config(Name,Config,{true,Scope}), + set_config(Name,Config,{true,Scope}), return(From,ok), loop(StartDir); {{delete_default_config,Scope},From} -> @@ -182,12 +138,69 @@ loop(StartDir) -> update_conf(Name,NewConfig), return(From,ok), loop(StartDir); + {{reload_config, KeyOrName},From}-> + NewValue = reload_conf(KeyOrName), + return(From, NewValue), + loop(StartDir); {{stop},From} -> ets:delete(?attr_table), file:set_cwd(StartDir), return(From,ok) 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}}). + +reload_config(KeyOrName)-> + call({reload_config, KeyOrName}). + +process_default_configs(Opts)-> + case lists:keysearch(config, 1, Opts) of + {value,{_,Files=[File|_]}} when is_list(File) -> + Files; + {value,{_,File=[C|_]}} when is_integer(C) -> + [File]; + {value,{_,[]}} -> + []; + false -> + [] + end. + +process_user_configs(Opts, Acc)-> + case lists:keytake(userconfig, 1, Opts) of + false-> + Acc; + {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when + is_list(File)-> + process_user_configs(NewOpts, [{Callback, Files} | Acc]); + {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when + is_integer(C)-> + process_user_configs(NewOpts, [{Callback, [File]} | Acc]); + {value, {userconfig, {_Callback, []}}, NewOpts}-> + process_user_configs(NewOpts, Acc) + end. + +get_config_file_list(Opts)-> + DefaultConfigs = process_default_configs(Opts), + CfgFiles = + if + DefaultConfigs == []-> + []; + true-> + [{ct_config_plain, DefaultConfigs}] + end ++ + process_user_configs(Opts, []), + CfgFiles. + read_config_files(Opts) -> AddCallback = fun(CallBack, Files)-> lists:map(fun(X)-> {CallBack, X} end, Files) @@ -202,21 +215,50 @@ read_config_files(Opts) -> false-> [] end, - read_config_files_int(ConfigFiles). + read_config_files_int(ConfigFiles, fun store_config/3). -read_config_files_int([{Callback, File}|Files])-> +read_config_files_int([{Callback, File}|Files], FunToSave)-> case Callback:read_config_file(File) of {ok, Config}-> - set_config(Config), - read_config_files_int(Files); + FunToSave(Config, Callback, File), + read_config_files_int(Files, FunToSave); {error, ErrorName, ErrorDetail}-> {user_error, {ErrorName, File, ErrorDetail}} end; -read_config_files_int([])-> +read_config_files_int([], _FunToSave)-> ok. -set_config(Config) -> - set_config('_UNDEF',Config,false). +store_config(Config, Callback, File)-> + [ets:insert(?attr_table, + #ct_conf{key=Key, + value=Val, + handler=Callback, + config=File, + ref=ct_util:ct_make_ref(), + default=false}) || + {Key,Val} <- Config]. + +rewrite_config(Config, Callback, File)-> + [begin + Rows=case ets:match_object(?attr_table, + #ct_conf{key=Key, + handler=Callback, + config=File,_='_'}) of + []-> + [#ct_conf{default=false}]; + Elements-> + Elements + end, + lists:foreach(fun(Row)-> + ets:delete_object(?attr_table, Row), + ets:insert(?attr_table, + Row#ct_conf{key=Key, + value=Val, + handler=Callback, + config=File, + ref=ct_util:ct_make_ref()}) end, + Rows) + end || {Key,Val} <- Config]. set_config(Config,Default) -> set_config('_UNDEF',Config,Default). @@ -227,6 +269,12 @@ set_config(Name,Config,Default) -> name=Name,default=Default}) || {Key,Val} <- Config]. +get_config(KeyOrName) -> + get_config(KeyOrName,undefined,[]). + +get_config(KeyOrName,Default) -> + get_config(KeyOrName,Default,[]). + get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) -> case lookup_config(KeyOrName) of [] -> @@ -277,75 +325,6 @@ get_config({KeyOrName,SubKey},Default,Opts) -> end end. - -%%%----------------------------------------------------------------- -%%% @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 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. - -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) -> - ct_config:get_config(KeyOrName,undefined,[]). - -%%%----------------------------------------------------------------- -%%% @hidden -%%% @equiv ct:get_config/2 -get_config(KeyOrName,Default) -> - ct_config:get_config(KeyOrName,Default,[]). - get_subconfig(SubKeys,Values) -> get_subconfig(SubKeys,Values,[],[]). @@ -401,51 +380,87 @@ lookup_key(Key) -> [], [{{'$1','$2'}}]}]). -delete_config(Default) -> - ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}), +lookup_handler_for_config({Key, _Subkey})-> + lookup_handler_for_config(Key); +lookup_handler_for_config(KeyOrName)-> + case lookup_handler_for_name(KeyOrName) of + [] -> + lookup_handler_for_key(KeyOrName); + Values -> + Values + end. + +lookup_handler_for_name(Name)-> + ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',name=Name,_='_'}, + [], + [{{'$1','$2'}}]}]). + +lookup_handler_for_key(Key)-> + ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',key=Key,_='_'}, + [], + [{{'$1','$2'}}]}]). + + +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. -process_user_configs(Opts, Acc)-> - case lists:keytake(userconfig, 1, Opts) of - false-> - Acc; - {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when - is_list(File)-> - process_user_configs(NewOpts, [{Callback, Files} | Acc]); - {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when - is_integer(C)-> - process_user_configs(NewOpts, [{Callback, [File]} | Acc]); - {value, {userconfig, {_Callback, []}}, NewOpts}-> - process_user_configs(NewOpts, Acc) +reload_conf(KeyOrName) -> + case lookup_handler_for_config(KeyOrName) of + []-> + undefined; + HandlerList-> + read_config_files_int(HandlerList, fun rewrite_config/3), + get_config(KeyOrName) end. -process_default_configs(Opts)-> - case lists:keysearch(config, 1, Opts) of - {value,{_,Files=[File|_]}} when is_list(File) -> - Files; - {value,{_,File=[C|_]}} when is_integer(C) -> - [File]; - {value,{_,[]}} -> - []; - false -> - [] +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. + +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. -get_config_file_list(Opts)-> - DefaultConfigs = process_default_configs(Opts), - CfgFiles = - if - DefaultConfigs == []-> - []; - true-> - [{ct_config_plain, DefaultConfigs}] - end ++ - process_user_configs(Opts, []), - CfgFiles. +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}}. + +delete_config(Default) -> + ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}), + ok. -%%%----------------------------------------------------------------- -%%% @hidden -%%% @equiv ct:require/1 require(Key) when is_atom(Key) -> require({Key,[]}); require({Key,SubKeys}) when is_atom(Key) -> @@ -453,10 +468,6 @@ require({Key,SubKeys}) when is_atom(Key) -> 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) -> @@ -484,10 +495,6 @@ do_require(Name,Key,SubKeys) when is_list(SubKeys) -> {error,{name_in_use,Name,OtherKey}} end. -%%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc encrypt_config_file(SrcFileName, EncryptFileName) -> case get_crypt_key_from_file() of {error,_} = E -> @@ -496,7 +503,6 @@ encrypt_config_file(SrcFileName, EncryptFileName) -> encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) end. - get_ref_from_name(Name) -> case ets:select(?attr_table,[{#ct_conf{name=Name,ref='$1',_='_'}, [], @@ -527,10 +533,6 @@ get_key_from_name(Name) -> {error,{no_such_name,Name}} end. -%%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) -> case get_crypt_key_from_file(KeyFile) of {error,_} = E -> @@ -562,10 +564,6 @@ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> {error,{Reason,SrcFileName}} end. -%%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc decrypt_config_file(EncryptFileName, TargetFileName) -> case get_crypt_key_from_file() of {error,_} = E -> @@ -574,23 +572,6 @@ decrypt_config_file(EncryptFileName, TargetFileName) -> decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) 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}}). - -%%%----------------------------------------------------------------- -%%% @spec -%%% -%%% @doc decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) -> case get_crypt_key_from_file(KeyFile) of {error,_} = E -> @@ -627,7 +608,6 @@ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) -> {error,{Reason,EncryptFileName}} end. - get_crypt_key_from_file(File) -> case file:read_file(File) of {ok,Bin} -> -- cgit v1.2.3 From 3237b512933560ea43173847c44d8d8dab93fd3a Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Thu, 4 Mar 2010 14:38:41 +0100 Subject: Allow callbacks to take any list as parameter --- lib/common_test/src/ct_config.erl | 60 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index dbb0e0b152..63e44b6301 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -48,6 +48,8 @@ -export([get_ref_from_name/1, get_name_from_ref/1, get_key_from_name/1]). +-export([check_config_files/1, prepare_config_list/1]). + -include("ct_util.hrl"). -define(cryptfile, ".ct_config.crypt"). @@ -179,6 +181,8 @@ process_user_configs(Opts, Acc)-> case lists:keytake(userconfig, 1, Opts) of false-> Acc; + {value, {userconfig, {Callback, []}}, NewOpts}-> + process_user_configs(NewOpts, [{Callback, []} | Acc]); {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when is_list(File)-> process_user_configs(NewOpts, [{Callback, Files} | Acc]); @@ -202,9 +206,12 @@ get_config_file_list(Opts)-> CfgFiles. read_config_files(Opts) -> - AddCallback = fun(CallBack, Files)-> - lists:map(fun(X)-> {CallBack, X} end, Files) - end, + ct:pal("ct_config:read_config_files/1:~nOpts:~n~p", [Opts]), + AddCallback = fun(CallBack, [])-> + [{CallBack, []}]; + (CallBack, Files)-> + lists:map(fun(X)-> {CallBack, X} end, Files) + end, ConfigFiles = case lists:keyfind(config, 1, Opts) of {config, ConfigLists}-> lists:foldr(fun({Callback,Files}, Acc)-> @@ -215,10 +222,11 @@ read_config_files(Opts) -> false-> [] end, + ct:pal("ct_config:read_config_files/1:~nConfigFiles:~n~p", [ConfigFiles]), read_config_files_int(ConfigFiles, fun store_config/3). read_config_files_int([{Callback, File}|Files], FunToSave)-> - case Callback:read_config_file(File) of + case Callback:read_config(File) of {ok, Config}-> FunToSave(Config, Callback, File), read_config_files_int(Files, FunToSave); @@ -666,3 +674,47 @@ random_bytes(N) -> random_bytes_1(0, Acc) -> Acc; random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]). + +check_config_files(Configs)-> + lists:keysearch(nok, 1, + lists:flatten( + lists:map(fun({Callback, Files})-> + case code:load_file(Callback) of + {module, Callback}-> + lists:map(fun(File)-> + Callback:check_parameter(File) + end, + Files); + {error, _}-> + {nok, {callback, Callback}} + end + end, + Configs))). + +prepare_user_configs([ConfigString|UserConfigs], Acc, new)-> + prepare_user_configs(UserConfigs, + [{list_to_atom(ConfigString), []}|Acc], + cur); +prepare_user_configs(["and"|UserConfigs], Acc, _)-> + prepare_user_configs(UserConfigs, Acc, new); +prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur)-> + prepare_user_configs(UserConfigs, + [{LastMod, [ConfigString|LastList]}|Acc], + cur); +prepare_user_configs([], Acc, _)-> + Acc. + +prepare_config_list(Args)-> + ConfigFiles = case lists:keysearch(ct_config, 1, Args) of + {value,{ct_config,Files}}-> + [{ct_config_plain, Files}]; + false-> + [] + end, + UserConfigs = case lists:keysearch(userconfig, 1, Args) of + {value,{userconfig,UserConfigFiles}}-> + prepare_user_configs(UserConfigFiles, [], new); + false-> + [] + end, + ConfigFiles ++ UserConfigs. -- cgit v1.2.3 From 2dc962eead182ca5c79c5ac8d9d31a08f22c97af Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Fri, 5 Mar 2010 08:48:06 +0100 Subject: Fix problems with test specifications Work in progress... --- lib/common_test/src/ct_config.erl | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 63e44b6301..8e2f71647f 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -188,9 +188,7 @@ process_user_configs(Opts, Acc)-> process_user_configs(NewOpts, [{Callback, Files} | Acc]); {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when is_integer(C)-> - process_user_configs(NewOpts, [{Callback, [File]} | Acc]); - {value, {userconfig, {_Callback, []}}, NewOpts}-> - process_user_configs(NewOpts, Acc) + process_user_configs(NewOpts, [{Callback, [File]} | Acc]) end. get_config_file_list(Opts)-> @@ -675,12 +673,26 @@ random_bytes(N) -> random_bytes_1(0, Acc) -> Acc; random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]). +check_callback_load(Callback)-> + case code:is_loaded(Callback) of + {file, _Filename}-> + {ok, Callback}; + false-> + case code:load_file(Callback) of + {module, Callback}-> + {ok, Callback}; + {error, Error}-> + {error, Error} + end + end. + check_config_files(Configs)-> + ct:pal("ct_config:check_config_files(~p)", [Configs]), lists:keysearch(nok, 1, lists:flatten( lists:map(fun({Callback, Files})-> - case code:load_file(Callback) of - {module, Callback}-> + case check_callback_load(Callback) of + {ok, Callback}-> lists:map(fun(File)-> Callback:check_parameter(File) end, -- cgit v1.2.3 From 5b66bed40bd374dfcaa5e5669adf338734d812a3 Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Tue, 9 Mar 2010 15:10:22 +0100 Subject: Add test suites for configuration --- lib/common_test/src/ct_config.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 8e2f71647f..bc57930381 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -198,13 +198,13 @@ get_config_file_list(Opts)-> DefaultConfigs == []-> []; true-> - [{ct_config_plain, DefaultConfigs}] + [{?ct_config_txt, DefaultConfigs}] end ++ process_user_configs(Opts, []), CfgFiles. read_config_files(Opts) -> - ct:pal("ct_config:read_config_files/1:~nOpts:~n~p", [Opts]), + %ct:pal("ct_config:read_config_files/1:~nOpts:~n~p", [Opts]), AddCallback = fun(CallBack, [])-> [{CallBack, []}]; (CallBack, Files)-> @@ -220,7 +220,7 @@ read_config_files(Opts) -> false-> [] end, - ct:pal("ct_config:read_config_files/1:~nConfigFiles:~n~p", [ConfigFiles]), + %ct:pal("ct_config:read_config_files/1:~nConfigFiles:~n~p", [ConfigFiles]), read_config_files_int(ConfigFiles, fun store_config/3). read_config_files_int([{Callback, File}|Files], FunToSave)-> @@ -687,7 +687,7 @@ check_callback_load(Callback)-> end. check_config_files(Configs)-> - ct:pal("ct_config:check_config_files(~p)", [Configs]), + ct:pal("ct_config:check_config_files/1~nConfigs:~n~p", [Configs]), lists:keysearch(nok, 1, lists:flatten( lists:map(fun({Callback, Files})-> @@ -719,7 +719,7 @@ prepare_user_configs([], Acc, _)-> prepare_config_list(Args)-> ConfigFiles = case lists:keysearch(ct_config, 1, Args) of {value,{ct_config,Files}}-> - [{ct_config_plain, Files}]; + [{?ct_config_txt, Files}]; false-> [] end, -- cgit v1.2.3 From e04b9be7167841c7eaefcfb7eee5b4bc2eb3a943 Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Thu, 11 Mar 2010 14:21:51 +0100 Subject: Add tests for test specifications --- lib/common_test/src/ct_config.erl | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index bc57930381..4b99d8db88 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -204,7 +204,6 @@ get_config_file_list(Opts)-> CfgFiles. read_config_files(Opts) -> - %ct:pal("ct_config:read_config_files/1:~nOpts:~n~p", [Opts]), AddCallback = fun(CallBack, [])-> [{CallBack, []}]; (CallBack, Files)-> @@ -220,7 +219,6 @@ read_config_files(Opts) -> false-> [] end, - %ct:pal("ct_config:read_config_files/1:~nConfigFiles:~n~p", [ConfigFiles]), read_config_files_int(ConfigFiles, fun store_config/3). read_config_files_int([{Callback, File}|Files], FunToSave)-> @@ -687,7 +685,6 @@ check_callback_load(Callback)-> end. check_config_files(Configs)-> - ct:pal("ct_config:check_config_files/1~nConfigs:~n~p", [Configs]), lists:keysearch(nok, 1, lists:flatten( lists:map(fun({Callback, Files})-> -- cgit v1.2.3 From a9c315929d96c7d0d0e5e79f8fc0ba0c7f17e94d Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Mon, 15 Mar 2010 15:26:02 +0100 Subject: Changed return value tags for config file handling Return value tags modified and various documentation updates made (work in progress). --- lib/common_test/src/ct_config.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 4b99d8db88..5623b80592 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -685,7 +685,7 @@ check_callback_load(Callback)-> end. check_config_files(Configs)-> - lists:keysearch(nok, 1, + lists:keysearch(error, 1, lists:flatten( lists:map(fun({Callback, Files})-> case check_callback_load(Callback) of @@ -695,7 +695,7 @@ check_config_files(Configs)-> end, Files); {error, _}-> - {nok, {callback, Callback}} + {error, {callback, Callback}} end end, Configs))). -- cgit v1.2.3 From dd67e73980ac0c8b601079c2e3f27701c367936c Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Thu, 18 Mar 2010 18:02:37 +0100 Subject: Fix problem with disappearing variables and aliases --- lib/common_test/src/ct_config.erl | 81 ++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 19 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 5623b80592..728dcde6af 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -242,27 +242,51 @@ store_config(Config, Callback, File)-> default=false}) || {Key,Val} <- Config]. +keyfindall(Key, N, List)-> + keyfindall(Key, N, List, []). + +keyfindall(Key, N, List, Acc)-> + case lists:keytake(Key, N, List) of + false-> + Acc; + {value, Row, Rest}-> + keyfindall(Key, N, Rest, [Row|Acc]) + end. + rewrite_config(Config, Callback, File)-> - [begin - Rows=case ets:match_object(?attr_table, - #ct_conf{key=Key, - handler=Callback, - config=File,_='_'}) of - []-> - [#ct_conf{default=false}]; - Elements-> - Elements - end, - lists:foreach(fun(Row)-> - ets:delete_object(?attr_table, Row), - ets:insert(?attr_table, - Row#ct_conf{key=Key, - value=Val, + % 1) read the old config for this callback/file from the table + OldRows = ets:match_object(?attr_table, + #ct_conf{handler=Callback, + config=File,_='_'}), + % 2) remove all config data loaded from this callback/file + ets:match_delete(?attr_table, + #ct_conf{handler=Callback, + config=File,_='_'}), + % prepare function that will + % 1. find records with this key in the old config, including aliases + % 2. update values + % 3. put them back to the table + Updater = fun({Key, Value})-> + case keyfindall(Key, #ct_conf.key, OldRows) of + []-> % if variable is new, just insert it + ets:insert(?attr_table, + #ct_conf{key=Key, + value=Value, handler=Callback, config=File, - ref=ct_util:ct_make_ref()}) end, - Rows) - end || {Key,Val} <- Config]. + ref=ct_util:ct_make_ref()}); + RowsToUpdate -> % else update all occurrencies of the key + Inserter = fun(Row)-> + ets:insert(?attr_table, + Row#ct_conf{value=Value, + ref=ct_util:ct_make_ref()}) + end, + lists:foreach(Inserter, RowsToUpdate) + end + end, + + % run it key-by-key from the new config + [Updater({Key, Value})||{Key, Value}<-Config]. set_config(Config,Default) -> set_config('_UNDEF',Config,Default). @@ -414,12 +438,31 @@ update_conf(Name, NewConfig) -> end, Old), ok. +has_element(_, [])-> + false; +has_element(Element, [Element|_Rest])-> + true; +has_element(Element, [_|Rest])-> + has_element(Element, Rest). + +remove_duplicates([], Acc)-> + Acc; +remove_duplicates([{Handler, File}|Rest], Acc)-> + case has_element({Handler, File}, Acc) of + false-> + remove_duplicates(Rest, [{Handler, File}|Acc]); + true-> + remove_duplicates(Rest, Acc) + end. + reload_conf(KeyOrName) -> case lookup_handler_for_config(KeyOrName) of []-> undefined; HandlerList-> - read_config_files_int(HandlerList, fun rewrite_config/3), + % if aliases set, config will be reloaded several times + HandlerList2 = remove_duplicates(HandlerList, []), + read_config_files_int(HandlerList2, fun rewrite_config/3), get_config(KeyOrName) end. -- cgit v1.2.3 From 485a982ef7e7d2ffcdfb2606b6e2f4b66d000bac Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Fri, 19 Mar 2010 09:09:46 +0100 Subject: Replace own has_element/2 with lists:config/2 --- lib/common_test/src/ct_config.erl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 728dcde6af..0ae178ef02 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -438,17 +438,10 @@ update_conf(Name, NewConfig) -> end, Old), ok. -has_element(_, [])-> - false; -has_element(Element, [Element|_Rest])-> - true; -has_element(Element, [_|Rest])-> - has_element(Element, Rest). - remove_duplicates([], Acc)-> Acc; remove_duplicates([{Handler, File}|Rest], Acc)-> - case has_element({Handler, File}, Acc) of + case lists:member({Handler, File}, Acc) of false-> remove_duplicates(Rest, [{Handler, File}|Acc]); true-> -- cgit v1.2.3 From 2fa838d02446ad54588c2fe6995e3c065e99ec9c Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Mon, 22 Mar 2010 15:29:28 +0100 Subject: Add functions for adding and removing config handlers This is a draft version. --- lib/common_test/src/ct_config.erl | 58 +++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 33 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 0ae178ef02..6fd89c5617 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -36,7 +36,7 @@ -export([set_default_config/2, set_default_config/3]). --export([delete_config/1, delete_default_config/1]). +-export([delete_default_config/1]). -export([reload_config/1, update_config/2]). @@ -50,6 +50,8 @@ -export([check_config_files/1, prepare_config_list/1]). +-export([add_config/2, remove_config/2]). + -include("ct_util.hrl"). -define(cryptfile, ".ct_config.crypt"). @@ -133,7 +135,7 @@ loop(StartDir) -> return(From,ok), loop(StartDir); {{delete_default_config,Scope},From} -> - ct_config:delete_config({true,Scope}), + delete_config({true,Scope}), return(From,ok), loop(StartDir); {{update_config,{Name,NewConfig}},From} -> @@ -242,40 +244,26 @@ store_config(Config, Callback, File)-> default=false}) || {Key,Val} <- Config]. -keyfindall(Key, N, List)-> - keyfindall(Key, N, List, []). - -keyfindall(Key, N, List, Acc)-> - case lists:keytake(Key, N, List) of - false-> - Acc; - {value, Row, Rest}-> - keyfindall(Key, N, Rest, [Row|Acc]) - end. +keyfindall(Key, Pos, List)-> + [E || E <- List, element(Pos, E) =:= Key]. rewrite_config(Config, Callback, File)-> - % 1) read the old config for this callback/file from the table OldRows = ets:match_object(?attr_table, #ct_conf{handler=Callback, config=File,_='_'}), - % 2) remove all config data loaded from this callback/file ets:match_delete(?attr_table, #ct_conf{handler=Callback, config=File,_='_'}), - % prepare function that will - % 1. find records with this key in the old config, including aliases - % 2. update values - % 3. put them back to the table Updater = fun({Key, Value})-> case keyfindall(Key, #ct_conf.key, OldRows) of - []-> % if variable is new, just insert it + []-> ets:insert(?attr_table, #ct_conf{key=Key, value=Value, handler=Callback, config=File, ref=ct_util:ct_make_ref()}); - RowsToUpdate -> % else update all occurrencies of the key + RowsToUpdate -> Inserter = fun(Row)-> ets:insert(?attr_table, Row#ct_conf{value=Value, @@ -285,7 +273,6 @@ rewrite_config(Config, Callback, File)-> end end, - % run it key-by-key from the new config [Updater({Key, Value})||{Key, Value}<-Config]. set_config(Config,Default) -> @@ -438,23 +425,12 @@ update_conf(Name, NewConfig) -> end, Old), ok. -remove_duplicates([], Acc)-> - Acc; -remove_duplicates([{Handler, File}|Rest], Acc)-> - case lists:member({Handler, File}, Acc) of - false-> - remove_duplicates(Rest, [{Handler, File}|Acc]); - true-> - remove_duplicates(Rest, Acc) - end. - reload_conf(KeyOrName) -> case lookup_handler_for_config(KeyOrName) of []-> undefined; HandlerList-> - % if aliases set, config will be reloaded several times - HandlerList2 = remove_duplicates(HandlerList, []), + HandlerList2 = lists:usort(HandlerList), read_config_files_int(HandlerList2, fun rewrite_config/3), get_config(KeyOrName) end. @@ -763,3 +739,19 @@ prepare_config_list(Args)-> [] end, ConfigFiles ++ UserConfigs. + +add_config(Callback, [])-> + read_config_files_int([{Callback, []}], fun store_config/3); +add_config(Callback, [File|_Files]=Config) when is_list(File)-> + lists:foreach(fun(CfgStr)-> + read_config_files_int([{Callback, CfgStr}], fun store_config/3) end, + Config); +add_config(Callback, [C|_]=Config) when is_integer(C)-> + read_config_files_int([{Callback, Config}], fun store_config/3), + ok. + +remove_config(Callback, Config)-> + ets:match_delete(?attr_table, + #ct_conf{handler=Callback, + config=Config,_='_'}), + ok. -- cgit v1.2.3 From afff4fa809f4e5dbb5a8dca556f30df825d9c781 Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Mon, 22 Mar 2010 16:23:42 +0100 Subject: Bug fix: ["config.txt"] /= "config.txt" --- lib/common_test/src/ct_config.erl | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 6fd89c5617..312dc8782f 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -208,7 +208,9 @@ get_config_file_list(Opts)-> read_config_files(Opts) -> AddCallback = fun(CallBack, [])-> [{CallBack, []}]; - (CallBack, Files)-> + (CallBack, [F|_]=Files) when is_integer(F)-> + [{CallBack, Files}]; + (CallBack, [F|_]=Files) when is_list(F)-> lists:map(fun(X)-> {CallBack, X} end, Files) end, ConfigFiles = case lists:keyfind(config, 1, Opts) of @@ -697,20 +699,31 @@ check_callback_load(Callback)-> end. check_config_files(Configs)-> - lists:keysearch(error, 1, - lists:flatten( - lists:map(fun({Callback, Files})-> - case check_callback_load(Callback) of - {ok, Callback}-> - lists:map(fun(File)-> - Callback:check_parameter(File) - end, - Files); - {error, _}-> - {error, {callback, Callback}} - end - end, - Configs))). + ConfigChecker = fun + ({Callback, [F|_R]=Files})-> + case check_callback_load(Callback) of + {ok, Callback}-> + if + is_integer(F)-> + Callback:check_parameter(Files); + is_list(F)-> + lists:map(fun(File)-> + Callback:check_parameter(File) + end, + Files) + end; + {error, _}-> + {error, {callback, Callback}} + end; + ({Callback, []})-> + case check_callback_load(Callback) of + {ok, Callback}-> + Callback:check_parameter([]); + {error, _}-> + {error, {callback, Callback}} + end + end, + lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))). prepare_user_configs([ConfigString|UserConfigs], Acc, new)-> prepare_user_configs(UserConfigs, -- cgit v1.2.3 From 497c107bcdb7095f402d4b9884b0bfc8f9bbe97f Mon Sep 17 00:00:00 2001 From: Andrey Pampukha Date: Wed, 24 Mar 2010 11:35:59 +0100 Subject: Add support for user_config in ct_master --- lib/common_test/src/ct_config.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 312dc8782f..6314361b35 100755 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -753,6 +753,7 @@ prepare_config_list(Args)-> end, ConfigFiles ++ UserConfigs. +% TODO: add logging of the loaded configuration file to the CT FW log!!! add_config(Callback, [])-> read_config_files_int([{Callback, []}], fun store_config/3); add_config(Callback, [File|_Files]=Config) when is_list(File)-> -- cgit v1.2.3 From 5cf552a62742c6ddc974ba5491188576d512254e Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Mon, 24 May 2010 17:38:19 +0200 Subject: Add run_test program for Common Test Common Test may now be started with the program run_test instead of the legacy shell script with the same name. Minor updates have also been made to the Webtool application. --- lib/common_test/src/ct_config.erl | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 lib/common_test/src/ct_config.erl (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl old mode 100755 new mode 100644 -- cgit v1.2.3 From 4da38a84f7540856fa590afdba2eb7958978788c Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 3 Jun 2010 14:27:21 +0200 Subject: Make it possible to run ts tests for Common Test via the ct_run:script_start() interface The possibility to pass start arguments to ct_run:start_script/0 by means of an application environment variable has been implemented. This will be used by ct_test_support for automatic testing of all common_test start interfaces. --- lib/common_test/src/ct_config.erl | 119 +++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 60 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 6314361b35..ee84164ad7 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -164,10 +164,10 @@ delete_default_config(Scope) -> update_config(Name, Config) -> call({update_config, {Name, Config}}). -reload_config(KeyOrName)-> +reload_config(KeyOrName) -> call({reload_config, KeyOrName}). -process_default_configs(Opts)-> +process_default_configs(Opts) -> case lists:keysearch(config, 1, Opts) of {value,{_,Files=[File|_]}} when is_list(File) -> Files; @@ -179,21 +179,21 @@ process_default_configs(Opts)-> [] end. -process_user_configs(Opts, Acc)-> +process_user_configs(Opts, Acc) -> case lists:keytake(userconfig, 1, Opts) of false-> Acc; {value, {userconfig, {Callback, []}}, NewOpts}-> process_user_configs(NewOpts, [{Callback, []} | Acc]); {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when - is_list(File)-> + is_list(File) -> process_user_configs(NewOpts, [{Callback, Files} | Acc]); {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when - is_integer(C)-> + is_integer(C) -> process_user_configs(NewOpts, [{Callback, [File]} | Acc]) end. -get_config_file_list(Opts)-> +get_config_file_list(Opts) -> DefaultConfigs = process_default_configs(Opts), CfgFiles = if @@ -206,16 +206,16 @@ get_config_file_list(Opts)-> CfgFiles. read_config_files(Opts) -> - AddCallback = fun(CallBack, [])-> + AddCallback = fun(CallBack, []) -> [{CallBack, []}]; - (CallBack, [F|_]=Files) when is_integer(F)-> + (CallBack, [F|_]=Files) when is_integer(F) -> [{CallBack, Files}]; - (CallBack, [F|_]=Files) when is_list(F)-> - lists:map(fun(X)-> {CallBack, X} end, Files) + (CallBack, [F|_]=Files) when is_list(F) -> + lists:map(fun(X) -> {CallBack, X} end, Files) end, ConfigFiles = case lists:keyfind(config, 1, Opts) of {config, ConfigLists}-> - lists:foldr(fun({Callback,Files}, Acc)-> + lists:foldr(fun({Callback,Files}, Acc) -> AddCallback(Callback,Files) ++ Acc end, [], @@ -225,18 +225,18 @@ read_config_files(Opts) -> end, read_config_files_int(ConfigFiles, fun store_config/3). -read_config_files_int([{Callback, File}|Files], FunToSave)-> +read_config_files_int([{Callback, File}|Files], FunToSave) -> case Callback:read_config(File) of - {ok, Config}-> + {ok, Config} -> FunToSave(Config, Callback, File), read_config_files_int(Files, FunToSave); {error, ErrorName, ErrorDetail}-> {user_error, {ErrorName, File, ErrorDetail}} end; -read_config_files_int([], _FunToSave)-> +read_config_files_int([], _FunToSave) -> ok. -store_config(Config, Callback, File)-> +store_config(Config, Callback, File) -> [ets:insert(?attr_table, #ct_conf{key=Key, value=Val, @@ -246,35 +246,34 @@ store_config(Config, Callback, File)-> default=false}) || {Key,Val} <- Config]. -keyfindall(Key, Pos, List)-> +keyfindall(Key, Pos, List) -> [E || E <- List, element(Pos, E) =:= Key]. -rewrite_config(Config, Callback, File)-> +rewrite_config(Config, Callback, File) -> OldRows = ets:match_object(?attr_table, #ct_conf{handler=Callback, config=File,_='_'}), ets:match_delete(?attr_table, #ct_conf{handler=Callback, config=File,_='_'}), - Updater = fun({Key, Value})-> + Updater = fun({Key, Value}) -> case keyfindall(Key, #ct_conf.key, OldRows) of []-> ets:insert(?attr_table, #ct_conf{key=Key, - value=Value, - handler=Callback, - config=File, - ref=ct_util:ct_make_ref()}); + value=Value, + handler=Callback, + config=File, + ref=ct_util:ct_make_ref()}); RowsToUpdate -> - Inserter = fun(Row)-> - ets:insert(?attr_table, - Row#ct_conf{value=Value, - ref=ct_util:ct_make_ref()}) - end, + Inserter = fun(Row) -> + ets:insert(?attr_table, + Row#ct_conf{value=Value, + ref=ct_util:ct_make_ref()}) + end, lists:foreach(Inserter, RowsToUpdate) end - end, - + end, [Updater({Key, Value})||{Key, Value}<-Config]. set_config(Config,Default) -> @@ -397,9 +396,9 @@ lookup_key(Key) -> [], [{{'$1','$2'}}]}]). -lookup_handler_for_config({Key, _Subkey})-> +lookup_handler_for_config({Key, _Subkey}) -> lookup_handler_for_config(Key); -lookup_handler_for_config(KeyOrName)-> +lookup_handler_for_config(KeyOrName) -> case lookup_handler_for_name(KeyOrName) of [] -> lookup_handler_for_key(KeyOrName); @@ -407,12 +406,12 @@ lookup_handler_for_config(KeyOrName)-> Values end. -lookup_handler_for_name(Name)-> +lookup_handler_for_name(Name) -> ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',name=Name,_='_'}, [], [{{'$1','$2'}}]}]). -lookup_handler_for_key(Key)-> +lookup_handler_for_key(Key) -> ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',key=Key,_='_'}, [], [{{'$1','$2'}}]}]). @@ -685,7 +684,7 @@ random_bytes(N) -> random_bytes_1(0, Acc) -> Acc; random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]). -check_callback_load(Callback)-> +check_callback_load(Callback) -> case code:is_loaded(Callback) of {file, _Filename}-> {ok, Callback}; @@ -698,16 +697,16 @@ check_callback_load(Callback)-> end end. -check_config_files(Configs)-> +check_config_files(Configs) -> ConfigChecker = fun - ({Callback, [F|_R]=Files})-> + ({Callback, [F|_R]=Files}) -> case check_callback_load(Callback) of {ok, Callback}-> if - is_integer(F)-> + is_integer(F) -> Callback:check_parameter(Files); - is_list(F)-> - lists:map(fun(File)-> + is_list(F) -> + lists:map(fun(File) -> Callback:check_parameter(File) end, Files) @@ -715,7 +714,7 @@ check_config_files(Configs)-> {error, _}-> {error, {callback, Callback}} end; - ({Callback, []})-> + ({Callback, []}) -> case check_callback_load(Callback) of {ok, Callback}-> Callback:check_parameter([]); @@ -725,46 +724,46 @@ check_config_files(Configs)-> end, lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))). -prepare_user_configs([ConfigString|UserConfigs], Acc, new)-> +prepare_user_configs([ConfigString|UserConfigs], Acc, new) -> prepare_user_configs(UserConfigs, [{list_to_atom(ConfigString), []}|Acc], cur); -prepare_user_configs(["and"|UserConfigs], Acc, _)-> +prepare_user_configs(["and"|UserConfigs], Acc, _) -> prepare_user_configs(UserConfigs, Acc, new); -prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur)-> +prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur) -> prepare_user_configs(UserConfigs, [{LastMod, [ConfigString|LastList]}|Acc], cur); -prepare_user_configs([], Acc, _)-> +prepare_user_configs([], Acc, _) -> Acc. -prepare_config_list(Args)-> +prepare_config_list(Args) -> ConfigFiles = case lists:keysearch(ct_config, 1, Args) of - {value,{ct_config,Files}}-> - [{?ct_config_txt, Files}]; - false-> - [] - end, + {value,{ct_config,Files}}-> + [{?ct_config_txt,[filename:absname(F) || F <- Files]}]; + false-> + [] + end, UserConfigs = case lists:keysearch(userconfig, 1, Args) of - {value,{userconfig,UserConfigFiles}}-> - prepare_user_configs(UserConfigFiles, [], new); - false-> - [] - end, + {value,{userconfig,UserConfigFiles}}-> + prepare_user_configs(UserConfigFiles, [], new); + false-> + [] + end, ConfigFiles ++ UserConfigs. % TODO: add logging of the loaded configuration file to the CT FW log!!! -add_config(Callback, [])-> +add_config(Callback, []) -> read_config_files_int([{Callback, []}], fun store_config/3); -add_config(Callback, [File|_Files]=Config) when is_list(File)-> - lists:foreach(fun(CfgStr)-> +add_config(Callback, [File|_Files]=Config) when is_list(File) -> + lists:foreach(fun(CfgStr) -> read_config_files_int([{Callback, CfgStr}], fun store_config/3) end, Config); -add_config(Callback, [C|_]=Config) when is_integer(C)-> +add_config(Callback, [C|_]=Config) when is_integer(C) -> read_config_files_int([{Callback, Config}], fun store_config/3), ok. -remove_config(Callback, Config)-> +remove_config(Callback, Config) -> ets:match_delete(?attr_table, #ct_conf{handler=Callback, config=Config,_='_'}), -- cgit v1.2.3 From 7c6504029f84c52de40a6b29b2fd1b17c053ccec Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 3 Jun 2010 14:34:09 +0200 Subject: Add event_handler_init start flag that can pass init arguments to event handlers Also changed: The userconfig option in ct:run_test/1 from 3-tuple to 2-tuple. --- lib/common_test/src/ct_config.erl | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index ee84164ad7..a7b8a9e906 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -181,15 +181,22 @@ process_default_configs(Opts) -> process_user_configs(Opts, Acc) -> case lists:keytake(userconfig, 1, Opts) of - false-> - Acc; - {value, {userconfig, {Callback, []}}, NewOpts}-> + false -> + lists:reverse(Acc); + {value, {userconfig, Config=[{_,_}|_]}, NewOpts} -> + Acc1 = lists:map(fun({_Callback, []}=Cfg) -> + Cfg; + ({Callback, Files=[File|_]}) when is_list(File) -> + {Callback, Files}; + ({Callback, File=[C|_]}) when is_integer(C) -> + {Callback, [File]} + end, Config), + process_user_configs(NewOpts, lists:reverse(Acc1)++Acc); + {value, {userconfig, {Callback, []}}, NewOpts} -> process_user_configs(NewOpts, [{Callback, []} | Acc]); - {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when - is_list(File) -> + {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when is_list(File) -> process_user_configs(NewOpts, [{Callback, Files} | Acc]); - {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when - is_integer(C) -> + {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when is_integer(C) -> process_user_configs(NewOpts, [{Callback, [File]} | Acc]) end. -- cgit v1.2.3 From 470063d25f6128ef83cba6864533fc05e45cca74 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Thu, 3 Jun 2010 22:48:10 +0200 Subject: Flush old DOWN messages in demonitor --- lib/common_test/src/ct_config.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index a7b8a9e906..a6ade3f66b 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -111,7 +111,7 @@ call(Msg) -> ct_config_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}} -- cgit v1.2.3 From 34cf18550ff792ed9da884b00a49d6accd1bd5f5 Mon Sep 17 00:00:00 2001 From: Peter Andersson Date: Fri, 4 Jun 2010 15:34:42 +0200 Subject: Add support for dynamic timetrap handling --- lib/common_test/src/ct_config.erl | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'lib/common_test/src/ct_config.erl') diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index a6ade3f66b..dc6fcc66e5 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -694,39 +694,48 @@ random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]). check_callback_load(Callback) -> case code:is_loaded(Callback) of {file, _Filename}-> - {ok, Callback}; + check_exports(Callback); false-> case code:load_file(Callback) of {module, Callback}-> - {ok, Callback}; + check_exports(Callback); {error, Error}-> {error, Error} end end. +check_exports(Callback) -> + Fs = Callback:module_info(exports), + case {lists:member({check_parameter,1},Fs), + lists:member({read_config,1},Fs)} of + {true, true} -> + {ok, Callback}; + _ -> + {error, missing_callback_functions} + end. + check_config_files(Configs) -> ConfigChecker = fun ({Callback, [F|_R]=Files}) -> case check_callback_load(Callback) of - {ok, Callback}-> - if - is_integer(F) -> + {ok, Callback} -> + if is_integer(F) -> Callback:check_parameter(Files); - is_list(F) -> + is_list(F) -> lists:map(fun(File) -> - Callback:check_parameter(File) - end, - Files) + Callback:check_parameter(File) + end, + Files) end; - {error, _}-> - {error, {callback, Callback}} + {error, Why}-> + {error, {callback, {Callback,Why}}} end; ({Callback, []}) -> case check_callback_load(Callback) of {ok, Callback}-> Callback:check_parameter([]); - {error, _}-> - {error, {callback, Callback}} + {error, Why}-> + {error, {callback, {Callback,Why}}} end end, lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))). -- cgit v1.2.3