diff options
Diffstat (limited to 'lib/common_test/src')
-rwxr-xr-x | lib/common_test/src/ct_config_xml.erl | 3 | ||||
-rw-r--r-- | lib/common_test/src/ct_master.erl | 113 | ||||
-rw-r--r-- | lib/common_test/src/ct_slave.erl | 1 | ||||
-rw-r--r-- | lib/common_test/src/ct_testspec.erl | 155 | ||||
-rw-r--r-- | lib/common_test/src/ct_util.hrl | 2 |
5 files changed, 141 insertions, 133 deletions
diff --git a/lib/common_test/src/ct_config_xml.erl b/lib/common_test/src/ct_config_xml.erl index 111d1426c9..4ced80aeac 100755 --- a/lib/common_test/src/ct_config_xml.erl +++ b/lib/common_test/src/ct_config_xml.erl @@ -24,9 +24,6 @@ -module(ct_config_xml). -export([read_config/1, check_parameter/1]). -% DEBUG ONLY --export([list_to_term/1]). - % read config file read_config(ConfigFile) -> case catch do_read_xml_config(ConfigFile) of diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index ecf516bd98..6cabe4c7a2 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -101,12 +101,12 @@ run([TS|TestSpecs],AllowUserTerms,InclNodes,ExclNodes) when is_list(TS), TSRec=#testspec{logdir=AllLogDirs, config=StdCfgFiles, userconfig=UserCfgFiles, - node_start=AllNSOpts, + init=AllInitOpts, event_handler=AllEvHs} -> AllCfgFiles = {StdCfgFiles, UserCfgFiles}, RunSkipPerNode = ct_testspec:prepare_tests(TSRec), RunSkipPerNode2 = exclude_nodes(ExclNodes,RunSkipPerNode), - run_all(RunSkipPerNode2,AllLogDirs,AllCfgFiles,AllEvHs,[],[],AllNSOpts,TS1) + run_all(RunSkipPerNode2,AllLogDirs,AllCfgFiles,AllEvHs,[],[],AllInitOpts,TS1) end, [{TS,Result} | run(TestSpecs,AllowUserTerms,InclNodes,ExclNodes)]; run([],_,_,_) -> @@ -162,12 +162,12 @@ run_on_node([TS|TestSpecs],AllowUserTerms,Node) when is_list(TS),is_atom(Node) - {error,Reason}; TSRec=#testspec{logdir=AllLogDirs, config=StdCfgFiles, - node_start=AllNSOpts, + init=AllInitOpts, userconfig=UserCfgFiles, event_handler=AllEvHs} -> AllCfgFiles = {StdCfgFiles, UserCfgFiles}, {Run,Skip} = ct_testspec:prepare_tests(TSRec,Node), - run_all([{Node,Run,Skip}],AllLogDirs,AllCfgFiles,AllEvHs,[],[],AllNSOpts,TS1) + run_all([{Node,Run,Skip}],AllLogDirs,AllCfgFiles,AllEvHs,[],[],AllInitOpts,TS1) end, [{TS,Result} | run_on_node(TestSpecs,AllowUserTerms,Node)]; run_on_node([],_,_) -> @@ -189,7 +189,7 @@ run_on_node(TestSpecs,Node) -> run_all([{Node,Run,Skip}|Rest],AllLogDirs, {AllStdCfgFiles, AllUserCfgFiles}=AllCfgFiles, - AllEvHs,NodeOpts,LogDirs,NSOptions,Specs) -> + AllEvHs,NodeOpts,LogDirs,InitOptions,Specs) -> LogDir = lists:foldl(fun({N,Dir},_Found) when N == Node -> Dir; @@ -221,15 +221,15 @@ run_all([{Node,Run,Skip}|Rest],AllLogDirs, {logdir,LogDir}, {config,StdCfgFiles}, {event_handler,EvHs}] ++ UserCfgFiles}, - run_all(Rest,AllLogDirs,AllCfgFiles,AllEvHs,[NO|NodeOpts],[LogDir|LogDirs],NSOptions,Specs); -run_all([],AllLogDirs,_,AllEvHs,NodeOpts,LogDirs,NSOptions,Specs) -> + run_all(Rest,AllLogDirs,AllCfgFiles,AllEvHs,[NO|NodeOpts],[LogDir|LogDirs],InitOptions,Specs); +run_all([],AllLogDirs,_,AllEvHs,NodeOpts,LogDirs,InitOptions,Specs) -> Handlers = [{H,A} || {Master,H,A} <- AllEvHs, Master == master], MasterLogDir = case lists:keysearch(master,1,AllLogDirs) of {value,{_,Dir}} -> Dir; false -> "." end, log(tty,"Master Logdir","~s",[MasterLogDir]), - start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,LogDirs,NSOptions,Specs), + start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,LogDirs,InitOptions,Specs), ok. @@ -269,15 +269,15 @@ progress() -> start_master(NodeOptsList) -> start_master(NodeOptsList,[],".",[],[],[]). -start_master(NodeOptsList,EvHandlers,MasterLogDir,LogDirs,NSOptions,Specs) -> +start_master(NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) -> Master = spawn_link(?MODULE,init_master,[self(),NodeOptsList,EvHandlers, - MasterLogDir,LogDirs,NSOptions,Specs]), + MasterLogDir,LogDirs,InitOptions,Specs]), receive {Master,Result} -> Result end. %%% @hidden -init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,NSOptions,Specs) -> +init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) -> case whereis(ct_master) of undefined -> register(ct_master,self()), @@ -330,11 +330,10 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,NSOptions,Specs) Pid when is_pid(Pid) -> ok end, - init_master1(Parent,NodeOptsList,NSOptions,LogDirs). + init_master1(Parent,NodeOptsList,InitOptions,LogDirs). -init_master1(Parent,NodeOptsList,NSOptions,LogDirs) -> - start_nodes(NSOptions), - {Inaccessible,NodeOptsList1} = ping_nodes(NodeOptsList,[],[]), +init_master1(Parent,NodeOptsList,InitOptions,LogDirs) -> + {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList,InitOptions), case Inaccessible of [] -> init_master2(Parent,NodeOptsList,LogDirs); @@ -348,7 +347,7 @@ init_master1(Parent,NodeOptsList,NSOptions,LogDirs) -> "Proceeding without: ~p",[Inaccessible]), init_master2(Parent,NodeOptsList1,LogDirs); "r\n" -> - init_master1(Parent,NodeOptsList,NSOptions,LogDirs); + init_master1(Parent,NodeOptsList,InitOptions1,LogDirs); _ -> log(html,"Aborting Tests","",[]), ct_master_event:stop(), @@ -559,6 +558,9 @@ get_pid(Node,NodeCtrlPids) -> undefined end. +ping_nodes(NodeOptions)-> + ping_nodes(NodeOptions, [], []). + ping_nodes([NO={Node,_Opts}|NOs],Inaccessible,NodeOpts) -> case net_adm:ping(Node) of pong -> @@ -702,21 +704,72 @@ reply(Result,To) -> To ! {self(),Result}, ok. -start_nodes([])-> - ok; -start_nodes([{NodeName, Options}|Rest])-> - [NodeS,HostS]=string:tokens(atom_to_list(NodeName), "@"), - Node=list_to_atom(NodeS), - Host=list_to_atom(HostS), - {value, {callback_module, Callback}, Options2}= - lists:keytake(callback_module, 1, Options), - case Callback:start(Host, Node, Options2) of - {ok, NodeName} -> - io:format("Node ~p started successfully with callback ~p~n", [NodeName,Callback]); - {error, Reason, _NodeName} -> - io:format("Failed to start node ~p with callback ~p! Reason: ~p~n", [NodeName, Callback, Reason]) +init_nodes(NodeOptions, InitOptions)-> + ping_nodes(NodeOptions), + start_nodes(InitOptions), + eval_on_nodes(InitOptions), + {Inaccessible, NodeOptions1}=ping_nodes(NodeOptions), + InitOptions1 = filter_accessible(InitOptions, Inaccessible), + {Inaccessible, NodeOptions1, InitOptions1}. + +% only nodes which are inaccessible now, should be initiated later +filter_accessible(InitOptions, Inaccessible)-> + [{Node,Option}||{Node,Option}<-InitOptions, lists:member(Node, Inaccessible)]. + +start_nodes(InitOptions)-> + lists:foreach(fun({NodeName, Options})-> + [NodeS,HostS]=string:tokens(atom_to_list(NodeName), "@"), + Node=list_to_atom(NodeS), + Host=list_to_atom(HostS), + HasNodeStart = lists:keymember(node_start, 1, Options), + IsAlive = lists:member(NodeName, nodes()), + case {HasNodeStart, IsAlive} of + {false, false}-> + io:format("WARNING: Node ~p is not alive but has no node_start option~n", [NodeName]); + {false, true}-> + io:format("Node ~p is alive~n", [NodeName]); + {true, false}-> + {node_start, NodeStart} = lists:keyfind(node_start, 1, Options), + {value, {callback_module, Callback}, NodeStart2}= + lists:keytake(callback_module, 1, NodeStart), + case Callback:start(Host, Node, NodeStart2) of + {ok, NodeName} -> + io:format("Node ~p started successfully with callback ~p~n", [NodeName,Callback]); + {error, Reason, _NodeName} -> + io:format("Failed to start node ~p with callback ~p! Reason: ~p~n", [NodeName, Callback, Reason]) + end; + {true, true}-> + io:format("WARNING: Node ~p is alive but has node_start option~n", [NodeName]) + end + end, + InitOptions). + +eval_on_nodes(InitOptions)-> + lists:foreach(fun({NodeName, Options})-> + HasEval = lists:keymember(eval, 1, Options), + IsAlive = lists:member(NodeName, nodes()), + case {HasEval, IsAlive} of + {false,_}-> + ok; + {true,false}-> + io:format("WARNING: Node ~p is not alive but has eval option ~n", [NodeName]); + {true,true}-> + {eval, MFAs} = lists:keyfind(eval, 1, Options), + evaluate(NodeName, MFAs) + end end, - start_nodes(Rest). + InitOptions). + +evaluate(Node, [{M,F,A}|MFAs])-> + case rpc:call(Node, M, F, A) of + {badrpc,Reason}-> + io:format("WARNING: Failed to call ~p:~p/~p on node ~p due to ~p~n", [M,F,length(A),Node,Reason]); + Result-> + io:format("Called ~p:~p/~p on node ~p, result: ~p~n", [M,F,length(A),Node,Result]) + end, + evaluate(Node, MFAs); +evaluate(_Node, [])-> + ok. %cast(Msg) -> % cast(whereis(ct_master),Msg). diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl index 476815895c..22cb17e55d 100644 --- a/lib/common_test/src/ct_slave.erl +++ b/lib/common_test/src/ct_slave.erl @@ -359,7 +359,6 @@ get_cmd(Node, Flags)-> spawn_local_node(Node, Options)-> ErlFlags = Options#options.erl_flags, Cmd = get_cmd(Node, ErlFlags), - %io:format("Running cmd: ~p~n", [Cmd]), open_port({spawn, Cmd}, [stream]). % start crypto and ssh if not yet started diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index db3efb16e8..4e34c1513e 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -270,52 +270,8 @@ collect_tests(Terms,TestSpec,Relaxed) -> put(relaxed,Relaxed), TestSpec1 = get_global(Terms,TestSpec), TestSpec2 = get_all_nodes(Terms,TestSpec1), - % filter out node_start options and save them into the specification - {Terms2, TestSpec3} = filter_nodestart_specs(Terms, [], TestSpec2), - % only save the 'global' evals and evals for nodes which have no node_start - {Terms3, TestSpec4} = filter_evals(Terms2, [], TestSpec3), - % after evaluation, only valid terms exist in the specification list - Terms4 = case catch evaluate(Terms3, [], TestSpec4) of - {error,{Node,{M,F,A},Reason}} -> - io:format("Error! Common Test failed to evaluate ~w:~w/~w on ~w. " - "Reason: ~p~n~n", [M,F,A,Node,Reason]), - Terms3; - NewTerms -> NewTerms - end, - add_tests(Terms4,TestSpec4). - -evaluate([{eval,Node,[{_,_,_}|_]=Mfas}|Ts],NewTerms,Spec)-> - EvalTerms = lists:map(fun(Mfa)-> - {eval, Node, Mfa} - end, - Mfas), - evaluate([EvalTerms|Ts], NewTerms, Spec); -evaluate([{eval,[{_,_,_}|_]=Mfas}|Ts],NewTerms,Spec)-> - EvalTerms = lists:map(fun(Mfa)-> - {eval, Mfa} - end, - Mfas), - evaluate([EvalTerms|Ts], NewTerms, Spec); -evaluate([{eval,Node,{M,F,Args}}|Ts],NewTerms,Spec) -> - case rpc:call(Node,M,F,Args) of - {badrpc,Reason} -> - throw({error,{Node,{M,F,length(Args)},Reason}}); - _ -> - ok - end, - evaluate(Ts,NewTerms,Spec); -evaluate([{eval,{M,F,Args}}|Ts],NewTerms,Spec) -> - case catch apply(M,F,Args) of - {'EXIT',Reason} -> - throw({error,{node(),{M,F,length(Args)},Reason}}); - _ -> - ok - end, - evaluate(Ts,NewTerms,Spec); -evaluate([Term|Ts], NewTerms, Spec)-> - evaluate(Ts, [Term|NewTerms], Spec); -evaluate([], NewTerms, _Spec) -> - NewTerms. + {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2), + add_tests(Terms2,TestSpec3). get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) -> get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]}); @@ -392,64 +348,67 @@ get_all_nodes([_|Ts],Spec) -> get_all_nodes([],Spec) -> Spec. -filter_nodestart_specs([{node_start, Options}|Ts], NewTerms, Spec) -> - filter_nodestart_specs([{node_start, list_nodes(Spec), Options}|Ts], NewTerms, Spec); -filter_nodestart_specs([{node_start, NodeRef, Options}|Ts], NewTerms, Spec) when is_atom(NodeRef) -> - filter_nodestart_specs([{node_start, [NodeRef], Options}|Ts], NewTerms, Spec); -filter_nodestart_specs([{node_start, NodeRefs, Options}|Ts], NewTerms, Spec=#testspec{node_start=NodeStart})-> - Options2 = case lists:keyfind(callback_module, 1, Options) of +filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)-> + filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec); +filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec) + when is_atom(NodeRef)-> + filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec); +filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) -> + filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec); +filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})-> + NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of + {node_start, NSOptions}-> + case lists:keyfind(callback_module, 1, NSOptions) of + {callback_module, _Callback}-> + NSOptions; + false-> + [{callback_module, ct_slave}|NSOptions] + end; false-> - [{callback_module, ct_slave}|Options]; - {callback_module, _Callback}-> - Options + [] end, - NSSAdder = fun(NodeRef, NodeStartAcc)-> - Node=ref2node(NodeRef,Spec#testspec.nodes), - case lists:keyfind(Node, 1, NodeStartAcc) of - false-> - [{Node, Options2}|NodeStartAcc]; - {Node, OtherOptions}-> - io:format("~nWarning: There are other options defined for node ~p:" - "~n~w, skipping ~n~w...~n", [Node, OtherOptions, Options2]), - NodeStartAcc - end + EvalTerms = case lists:keyfind(eval, 1, InitOptions) of + {eval, MFA} when is_tuple(MFA)-> + [MFA]; + {eval, MFAs} when is_list(MFAs)-> + MFAs; + false-> + [] end, - NodeStart2 = lists:foldl(NSSAdder, NodeStart, NodeRefs), - filter_nodestart_specs(Ts, NewTerms, Spec#testspec{node_start=NodeStart2}); -filter_nodestart_specs([Term|Ts], NewTerms, Spec)-> - filter_nodestart_specs(Ts, [Term|NewTerms], Spec); -filter_nodestart_specs([], NewTerms, Spec) -> + Node = ref2node(NodeRef,Spec#testspec.nodes), + InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true), + InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false), + filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3}); +filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)-> + filter_init_terms(Ts, NewTerms, Spec); +filter_init_terms([Term|Ts], NewTerms, Spec)-> + filter_init_terms(Ts, [Term|NewTerms], Spec); +filter_init_terms([], NewTerms, Spec)-> {NewTerms, Spec}. -filter_evals([{eval,NodeRefs,Mfa}|Ts], NewTerms, Spec) when is_list(NodeRefs)-> - EvalTerms = lists:map(fun(NodeRef)-> - {eval, NodeRef, Mfa} - end, - NodeRefs), - filter_evals(EvalTerms++Ts, NewTerms, Spec); -filter_evals([{eval,NodeRef,{_,_,_}=Mfa}|Ts],NewTerms,Spec)-> - filter_evals([{eval,NodeRef,[Mfa]}|Ts],NewTerms,Spec); -filter_evals([{eval,NodeRef,[{_,_,_}|_]=Mfas}=EvalTerm|Ts], - NewTerms,Spec=#testspec{node_start=NodeStart})-> - Node=ref2node(NodeRef,Spec#testspec.nodes), - case lists:keyfind(Node, 1, NodeStart) of - false-> - filter_evals(Ts, [EvalTerm|NewTerms], Spec); +add_option([], _, List, _)-> + List; +add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)-> + OldOptions = case lists:keyfind(Node, 1, List) of {Node, Options}-> - Options2 = case lists:keyfind(startup_functions, 1, Options) of - false-> - [{startup_functions, Mfas}|Options]; - {startup_functions, StartupFunctions}-> - lists:keyreplace(startup_functions, 1, Options, - {startup_functions, StartupFunctions ++ Mfas}) - end, - NodeStart2 = lists:keyreplace(Node, 1, NodeStart, {Node, Options2}), - filter_evals(Ts, NewTerms, Spec#testspec{node_start=NodeStart2}) - end; -filter_evals([Term|Ts], NewTerms, Spec)-> - filter_evals(Ts, [Term|NewTerms], Spec); -filter_evals([], NewTerms, Spec)-> - {NewTerms, Spec}. + Options; + false-> + [] + end, + NewOption = case lists:keyfind(Key, 1, OldOptions) of + {Key, OldOption} when WarnIfExists, OldOption/=[]-> + io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n", + [Key, OldOption, Node, Value]), + OldOption; + {Key, OldOption}-> + OldOption ++ Value; + false-> + Value + end, + lists:keystore(Node, 1, List, + {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})}); +add_option({Key, Value}, Node, List, WarnIfExists)-> + add_option({Key, [Value]}, Node, List, WarnIfExists). save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> NodeRefs1 = diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index 7c8fd67e1d..7ddb7d8c2b 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -29,7 +29,7 @@ -record(testspec, {spec_dir, nodes=[], - node_start=[], + init=[], logdir=["."], cover=[], config=[], |