Browse files

add interaction_server

  • Loading branch information...
1 parent cc7697e commit f684964178a747dab7a10efaab046c72cbf5f181 @nniclausse nniclausse committed Sep 11, 2012
View
2 examples/jabber_node.xml.in
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<!DOCTYPE tsung SYSTEM "/usr/share/tsung/tsung-1.0.dtd">
+<!DOCTYPE tsung SYSTEM "@prefix@/share/@PACKAGE_NAME@/@DTD@">
<tsung loglevel="debug" version="1.0">
<!-- Client side setup -->
View
4 src/test/ts_test_all.erl
@@ -26,4 +26,6 @@ all_test_() -> [ts_test_recorder,
ts_test_mon,
ts_test_user_server,
ts_test_search,
- ts_test_stats].
+ ts_test_stats,
+ ts_test_interaction
+ ].
View
18 src/test/ts_test_config.erl
@@ -19,14 +19,16 @@ test()->
popularity_test() ->
?assertError({"can't mix probabilites and weights",10,10}, ts_config:get_popularity(10,10,undefined,100)),
- ?assertError({"can't use probability when using weight"}, ts_config:get_popularity(10,0,true,100)),
- ?assertError({"can't use weights when using probabilities"}, ts_config:get_popularity(0,10,false,100)),
- ?assertEqual({10,false,110}, ts_config:get_popularity(10,0,false,100)),
- ?assertEqual({10,true,110}, ts_config:get_popularity(0,10,true,100)),
- ?assertEqual({30,false,60}, ts_config:get_popularity(30,0,false,30)),
- ?assertEqual({0,false,100}, ts_config:get_popularity(0,0,undefined,100)),
- ?assertEqual({0,true,100}, ts_config:get_popularity(0,0,true,100)),
- ?assertEqual({0,false,100}, ts_config:get_popularity(0,0,false,100)).
+ ?assertError({"can't use probability when using weight"}, ts_config:get_popularity(10,-1,true,100)),
+ ?assertError({"can't use weights when using probabilities"}, ts_config:get_popularity(-1,10,false,100)),
+ ?assertEqual({10,false,110}, ts_config:get_popularity(10,-1,false,100)),
+ ?assertEqual({10,true,110}, ts_config:get_popularity(-1,10,true,100)),
+ ?assertEqual({30,false,60}, ts_config:get_popularity(30,-1,false,30)),
+ ?assertError({"must set weight or probability in session"} , ts_config:get_popularity(-1,-1,undefined,100)),
+ ?assertError({"can't mix probabilites and weights",0,0}, ts_config:get_popularity(0,0,true,100)),
+ ?assertError({"can't mix probabilites and weights",0,0}, ts_config:get_popularity(0,0,false,100)),
+ ?assertEqual({0,true,100}, ts_config:get_popularity(-1,0,true,100)),
+ ?assertEqual({0,false,100}, ts_config:get_popularity(0,-1,false,100)).
read_config_http_test() ->
myset_env(),
View
55 src/test/ts_test_interaction.erl
@@ -0,0 +1,55 @@
+%%%-------------------------------------------------------------------
+%%% File : ts_test_interaction.erl
+%%% Author : Nicolas Niclausse <nicolas@niclux.org>
+%%% Description :
+%%%
+%%% Created : 28 Aug 2012 by Nicolas Niclausse <nicolas@niclux.org>
+%%%-------------------------------------------------------------------
+-module(ts_test_interaction).
+
+-compile(export_all).
+
+-include("ts_profile.hrl").
+-include("ts_config.hrl").
+
+-include_lib("eunit/include/eunit.hrl").
+
+
+test()->
+ ok.
+
+notify_test()->
+ myset_env(0),
+ ts_interaction_server:start(),
+ ts_interaction_server:send({chat,now()}),
+ ts_interaction_server:notify({'receive',chat,self()}),
+ ts_interaction_server:rcv({chat,now()}),
+ Res = receive
+ Data ->
+ erlang:display(["received 1",Data]),
+ ok
+ after 1000 ->
+ timeout
+ end,
+ ?assertMatch(ok, Res ).
+
+notify_to_test()->
+ myset_env(0),
+ ts_interaction_server:notify({send,chat2,self()}),
+ ts_interaction_server:send({chat2,now()}),
+ Res = receive
+ Data ->
+ erlang:display(["received 2",Data]),
+ ok
+ after 2000 ->
+ timeout
+ end,
+ ?assertMatch(ok, Res ).
+
+myset_env()->
+ myset_env(0).
+myset_env(Val)->
+ application:set_env(stdlib,dumpstats_interval,550),
+ application:set_env(stdlib,mon_file,"test-mon.log"),
+ application:set_env(stdlib,debug_level,Val).
+
View
6 src/tsung/ts_client.erl
@@ -448,6 +448,12 @@ handle_next_action(State) ->
{set_option, Type, Name, Args} ->
NewState=Type:set_option(Name,Args,State),
handle_next_action(NewState);
+ {interaction, send, Id} ->
+ ts_interaction_server:send({ts_search:subst(Id, State#state_rcv.dynvars),?NOW}),
+ handle_next_action(State#state_rcv{count=Count});
+ {interaction, 'receive', Id} ->
+ ts_interaction_server:rcv({ts_search:subst(Id, State#state_rcv.dynvars),?NOW}),
+ handle_next_action(State#state_rcv{count=Count});
Other ->
?LOGF("Error: set profile return value is ~p (count=~p)~n",[Other,Count],?ERR),
{stop, set_profile_error, State}
View
25 src/tsung_controller/ts_config.erl
@@ -358,8 +358,8 @@ parse(Element = #xmlElement{name=session, attributes=Attrs},
Name = getAttr(Attrs, name),
?LOGF("Session name for id ~p is ~p~n",[Id+1, Name],?NOTICE),
?LOGF("Session type: persistent=~p, bidi=~p~n",[Persistent,Bidi],?INFO),
- Probability = getAttr(float_or_integer, Attrs, probability, 0),
- Weight = getAttr(float_or_integer, Attrs, weight, 0),
+ Probability = getAttr(float_or_integer, Attrs, probability, -1),
+ Weight = getAttr(float_or_integer, Attrs, weight, -1),
{Popularity, NewUseWeights, NewTotal} = get_popularity(Probability, Weight, Conf#config.use_weights,Conf#config.total_popularity),
NewSList = case SList of
[] -> []; % first session
@@ -571,6 +571,17 @@ parse( #xmlElement{name=change_type, attributes=Attrs},
sessions=[CurS#session{type=CType}|Other] };
+parse( #xmlElement{name=interaction, attributes=Attrs},
+ Conf = #config{sessions=[CurS|_Other], curid=Id,session_tab = Tab}) ->
+
+ Action = list_to_atom(getAttr(string, Attrs, action, "send")),
+ RawId = getAttr(Attrs, id),
+ {ok, [{atom,1,IdInteraction}],1} = erl_scan:string("tr_"++RawId),
+
+ ets:insert(Tab,{{CurS#session.id, Id+1}, {interaction, Action, IdInteraction}}),
+ ?LOGF("Parse interaction ~p:~p ~n",[Action,Id],?NOTICE),
+ Conf#config{curid=Id+1 };
+
parse( Element = #xmlElement{name=set_option, attributes=Attrs},
Conf = #config{sessions=[CurS|_Other], curid=Id,session_tab = Tab}) ->
@@ -1025,13 +1036,15 @@ get_dynvar_name(VarNameStr) ->
%% @spec get_popularity(Proba::number(),Weight::number(),UseWeight:true|false|undefined, Total::number()) ->
%% {Value::number(), UseWeight:boolean(), Total:: number()}
%% check if we are using popularity or weights; keep the total up to date.
-get_popularity(Proba,Weight,_,_) when is_number(Proba), Proba > 0, is_number(Weight), Weight > 0 ->
+get_popularity(-1, -1, _, _)->
+ erlang:error({"must set weight or probability in session"});
+get_popularity(Proba,Weight,_,_) when is_number(Proba), Proba >= 0, is_number(Weight), Weight >= 0 ->
erlang:error({"can't mix probabilites and weights", Proba, Weight} );
-get_popularity(Proba, _Weight, true,_) when is_number(Proba), Proba > 0->
+get_popularity(Proba, _Weight, true,_) when is_number(Proba), Proba >= 0->
erlang:error({"can't use probability when using weight"});
-get_popularity(_, Weight, false,_) when is_number(Weight), Weight > 0->
+get_popularity(_, Weight, false,_) when is_number(Weight), Weight >= 0->
erlang:error({"can't use weights when using probabilities"});
-get_popularity(_, Weight, undefined,_) when is_number(Weight), Weight > 0 ->
+get_popularity(_, Weight, undefined,_) when is_number(Weight), Weight >= 0 ->
{Weight, true, Weight};
get_popularity(Proba, _, undefined,Total) when is_number(Proba) ->
{Proba, false, Proba+Total};
View
4 src/tsung_controller/ts_controller_sup.erl
@@ -85,9 +85,11 @@ init([LogDir]) ->
supervisor,[ts_user_server_sup]},
Notify = {ts_job_notify, {ts_job_notify, start_link, []}, transient, 2000,
worker, [ts_job_notify]},
+ Interaction = {ts_interaction_server, {ts_interaction_server, start, []}, transient, 2000,
+ worker, [ts_interaction_server]},
{ok,{{one_for_one,?retries,10},
[Config, Mon, Stats_Mon, Request_Mon, Page_Mon, Connect_Mon, Transaction_Mon,
- Match_Log, Timer, Msg, Notify,UserSup, ErlangSup, MuninSup,SNMPSup]}}.
+ Match_Log, Timer, Msg, Notify, Interaction, UserSup, ErlangSup, MuninSup,SNMPSup]}}.
%%%----------------------------------------------------------------------
%%% Internal functions
View
241 src/tsung_controller/ts_interaction_server.erl
@@ -0,0 +1,241 @@
+%%%-------------------------------------------------------------------
+%%% @author Nicolas Niclausse <nicolas@niclux.org>
+%%% @copyright (C) 2012, Nicolas Niclausse <nicolas@niclux.org>
+%%% @doc
+%%%
+%%% This program is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation; either version 2 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with this program; if not, write to the Free Software
+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+%%%
+%%% In addition, as a special exception, you have the permission to
+%%% link the code of this program with any library released under
+%%% the EPL license and distribute linked combinations including
+%%% the two.
+%%%
+%%% @end
+%%% Created: 20 août 2009 by Nicolas Niclausse <nicolas@niclux.org>
+%%%-------------------------------------------------------------------
+-module(ts_interaction_server).
+
+-behaviour(gen_server).
+
+-include("ts_macros.hrl").
+
+%% API
+-export([start/0, send/1, rcv/1, notify/1, delete/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {to, notify}).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @spec start() -> {ok, Pid} | ignore | {error, Error}
+%% @end
+%%--------------------------------------------------------------------
+start() ->
+ gen_server:start_link({global, ?SERVER}, ?MODULE, [], []).
+
+
+%%--------------------------------------------------------------------
+%% @doc
+%%
+%%
+%% @spec to({StatsName, Date}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+send({StatsName, Date}) when is_atom(StatsName) ->
+ gen_server:cast({global, ?SERVER}, {send, StatsName, Date}).
+
+%%--------------------------------------------------------------------
+%% @doc
+%%
+%%
+%% @spec receive({StatsName, Date}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+rcv({StatsName, Date}) when is_atom(StatsName) ->
+ gen_server:cast({global, ?SERVER}, {'receive', StatsName, Date}).
+
+
+%%--------------------------------------------------------------------
+%% @doc
+%%
+%%
+%% @spec delete({StatsName}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+delete({StatsName}) when is_atom(StatsName) ->
+ gen_server:cast({global, ?SERVER}, {delete, StatsName}).
+
+%%--------------------------------------------------------------------
+%% @doc
+%%
+%%
+%% @spec from({Action, StatsName, Pid}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+notify({Action, StatsName, Pid}) when is_atom(Action), is_atom(StatsName), is_pid(Pid) ->
+ gen_server:cast({global, ?SERVER}, {notify,Action,StatsName,Pid}).
+
+
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+ {ok, #state{to=ets:new(to, []), notify=ets:new(notify, [bag])}}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @spec handle_call(Request, From, State) ->
+%% {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_cast({send, StatsName, Date}, State=#state{to=To, notify=Notify}) ->
+ case ets:lookup(To,StatsName) of
+ [] ->
+ ?LOGF("interaction: setting send timestamp for ~p~n",[StatsName],?DEB);
+ _Val ->
+ ?LOGF("interaction: resetting send timestamp for ~p~n",[StatsName],?WARN)
+ end,
+ ets:insert(To,{StatsName, Date}),
+ handle_notification(Notify,{send, StatsName}),
+ {noreply, State};
+
+handle_cast({'receive',StatsName, EndDate}, State=#state{to=To, notify=Notify}) ->
+ ?LOGF("~p ~n",[StatsName],?DEB),
+ case ets:lookup(To,StatsName) of
+ [] ->
+ handle_notification(Notify,{'receive',StatsName}),
+ {noreply, State};
+ [{_Key, StartDate}] ->
+ ?LOGF("to/from ended, logging ~p ~n",[StatsName],?DEB),
+ handle_notification(Notify,{'receive',StatsName}),
+ ts_mon:add({sample,StatsName, ts_utils:elapsed(StartDate,EndDate)}),
+ {noreply, State}
+ end;
+
+handle_cast({delete, StatsName}, State=#state{to=To}) ->
+ ets:delete(To,StatsName),
+ {noreply, State};
+
+handle_cast({notify, Action, StatsName, Pid}, State=#state{notify=Notify}) ->
+ %% TODO: check if event already exists ?
+ ets:insert(Notify,{{StatsName, Action}, {Pid}}),
+ {noreply, State};
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+handle_notification(Notify, {Action, StatsName}) ->
+ case ets:lookup(Notify, {StatsName, Action}) of
+ [] ->
+ ok;
+ List ->
+ ?LOGF("gotlist ~p~n",List,?DEB),
+ Fun = fun({{Stats, Action2},{Pid}}) ->
+ ?LOGF("sending msg to pid ~p~n",[Pid],?DEB),
+ Pid ! {notify, Action2, Stats}
+ end,
+ lists:foreach(Fun,List),
+ ets:delete(Notify,StatsName)
+ end.
View
27 tsung-1.0.dtd
@@ -113,7 +113,7 @@
<!ELEMENT sessions (session+)>
<!ELEMENT session ( request | thinktime | transaction | setdynvars | for |
-repeat | if | change_type | foreach | set_option)*>
+repeat | if | change_type | foreach | set_option | interaction )*>
<!ATTLIST session
name CDATA #REQUIRED
bidi CDATA #IMPLIED
@@ -122,6 +122,11 @@ repeat | if | change_type | foreach | set_option)*>
weight NMTOKEN #IMPLIED
type (ts_jabber | ts_http | ts_raw | ts_pgsql | ts_ldap | ts_webdav |ts_mysql| ts_fs |ts_shell|ts_job) #REQUIRED>
+<!ELEMENT interaction EMPTY>
+<!ATTLIST interaction
+ action (send|receive) #REQUIRED
+ id CDATA #REQUIRED>
+
<!ELEMENT change_type EMPTY>
<!ATTLIST change_type
new_type (ts_jabber | ts_http | ts_raw | ts_pgsql | ts_ldap | ts_webdav |ts_mysql| ts_fs | ts_shell|ts_job) #REQUIRED
@@ -165,7 +170,7 @@ repeat | if | change_type | foreach | set_option)*>
>
<!ELEMENT transaction (request | setdynvars | thinktime | for | repeat
- | if | foreach)+>
+ | if | foreach | interaction )+>
<!ATTLIST transaction name NMTOKEN #REQUIRED>
<!ELEMENT http ( www_authenticate?, soap?, http_header*, add_cookie*)>
@@ -183,12 +188,12 @@ repeat | if | change_type | foreach | set_option)*>
<!ELEMENT dyn_variable EMPTY >
<!ATTLIST dyn_variable
- name CDATA #REQUIRED
- xpath CDATA #IMPLIED
- re CDATA #IMPLIED
- jsonpath CDATA #IMPLIED
+ name CDATA #REQUIRED
+ xpath CDATA #IMPLIED
+ re CDATA #IMPLIED
+ jsonpath CDATA #IMPLIED
pgsql_expr CDATA #IMPLIED
- regexp CDATA #IMPLIED >
+ regexp CDATA #IMPLIED >
<!ELEMENT http_header EMPTY >
<!ATTLIST http_header
@@ -343,15 +348,15 @@ repeat | if | change_type | foreach | set_option)*>
name CDATA #REQUIRED>
<!ELEMENT for (request | thinktime | transaction | setdynvars | for |
- if | repeat | change_type | foreach )+>
+ if | repeat | change_type | foreach | interaction )+>
<!ATTLIST for
var CDATA #REQUIRED
from CDATA #REQUIRED
to CDATA #REQUIRED
incr NMTOKEN "1">
<!ELEMENT foreach (request | thinktime | transaction | setdynvars | foreach |
- if | repeat | change_type | for )+>
+ if | repeat | change_type | for | interaction)+>
<!ATTLIST foreach
name NMTOKEN #REQUIRED
in NMTOKEN #REQUIRED
@@ -360,13 +365,13 @@ repeat | if | change_type | foreach | set_option)*>
>
<!ELEMENT repeat (request | thinktime | transaction | setdynvars | for | repeat
-| while | if | until | change_type | foreach )+>
+| while | if | until | change_type | foreach | interaction)+>
<!ATTLIST repeat
name NMTOKEN #REQUIRED
max_repeat NMTOKEN "20">
<!ELEMENT if (request | thinktime | transaction | setdynvars | for | repeat
-| while | if | until | change_type | foreach)+>
+| while | if | until | change_type | foreach | interaction)+>
<!ATTLIST if
var CDATA #REQUIRED
eq CDATA #IMPLIED

0 comments on commit f684964

Please sign in to comment.