Add tests for XEP-0013

This commit is contained in:
Evgeniy Khramtsov 2016-02-10 16:15:43 +03:00
parent 02a519a11e
commit d6323a7b5e
5 changed files with 363 additions and 4 deletions

View File

@ -110,7 +110,7 @@ edoc:
spec:
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
'case xml_gen:compile("tools/xmpp_codec.spec") of ok -> halt(0); _ -> halt(1) end.'
'case fxml_gen:compile("tools/xmpp_codec.spec") of ok -> halt(0); _ -> halt(1) end.'
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))

View File

@ -214,6 +214,8 @@ db_tests(riak) ->
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
{test_flex_offline, [sequence],
[flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
{test_muc, [parallel],
@ -245,6 +247,8 @@ db_tests(mnesia) ->
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
{test_flex_offline, [sequence],
[flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
{test_old_mam, [parallel],
@ -285,6 +289,8 @@ db_tests(_) ->
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
{test_flex_offline, [sequence],
[flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
{test_old_mam, [parallel],
@ -1433,6 +1439,131 @@ announce_slave(Config) ->
send(Config, #message{to = MotdDelJID}),
disconnect(Config).
flex_offline_master(Config) ->
Peer = ?config(slave, Config),
LPeer = jlib:jid_remove_resource(Peer),
lists:foreach(
fun(I) ->
Body = jlib:integer_to_binary(I),
send(Config, #message{to = LPeer,
body = [#text{data = Body}],
subject = [#text{data = <<"subject">>}]})
end, lists:seq(1, 5)),
disconnect(Config).
flex_offline_slave(Config) ->
MyJID = my_jid(Config),
MyBareJID = jid:remove_resource(MyJID),
Peer = ?config(master, Config),
Peer_s = jid:to_string(Peer),
true = is_feature_advertised(Config, ?NS_FLEX_OFFLINE),
%% Request disco#info
#iq{type = result,
sub_els = [#disco_info{
node = ?NS_FLEX_OFFLINE,
identities = Ids,
features = Fts,
xdata = [X]}]} =
send_recv(Config, #iq{type = get,
sub_els = [#disco_info{
node = ?NS_FLEX_OFFLINE}]}),
%% Check if we have correct identities
true = lists:any(
fun(#identity{category = <<"automation">>,
type = <<"message-list">>}) -> true;
(_) -> false
end, Ids),
%% Check if we have needed feature
true = lists:member(?NS_FLEX_OFFLINE, Fts),
%% Check xdata, the 'number_of_messages' should be 5
#xdata{type = result,
fields = [#xdata_field{type = hidden,
var = <<"FORM_TYPE">>},
#xdata_field{var = <<"number_of_messages">>,
values = [<<"5">>]}]} = X,
%% Fetch headers,
#iq{type = result,
sub_els = [#disco_items{
node = ?NS_FLEX_OFFLINE,
items = DiscoItems}]} =
send_recv(Config, #iq{type = get,
sub_els = [#disco_items{
node = ?NS_FLEX_OFFLINE}]}),
%% Check if headers are correct
Nodes = lists:sort(
lists:map(
fun(#disco_item{jid = J, name = P, node = N})
when (J == MyBareJID) and (P == Peer_s) ->
N
end, DiscoItems)),
%% Check full fetch
I0 = send(Config, #iq{type = get, sub_els = [#offline{fetch = true}]}),
lists:foreach(
fun({I, N}) ->
Text = jlib:integer_to_binary(I),
?recv1(#message{body = Body, sub_els = SubEls}),
[#text{data = Text}] = Body,
#offline{items = [#offline_item{node = N}]} =
lists:keyfind(offline, 1, SubEls),
#delay{} = lists:keyfind(delay, 1, SubEls)
end, lists:zip(lists:seq(1, 5), Nodes)),
?recv1(#iq{type = result, id = I0, sub_els = []}),
%% Fetch 2nd and 4th message
I1 = send(Config,
#iq{type = get,
sub_els = [#offline{
items = [#offline_item{
action = view,
node = lists:nth(2, Nodes)},
#offline_item{
action = view,
node = lists:nth(4, Nodes)}]}]}),
lists:foreach(
fun({I, N}) ->
Text = jlib:integer_to_binary(I),
?recv1(#message{body = [#text{data = Text}], sub_els = SubEls}),
#offline{items = [#offline_item{node = N}]} =
lists:keyfind(offline, 1, SubEls)
end, lists:zip([2, 4], [lists:nth(2, Nodes), lists:nth(4, Nodes)])),
?recv1(#iq{type = result, id = I1, sub_els = []}),
%% Delete 2nd and 4th message
#iq{type = result, sub_els = []} =
send_recv(
Config,
#iq{type = set,
sub_els = [#offline{
items = [#offline_item{
action = remove,
node = lists:nth(2, Nodes)},
#offline_item{
action = remove,
node = lists:nth(4, Nodes)}]}]}),
%% Check if messages were deleted
#iq{type = result,
sub_els = [#disco_items{
node = ?NS_FLEX_OFFLINE,
items = RemainedItems}]} =
send_recv(Config, #iq{type = get,
sub_els = [#disco_items{
node = ?NS_FLEX_OFFLINE}]}),
RemainedNodes = [lists:nth(1, Nodes),
lists:nth(3, Nodes),
lists:nth(5, Nodes)],
RemainedNodes = lists:sort(
lists:map(
fun(#disco_item{node = N}) -> N end,
RemainedItems)),
%% Purge everything left
#iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = set, sub_els = [#offline{purge = true}]}),
%% Check if there is no offline messages
#iq{type = result,
sub_els = [#disco_items{node = ?NS_FLEX_OFFLINE, items = []}]} =
send_recv(Config, #iq{type = get,
sub_els = [#disco_items{
node = ?NS_FLEX_OFFLINE}]}),
disconnect(Config).
offline_master(Config) ->
Peer = ?config(slave, Config),
LPeer = jlib:jid_remove_resource(Peer),

