Current section
Files
Jump to
Current section
Files
src/escalus_story.erl
%%==============================================================================
%% Copyright 2010 Erlang Solutions Ltd.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%==============================================================================
-module(escalus_story).
% Public API
-export([story/3,
make_everyone_friends/1,
make_everyone_friends/2,
start_ready_clients/2,
send_initial_presence/1]).
-include("escalus.hrl").
-include_lib("test_server/include/test_server.hrl").
%%--------------------------------------------------------------------
%% Public API
%%--------------------------------------------------------------------
story(ConfigIn, ResourceCounts, Story) ->
ClientDescs = clients_from_resource_counts(ConfigIn, ResourceCounts),
try
Config = escalus_server:pre_story(ConfigIn),
Clients = start_clients(Config, ClientDescs),
ensure_all_clean(Clients),
escalus_event:story_start(Config),
apply_w_arity_check(Story, Clients),
escalus_event:story_end(Config),
post_story_checks(Config, Clients),
escalus_server:post_story(Config)
catch Class:Reason ->
Stacktrace = erlang:get_stacktrace(),
escalus_event:print_history(ConfigIn),
erlang:raise(Class, Reason, Stacktrace)
after
escalus_cleaner:clean(ConfigIn)
end.
make_everyone_friends(Config) ->
Users = escalus_config:get_config(escalus_users, Config),
make_everyone_friends(Config, Users).
make_everyone_friends(Config0, Users) ->
% start the clients
Config1 = escalus_cleaner:start(Config0),
Clients = start_clients(Config1, [[{US, <<"friendly">>}] || {_Name, US} <- Users]),
% exchange subscribe and subscribed stanzas
escalus_utils:distinct_pairs(fun(C1, C2) ->
send_presence(C1, <<"subscribe">>, C2),
swallow_stanzas(C1, 1, 0),
swallow_stanzas(C2, 0, 1),
send_presence(C2, <<"subscribe">>, C1),
swallow_stanzas(C1, 1, 1),
swallow_stanzas(C2, 1, 0),
send_presence(C2, <<"subscribed">>, C1),
swallow_stanzas(C1, 1, 2),
swallow_stanzas(C2, 1, 0),
send_presence(C1, <<"subscribed">>, C2),
swallow_stanzas(C1, 1, 0),
swallow_stanzas(C2, 1, 2)
end, Clients),
ensure_all_clean(Clients),
% stop the clients
escalus_cleaner:clean(Config1),
escalus_cleaner:stop(Config1),
% return Config0
[{everyone_is_friends, true} | Config0].
call_start_ready_clients(Config, UserCDs) ->
escalus_overridables:do(Config, start_ready_clients, [Config, UserCDs],
{?MODULE, start_ready_clients}).
start_ready_clients(Config, FlatCDs) ->
{_, RClients} = lists:foldl(fun({UserSpec, BaseResource}, {N, Acc}) ->
Resource = escalus_overridables:do(Config, modify_resource, [BaseResource],
{escalus_utils, identity}),
{ok, Client} = escalus_client:start(Config, UserSpec, Resource),
escalus_overridables:do(Config, initial_activity, [Client],
{?MODULE, send_initial_presence}),
%% drop 1 own presence + N-1 probe replies = N presence stanzas
drop_presences(Client, N),
{N+1, [Client|Acc]}
end, {1, []}, FlatCDs),
Clients = lists:reverse(RClients),
ClientsCount = length(Clients),
escalus_utils:each_with_index(fun(Client, N) ->
%% drop presence updates of guys who have logged in after you did
drop_presences(Client, ClientsCount - N)
end, 1, Clients),
ensure_all_clean(Clients),
Clients.
send_initial_presence(Client) ->
escalus_client:send(Client, escalus_stanza:presence(<<"available">>)).
%%--------------------------------------------------------------------
%% Helpers
%%--------------------------------------------------------------------
send_presence(From, Type, To) ->
ToJid = escalus_client:short_jid(To),
Stanza = escalus_stanza:presence_direct(ToJid, Type),
escalus_client:send(From, Stanza).
swallow_stanzas(User, NoRosters, NoPresences) ->
Rosters = lists:duplicate(NoRosters, is_roster_set),
Presences = lists:duplicate(NoPresences, is_presence),
Stanzas = escalus:wait_for_stanzas(User, NoRosters + NoPresences),
escalus:assert_many(Rosters ++ Presences, Stanzas).
ensure_all_clean(Clients) ->
lists:foreach(fun(Client) ->
escalus_assert:has_no_stanzas(Client)
end, Clients).
start_clients(Config, ClientDescs) ->
case proplists:get_bool(everyone_is_friends, Config) of
true ->
call_start_ready_clients(Config, lists:append(ClientDescs));
false ->
lists:flatmap(fun(UserCDs) ->
call_start_ready_clients(Config, UserCDs)
end, ClientDescs)
end.
drop_presences(Client, N) ->
Dropped = escalus_client:wait_for_stanzas(Client, N),
[escalus:assert(is_presence, Stanza) || Stanza <- Dropped],
N = length(Dropped).
post_story_checks(Config, Clients) ->
case proplists:get_bool(escalus_no_stanzas_after_story, Config) of
true ->
lists:foreach(
fun escalus_assert:has_no_stanzas/1,
Clients
);
_ ->
ok
end.
zip_shortest([H1|T1], [H2|T2]) ->
[{H1,H2}|zip_shortest(T1, T2)];
zip_shortest(_, _) ->
[].
%% ResourceCounts is a list of tuples: [{alice,2}, {bob,1}]
clients_from_resource_counts(Config, ResourceCounts = [{_, _} | _]) ->
NamedSpecs = escalus_config:get_config(escalus_users, Config),
[resources_per_spec(UserSpec, ResCount)
|| { User, ResCount} <- ResourceCounts,
{_User, UserSpec} <- [lists:keyfind(User, 1, NamedSpecs)]];
%% Old-style ResourceCounts: [2, 1]
clients_from_resource_counts(Config, ResourceCounts) ->
Deprecated = io_lib:format("DEPRECATED resource counts ~p (use [{alice, 1}, ...] or similar)",
[ResourceCounts]),
escalus_compat:complain(Deprecated),
NamedSpecs = escalus_config:get_config(escalus_users, Config),
[resources_per_spec(UserSpec, ResCount)
|| {{_, UserSpec}, ResCount} <- zip_shortest(NamedSpecs,
ResourceCounts)].
resources_per_spec(UserSpec, ResCount) ->
[{UserSpec, list_to_binary("res" ++ integer_to_list(N))}
|| N <- lists:seq(1, ResCount)].
apply_w_arity_check(Fun, Args) when is_function(Fun, 1) ->
case length(Args) of
1 -> apply(Fun, Args); %% Fun expects one logged-in user
_ -> apply(Fun, [Args]) %% Fun expects list of users
end;
apply_w_arity_check(Fun, Args) when is_function(Fun) ->
apply(Fun, Args).