%%============================================================================== %% 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_utils). -export([log_stanzas/2, pretty_stanza_list/1, distinct_pairs/2, distinct_ordered_pairs/2, each_with_index/3, all_true/1, any_true/1, identity/1, mix_match/3, get_jid/1, get_short_jid/1, jid_to_lower/1, get_username/1, get_server/1, get_resource/1, drop_first_such/2, show_backtrace/0, is_prefix/2, start_clients/1, start_clients/2, regexp_get/2]). -import(escalus_compat, [unimplemented/0, bin/1]). -include("escalus.hrl"). -include_lib("exml/include/exml.hrl"). -type jid_spec() :: #client{} | atom() | binary() | string(). -export_type([jid_spec/0]). -spec log_stanzas(iolist(), [#xmlel{}]) -> any(). log_stanzas(Comment, Stanzas) -> error_logger:info_msg("~s:~s~n", [Comment, stanza_lines("\n * ", Stanzas)]). -spec pretty_stanza_list([#xmlel{}]) -> string(). pretty_stanza_list(Stanzas) -> binary_to_list(list_to_binary(stanza_lines(" ", Stanzas))). %% calls Fun(A, B) on each distinct (A =/= B) pair of elements in List %% if Fun(A, B) was called, then Fun(B, A) won't distinct_pairs(Fun, List) -> K = each_with_index(fun(A, N) -> each_with_index(fun(B, M) -> if N < M -> Fun(A, B); true -> skip end end, 0, List) end, 0, List), K * (K - 1) div 2. %% calls Fun(A, B) on each distinct (A =/= B) ordered pair of elements in List distinct_ordered_pairs(Fun, List) -> K = each_with_index(fun(A, N) -> each_with_index(fun(B, M) -> if N =/= M -> Fun(A, B); true -> skip end end, 0, List) end, 0, List), K * (K - 1). %% Calls Fun(Element, Index) for indices (starting from Start) and elements of List each_with_index(Fun, Start, List) -> lists:foldl(fun(Element, N) -> Fun(Element, N), N + 1 end, Start, List). all_true(List) -> lists:foldl(fun erlang:'and'/2, true, List). any_true(List) -> lists:foldl(fun erlang:'or'/2, false, List). identity(X) -> X. %% Does for each Case in Cases exist a Cond in Conds such that %% (Predgen(Cond))(Case) == true? mix_match(Predgen, Conds, Cases) -> [] == lists:foldl(fun(Cond, CasesLeft) -> Pred = Predgen(Cond), drop_first_such(Pred, CasesLeft) end, Cases, Conds). drop_first_such(Pred, List) -> drop_first_such(Pred, List, []). drop_first_such(_, [], Acc) -> lists:reverse(Acc); drop_first_such(Pred, [H|T], Acc) -> case Pred(H) of true -> lists:reverse(Acc) ++ T; false -> drop_first_such(Pred, T, [H|Acc]) end. stanza_lines(Prefix, Stanzas) -> [[Prefix, exml:to_iolist(S)] || S <- Stanzas]. show_backtrace() -> try throw(catch_me) catch _:_:S -> error_logger:info_msg("Backtrace:~n~p~n", [tl(S)]) end. -spec get_jid(jid_spec()) -> binary(). get_jid(#client{jid=Jid}) -> Jid; get_jid(Username) when is_atom(Username) -> %% TODO: Drop this clause since it relies on external state - the config file. %% Define users in a test suite and pass them around as needed. Complaint = io_lib:format("~s:get_jid/1: an atom() passed as Username - this clause will " "be dropped in the future as it relies on an external config file", [?MODULE]), escalus_compat:complain(Complaint), bin(escalus_users:get_jid([], Username)); get_jid(Jid) when is_list(Jid) -> bin(Jid); get_jid(Jid) when is_binary(Jid) -> Jid. -spec get_short_jid(#client{} | atom() | binary() | string()) -> binary(). get_short_jid(#client{}=Recipient) -> escalus_client:short_jid(Recipient); get_short_jid(Username) when is_atom(Username) -> escalus_users:get_jid([], Username); get_short_jid(Jid) when is_list(Jid) -> get_short_jid(list_to_binary(Jid)); get_short_jid(Jid) when is_binary(Jid) -> regexp_get(Jid, <<"^([^@]*[@][^/]*)">>). -spec jid_to_lower(binary()) -> binary(). jid_to_lower(Jid) -> %% simplified lowercaseing list_to_binary(string:to_lower(binary_to_list(Jid))). -spec get_username(UserOrClient :: jid_spec()) -> binary(). get_username(UserOrClient) -> regexp_get(get_short_jid(UserOrClient), <<"^([^@]*)">>). -spec get_server(UserOrClient :: jid_spec()) -> binary(). get_server(UserOrClient) -> regexp_get(get_short_jid(UserOrClient), <<"^[^@]*[@]([^/]*)">>). -spec get_resource(JID :: binary()) -> binary(). get_resource(JID) -> regexp_get(JID, <<"^[^/]+/(.*)$">>). is_prefix(Prefix, Full) when is_binary(Prefix), is_binary(Full) -> LCP = binary:longest_common_prefix([Prefix, Full]), size(Prefix) =< size(Full) andalso LCP == size(Prefix). start_clients(Clients) -> start_clients([], Clients). start_clients(Config0, ClientRecipes) -> AllCDs = escalus_config:get_config(escalus_users, Config0), FlatCDs = [{CD, Res} || {Username, Resources} <- ClientRecipes, {_, CD} <- [lists:keyfind(Username, 1, AllCDs)], Res <- Resources], Config1 = [{_, Cleaner} | _] = escalus_cleaner:start(Config0), Clients = escalus_overridables:do(Config0, start_ready_clients, [Config1, FlatCDs], {escalus_story, start_ready_clients}), {Cleaner, Clients}. regexp_get(Jid, Regex) -> {match, [ShortJid]} = re:run(Jid, Regex, [{capture, all_but_first, binary}]), ShortJid.