View File

@ -1,4 +1,4 @@
%% Created automatically by XML generator (xml_gen.erl)
%% Created automatically by XML generator (fxml_gen.erl)
%% Source: xmpp_codec.spec
-module(xmpp_codec).
@ -15,6 +15,22 @@ decode(_el) -> decode(_el, []).
decode({xmlel, _name, _attrs, _} = _el, Opts) ->
IgnoreEls = proplists:get_bool(ignore_els, Opts),
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"offline">>,
<<"http://jabber.org/protocol/offline">>} ->
decode_offline(<<"http://jabber.org/protocol/offline">>,
IgnoreEls, _el);
{<<"item">>,
<<"http://jabber.org/protocol/offline">>} ->
decode_offline_item(<<"http://jabber.org/protocol/offline">>,
IgnoreEls, _el);
{<<"fetch">>,
<<"http://jabber.org/protocol/offline">>} ->
decode_offline_fetch(<<"http://jabber.org/protocol/offline">>,
IgnoreEls, _el);
{<<"purge">>,
<<"http://jabber.org/protocol/offline">>} ->
decode_offline_purge(<<"http://jabber.org/protocol/offline">>,
IgnoreEls, _el);
{<<"failed">>, <<"urn:xmpp:sm:2">>} ->
decode_sm_failed(<<"urn:xmpp:sm:2">>, IgnoreEls, _el);
{<<"failed">>, <<"urn:xmpp:sm:3">>} ->
@ -1072,6 +1088,18 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
is_known_tag({xmlel, _name, _attrs, _} = _el) ->
case {_name, get_attr(<<"xmlns">>, _attrs)} of
{<<"offline">>,
<<"http://jabber.org/protocol/offline">>} ->
true;
{<<"item">>,
<<"http://jabber.org/protocol/offline">>} ->
true;
{<<"fetch">>,
<<"http://jabber.org/protocol/offline">>} ->
true;
{<<"purge">>,
<<"http://jabber.org/protocol/offline">>} ->
true;
{<<"failed">>, <<"urn:xmpp:sm:2">>} -> true;
{<<"failed">>, <<"urn:xmpp:sm:3">>} -> true;
{<<"a">>, <<"urn:xmpp:sm:2">>} -> true;
@ -2124,7 +2152,15 @@ encode({sm_resumed, _, _, _} = Resumed) ->
encode({sm_r, _} = R) -> encode_sm_r(R, []);
encode({sm_a, _, _} = A) -> encode_sm_a(A, []);
encode({sm_failed, _, _} = Failed) ->
encode_sm_failed(Failed, []).
encode_sm_failed(Failed, []);
encode({offline_item, _, _} = Item) ->
encode_offline_item(Item,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/offline">>}]);
encode({offline, _, _, _} = Offline) ->
encode_offline(Offline,
[{<<"xmlns">>,
<<"http://jabber.org/protocol/offline">>}]).
get_ns({last, _, _}) -> <<"jabber:iq:last">>;
get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@ -2319,6 +2355,10 @@ get_ns({carbons_sent, _}) -> <<"urn:xmpp:carbons:2">>;
get_ns({feature_csi, _}) -> <<"urn:xmpp:csi:0">>;
get_ns({csi, active}) -> <<"urn:xmpp:csi:0">>;
get_ns({csi, inactive}) -> <<"urn:xmpp:csi:0">>;
get_ns({offline_item, _, _}) ->
<<"http://jabber.org/protocol/offline">>;
get_ns({offline, _, _, _}) ->
<<"http://jabber.org/protocol/offline">>;
get_ns(_) -> <<>>.
dec_int(Val) -> dec_int(Val, infinity, infinity).
@ -2522,6 +2562,8 @@ pp(sm_resumed, 3) -> [h, previd, xmlns];
pp(sm_r, 1) -> [xmlns];
pp(sm_a, 2) -> [h, xmlns];
pp(sm_failed, 2) -> [reason, xmlns];
pp(offline_item, 2) -> [node, action];
pp(offline, 3) -> [items, purge, fetch];
pp(_, _) -> no.
enc_bool(false) -> <<"false">>;
@ -2564,6 +2606,156 @@ dec_tzo(Val) ->
M = jlib:binary_to_integer(M1),
if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
decode_offline(__TopXMLNS, __IgnoreEls,
{xmlel, <<"offline">>, _attrs, _els}) ->
{Items, Purge, Fetch} = decode_offline_els(__TopXMLNS,
__IgnoreEls, _els, [], false,
false),
{offline, Items, Purge, Fetch}.
decode_offline_els(__TopXMLNS, __IgnoreEls, [], Items,
Purge, Fetch) ->
{lists:reverse(Items), Purge, Fetch};
decode_offline_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"purge">>, _attrs, _} = _el | _els], Items,
Purge, Fetch) ->
_xmlns = get_attr(<<"xmlns">>, _attrs),
if _xmlns == <<>>; _xmlns == __TopXMLNS ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
decode_offline_purge(__TopXMLNS, __IgnoreEls,
_el),
Fetch);
true ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
Purge, Fetch)
end;
decode_offline_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"fetch">>, _attrs, _} = _el | _els], Items,
Purge, Fetch) ->
_xmlns = get_attr(<<"xmlns">>, _attrs),
if _xmlns == <<>>; _xmlns == __TopXMLNS ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
Purge,
decode_offline_fetch(__TopXMLNS, __IgnoreEls,
_el));
true ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
Purge, Fetch)
end;
decode_offline_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"item">>, _attrs, _} = _el | _els], Items,
Purge, Fetch) ->
_xmlns = get_attr(<<"xmlns">>, _attrs),
if _xmlns == <<>>; _xmlns == __TopXMLNS ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els,
[decode_offline_item(__TopXMLNS, __IgnoreEls, _el)
| Items],
Purge, Fetch);
true ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
Purge, Fetch)
end;
decode_offline_els(__TopXMLNS, __IgnoreEls, [_ | _els],
Items, Purge, Fetch) ->
decode_offline_els(__TopXMLNS, __IgnoreEls, _els, Items,
Purge, Fetch).
encode_offline({offline, Items, Purge, Fetch},
_xmlns_attrs) ->
_els = lists:reverse('encode_offline_$items'(Items,
'encode_offline_$purge'(Purge,
'encode_offline_$fetch'(Fetch,
[])))),
_attrs = _xmlns_attrs,
{xmlel, <<"offline">>, _attrs, _els}.
'encode_offline_$items'([], _acc) -> _acc;
'encode_offline_$items'([Items | _els], _acc) ->
'encode_offline_$items'(_els,
[encode_offline_item(Items, []) | _acc]).
'encode_offline_$purge'(false, _acc) -> _acc;
'encode_offline_$purge'(Purge, _acc) ->
[encode_offline_purge(Purge, []) | _acc].
'encode_offline_$fetch'(false, _acc) -> _acc;
'encode_offline_$fetch'(Fetch, _acc) ->
[encode_offline_fetch(Fetch, []) | _acc].
decode_offline_item(__TopXMLNS, __IgnoreEls,
{xmlel, <<"item">>, _attrs, _els}) ->
{Node, Action} = decode_offline_item_attrs(__TopXMLNS,
_attrs, undefined, undefined),
{offline_item, Node, Action}.
decode_offline_item_attrs(__TopXMLNS,
[{<<"node">>, _val} | _attrs], _Node, Action) ->
decode_offline_item_attrs(__TopXMLNS, _attrs, _val,
Action);
decode_offline_item_attrs(__TopXMLNS,
[{<<"action">>, _val} | _attrs], Node, _Action) ->
decode_offline_item_attrs(__TopXMLNS, _attrs, Node,
_val);
decode_offline_item_attrs(__TopXMLNS, [_ | _attrs],
Node, Action) ->
decode_offline_item_attrs(__TopXMLNS, _attrs, Node,
Action);
decode_offline_item_attrs(__TopXMLNS, [], Node,
Action) ->
{decode_offline_item_attr_node(__TopXMLNS, Node),
decode_offline_item_attr_action(__TopXMLNS, Action)}.
encode_offline_item({offline_item, Node, Action},
_xmlns_attrs) ->
_els = [],
_attrs = encode_offline_item_attr_action(Action,
encode_offline_item_attr_node(Node,
_xmlns_attrs)),
{xmlel, <<"item">>, _attrs, _els}.
decode_offline_item_attr_node(__TopXMLNS, undefined) ->
undefined;
decode_offline_item_attr_node(__TopXMLNS, _val) -> _val.
encode_offline_item_attr_node(undefined, _acc) -> _acc;
encode_offline_item_attr_node(_val, _acc) ->
[{<<"node">>, _val} | _acc].
decode_offline_item_attr_action(__TopXMLNS,
undefined) ->
undefined;
decode_offline_item_attr_action(__TopXMLNS, _val) ->
case catch dec_enum(_val, [view, remove]) of
{'EXIT', _} ->
erlang:error({xmpp_codec,
{bad_attr_value, <<"action">>, <<"item">>,
__TopXMLNS}});
_res -> _res
end.
encode_offline_item_attr_action(undefined, _acc) ->
_acc;
encode_offline_item_attr_action(_val, _acc) ->
[{<<"action">>, enc_enum(_val)} | _acc].
decode_offline_fetch(__TopXMLNS, __IgnoreEls,
{xmlel, <<"fetch">>, _attrs, _els}) ->
true.
encode_offline_fetch(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"fetch">>, _attrs, _els}.
decode_offline_purge(__TopXMLNS, __IgnoreEls,
{xmlel, <<"purge">>, _attrs, _els}) ->
true.
encode_offline_purge(true, _xmlns_attrs) ->
_els = [],
_attrs = _xmlns_attrs,
{xmlel, <<"purge">>, _attrs, _els}.
decode_sm_failed(__TopXMLNS, __IgnoreEls,
{xmlel, <<"failed">>, _attrs, _els}) ->
Reason = decode_sm_failed_els(__TopXMLNS, __IgnoreEls,

View File

@ -1,4 +1,4 @@
%% Created automatically by XML generator (xml_gen.erl)
%% Created automatically by XML generator (fxml_gen.erl)
%% Source: xmpp_codec.spec
-record(chatstate, {type :: active | composing | gone | inactive | paused}).
@ -424,6 +424,13 @@
features = [] :: [binary()],
xdata = [] :: [#xdata{}]}).
-record(offline_item, {node :: binary(),
action :: 'remove' | 'view'}).
-record(offline, {items = [] :: [#offline_item{}],
purge = false :: boolean(),
fetch = false :: boolean()}).
-record(sasl_mechanisms, {list = [] :: [binary()]}).
-record(sm_failed, {reason :: atom() | #gone{} | #redirect{},

View File

@ -2398,6 +2398,35 @@
#ref{name = error_unexpected_request,
min = 0, max = 1, label = '$reason'}]}).
-xml(offline_purge,
#elem{name = <<"purge">>,
xmlns = <<"http://jabber.org/protocol/offline">>,
result = true}).
-xml(offline_fetch,
#elem{name = <<"fetch">>,
xmlns = <<"http://jabber.org/protocol/offline">>,
result = true}).
-xml(offline_item,
#elem{name = <<"item">>,
xmlns = <<"http://jabber.org/protocol/offline">>,
result = {offline_item, '$node', '$action'},
attrs = [#attr{name = <<"node">>},
#attr{name = <<"action">>,
dec = {dec_enum, [[view, remove]]},
enc = {enc_enum, []}}]}).
-xml(offline,
#elem{name = <<"offline">>,
xmlns = <<"http://jabber.org/protocol/offline">>,
result = {offline, '$items', '$purge', '$fetch'},
refs = [#ref{name = offline_purge, min = 0, max = 1,
label = '$purge', default = false},
#ref{name = offline_fetch, min = 0, max = 1,
label = '$fetch', default = false},
#ref{name = offline_item, min = 0, label = '$items'}]}).
dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>),
H = jlib:binary_to_integer(H1),