1: -module(mam_misc_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("proper/include/proper.hrl"). 5: -include_lib("eunit/include/eunit.hrl"). 6: -include_lib("exml/include/exml.hrl"). 7: -include("mongoose_ns.hrl"). 8: 9: -import(mod_mam_utils, 10: [ 11: is_archivable_message/4, 12: is_valid_message/4 13: ]). 14: 15: all() -> 16: [ 17: test_encode_decode_functionality, 18: {group, should_archive} 19: ]. 20: 21: groups() -> 22: [ 23: {should_archive, [parallel], 24: [ 25: non_messages_are_always_false, 26: messages_type_error_false, 27: other_message_types_return_false, 28: must_be_rejected, 29: must_be_accepted 30: ]} 31: ]. 32: 33: init_per_suite(Config) -> 34: {ok, _} = application:ensure_all_started(jid), 35: Config. 36: 37: end_per_suite(Config) -> 38: Config. 39: 40: test_encode_decode_functionality(_Config) -> 41: PossibleDomainNames = [<<"a">>, <<"b">>, <<"c">>, <<"d">>, <<"e">>, <<"f">>], 42: PossibleUserNames = [<<"">> | PossibleDomainNames], 43: PossibleResourceNames = [<<"just:some@random/text here">> | PossibleUserNames], 44: PossibleJIDs = [{U, D, R, jid:make(U, D, R)} || U <- PossibleUserNames, 45: D <- PossibleDomainNames, 46: R <- PossibleResourceNames], 47: FailedJIDs = [[{U1, D1, R1}, {U2, D2, R2}, EncodedJID, DecodedJID] 48: || {U1, D1, R1, JID1} <- PossibleJIDs, 49: {U2, D2, R2, JID2} <- PossibleJIDs, 50: EncodedJID <- [catch mam_jid_mini:encode(JID1, JID2)], 51: DecodedJID <- [catch mam_jid_mini:decode(JID1, EncodedJID)], 52: DecodedJID =/= JID2], 53: case lists:sublist(FailedJIDs, 100) of 54: [] -> ok; 55: First100FailedJIDs -> 56: [ct:log("~nJID encoding/decoding failed:~n" 57: "\tbase JID - ~p~n" 58: "\tJID to encode - ~p~n" 59: "\tencoded JID - ~p~n" 60: "\tdecoded JID - ~p~n", Params) || Params <- First100FailedJIDs], 61: ct:fail("Failed to encode/decode some of the JIDs," 62: " see test suite logs for more details", []) 63: end. 64: 65: non_messages_are_always_false(_) -> 66: NonMsgPacket = ?LET({T, Attrs, Children}, 67: {oneof([<<"iq">>, <<"presence">>]), gen_attrs(all), gen_children()}, 68: packet(T, Attrs, Children)), 69: Prop = ?FORALL({Mod, Dir, Packet, Acm}, 70: {gen_modules(), gen_directions(), NonMsgPacket, gen_arc_markers()}, 71: not is_archivable_message(Mod, Dir, Packet, Acm)), 72: run_prop(?FUNCTION_NAME, Prop). 73: 74: messages_type_error_false(_) -> 75: ErrorMsgDef = ?LET({Attrs, Children}, 76: {gen_attrs(<<"error">>), gen_children()}, 77: packet(<<"message">>, Attrs, Children)), 78: Prop = ?FORALL({Mod, Dir, ErrorMessage, Acm}, 79: {gen_modules(), gen_directions(), ErrorMsgDef, gen_arc_markers()}, 80: not is_archivable_message(Mod, Dir, ErrorMessage, Acm)), 81: run_prop(?FUNCTION_NAME, Prop). 82: 83: other_message_types_return_false(_) -> 84: StrangeTypeDef = ?LET({Attrs, Children}, 85: {gen_attrs(strange), gen_children()}, 86: packet(<<"message">>, Attrs, Children)), 87: Prop = ?FORALL({Mod, Dir, StrangeType, Acm}, 88: {gen_modules(), gen_directions(), StrangeTypeDef, gen_arc_markers()}, 89: not is_archivable_message(Mod, Dir, StrangeType, Acm)), 90: run_prop(?FUNCTION_NAME, Prop). 91: 92: must_be_rejected(_) -> 93: MustContain = oneof([no_store, delay, result]), 94: MsgDef = ?LET({Attrs, Children}, 95: {gen_attrs(good), gen_children(MustContain, [])}, 96: packet(<<"message">>, Attrs, Children)), 97: Prop = ?FORALL({Mod, Dir, Msg, Acm}, 98: {gen_modules(), gen_directions(), MsgDef, gen_arc_markers()}, 99: not is_valid_message(Mod, Dir, Msg, Acm)), 100: run_prop(?FUNCTION_NAME, Prop). 101: 102: must_be_accepted(_) -> 103: MustContain = oneof([body, store, retraction]), 104: MustNotContain = [no_store, delay, result], 105: MsgDef = ?LET({Attrs, Children}, 106: {gen_attrs(good), gen_children(MustContain, MustNotContain)}, 107: packet(<<"message">>, Attrs, Children)), 108: Prop = ?FORALL({Mod, Dir, Msg, Acm}, 109: {gen_modules(), gen_directions(), MsgDef, gen_arc_markers()}, 110: is_valid_message(Mod, Dir, Msg, Acm)), 111: run_prop(?FUNCTION_NAME, Prop). 112: 113: %% Generators 114: gen_modules() -> 115: oneof([mod_mam_pm, mod_inbox]). 116: 117: gen_directions() -> 118: oneof([outgoing, incoming]). 119: 120: gen_arc_markers() -> 121: boolean(). 122: 123: gen_attrs(Bin) when is_binary(Bin) -> 124: ?LET({From, To}, {gen_jid(), gen_jid()}, attrs(From, To, Bin)); 125: gen_attrs(Type) when is_atom(Type) -> 126: ?LET({From, To, MsgType}, {gen_jid(), gen_jid(), gen_msg_type(Type)}, attrs(From, To, MsgType)). 127: 128: gen_msg_type(all) -> 129: oneof([<<"normal">>, <<"chat">>, <<"groupchat">>, <<"error">>]); 130: gen_msg_type(good) -> 131: oneof([<<"normal">>, <<"chat">>, <<"groupchat">>]); 132: gen_msg_type(strange) -> 133: ?SUCHTHAT(B, non_empty(binary()), 134: not lists:member(B, [<<"normal">>, <<"chat">>, <<"groupchat">>, <<"error">>])). 135: 136: gen_jid() -> 137: oneof([alice(), bob(), room()]). 138: 139: gen_children() -> 140: do_gen_children([undefined], []). 141: 142: gen_children([], F) -> 143: do_gen_children([undefined], F); 144: gen_children(F1, F2) -> 145: do_gen_children(F1, F2). 146: 147: do_gen_children(ForceOneYes, MustNotContain) -> 148: ?LET({B1, B2, B3, B4, B5, B6, B7, OneYes}, 149: {boolean(), boolean(), boolean(), boolean(), boolean(), boolean(), boolean(), ForceOneYes}, 150: begin 151: Elems = [{body, body(), B1}, 152: {store, store(), B2}, 153: {marker, chat_marker(), B3}, 154: {retraction, retraction(), B4}, 155: {result, mam_result(), B5}, 156: {delay, offline_delay(), B6}, 157: {no_store, no_store(), B7}], 158: Children = [ maybe_get(Val, OneYes, MustNotContain) || Val <- Elems ], 159: lists:filter(fun(El) -> El =/= false end, Children) 160: end). 161: 162: maybe_get({Elem, XmlElem, _}, Elem, _) -> 163: XmlElem; 164: maybe_get({Elem, XmlElem, Maybe}, _, MustNotContain) -> 165: Maybe andalso not lists:member(Elem, MustNotContain) andalso XmlElem. 166: 167: %% Possible XML elements 168: attrs(From, To, Type) -> 169: [{<<"from">>, From}, {<<"to">>, To}, {<<"type">>, Type}]. 170: body() -> 171: #xmlel{name = <<"body">>, children = [#xmlcdata{content = bin()}]}. 172: chat_marker() -> 173: #xmlel{name = <<"displayed">>, attrs = [{<<"xmlmn">>, ?NS_CHAT_MARKERS}, {<<"id">>, bin()}]}. 174: retraction() -> 175: #xmlel{name = <<"apply-to">>, 176: attrs = [{<<"id">>, bin()}, {<<"xmlns">>, ?NS_FASTEN}], 177: children = [#xmlel{name = <<"retract">>, attrs = [{<<"xmlns">>, ?NS_RETRACT}]}]}. 178: mam_result() -> 179: #xmlel{name = <<"result">>, 180: attrs = [{<<"id">>, bin()}, {<<"queryid">>, bin()}, {<<"xmlns">>, ?NS_MAM_06}]}. 181: offline_delay() -> 182: #xmlel{name = <<"delay">>, attrs = [{<<"stamp">>, bin()}, {<<"xmlns">>, ?NS_DELAY}]}. 183: no_store() -> 184: #xmlel{name = <<"no-store">>, attrs = [{<<"xmlns">>, ?NS_HINTS}]}. 185: store() -> 186: #xmlel{name = <<"store">>, attrs = [{<<"xmlns">>, ?NS_HINTS}]}. 187: packet(Name, Attrs, Children) -> 188: #xmlel{name = Name, attrs = Attrs, children = Children}. 189: 190: %% Helpers 191: bin() -> 192: base16:encode(crypto:strong_rand_bytes(8)). 193: alice() -> 194: <<"alice@localhost">>. 195: bob() -> 196: <<"bob@localhost">>. 197: room() -> 198: <<"room@muclight.localhost">>. 199: 200: run_prop(PropName, Property) -> 201: Opts = [quiet, long_result, {start_size, 2}, {numtests, 1000}, 202: {numworkers, erlang:system_info(schedulers_online)}], 203: case proper:quickcheck(proper:conjunction([{PropName, Property}]), Opts) of 204: true -> ok; 205: Res -> ct:fail(Res) 206: end.