* Wrapper proper test cases into common test cases * Improve test cases for reason code module (#2789) * Split 3 proper tests into 3 ct cases * Improve test cases for client, rpc and request-response * Add psk suites to increase coverage * Add sys test cases
352 lines
9.5 KiB
Erlang
352 lines
9.5 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% 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(emqx_session_SUITE).
|
|
|
|
-compile(export_all).
|
|
-compile(nowarn_export_all).
|
|
|
|
-include("emqx_mqtt.hrl").
|
|
-include_lib("proper/include/proper.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-define(mock_modules,
|
|
[ emqx_metrics
|
|
, emqx_broker
|
|
, emqx_misc
|
|
, emqx_message
|
|
, emqx_hooks
|
|
, emqx_zone
|
|
, emqx_pd
|
|
]).
|
|
|
|
all() -> emqx_ct:all(?MODULE).
|
|
|
|
t_proper_session(_) ->
|
|
Opts = [{numtests, 1000}, {to_file, user}],
|
|
ok = emqx_logger:set_log_level(emergency),
|
|
ok = before_proper(),
|
|
?assert(proper:quickcheck(prop_session(), Opts)),
|
|
ok = after_proper().
|
|
|
|
before_proper() ->
|
|
load(?mock_modules).
|
|
|
|
after_proper() ->
|
|
unload(?mock_modules),
|
|
emqx_logger:set_log_level(error).
|
|
|
|
prop_session() ->
|
|
?FORALL({Session, OpList}, {session(), session_op_list()},
|
|
begin
|
|
try
|
|
apply_ops(Session, OpList),
|
|
true
|
|
after
|
|
true
|
|
end
|
|
end).
|
|
|
|
%%%%%%%%%%%%%%%
|
|
%%% Helpers %%%
|
|
%%%%%%%%%%%%%%%
|
|
|
|
apply_ops(Session, []) ->
|
|
?assertEqual(session, element(1, Session));
|
|
apply_ops(Session, [Op | Rest]) ->
|
|
NSession = apply_op(Session, Op),
|
|
apply_ops(NSession, Rest).
|
|
|
|
apply_op(Session, info) ->
|
|
Info = emqx_session:info(Session),
|
|
?assert(is_map(Info)),
|
|
?assertEqual(16, maps:size(Info)),
|
|
Session;
|
|
apply_op(Session, attrs) ->
|
|
Attrs = emqx_session:attrs(Session),
|
|
?assert(is_map(Attrs)),
|
|
?assertEqual(3, maps:size(Attrs)),
|
|
Session;
|
|
apply_op(Session, stats) ->
|
|
Stats = emqx_session:stats(Session),
|
|
?assert(is_list(Stats)),
|
|
?assertEqual(9, length(Stats)),
|
|
Session;
|
|
apply_op(Session, {info, InfoArg}) ->
|
|
_Ret = emqx_session:info(InfoArg, Session),
|
|
Session;
|
|
apply_op(Session, {subscribe, {Client, TopicFilter, SubOpts}}) ->
|
|
case emqx_session:subscribe(Client, TopicFilter, SubOpts, Session) of
|
|
{ok, NSession} ->
|
|
NSession;
|
|
{error, ?RC_QUOTA_EXCEEDED} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {unsubscribe, {Client, TopicFilter}}) ->
|
|
case emqx_session:unsubscribe(Client, TopicFilter, Session) of
|
|
{ok, NSession} ->
|
|
NSession;
|
|
{error, ?RC_NO_SUBSCRIPTION_EXISTED} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {publish, {PacketId, Msg}}) ->
|
|
case emqx_session:publish(PacketId, Msg, Session) of
|
|
{ok, _Msg} ->
|
|
Session;
|
|
{ok, _Deliver, NSession} ->
|
|
NSession;
|
|
{error, _ErrorCode} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {puback, PacketId}) ->
|
|
case emqx_session:puback(PacketId, Session) of
|
|
{ok, _Msg} ->
|
|
Session;
|
|
{ok, _Deliver, NSession} ->
|
|
NSession;
|
|
{error, _ErrorCode} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {pubrec, PacketId}) ->
|
|
case emqx_session:pubrec(PacketId, Session) of
|
|
{ok, NSession} ->
|
|
NSession;
|
|
{error, _ErrorCode} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {pubrel, PacketId}) ->
|
|
case emqx_session:pubrel(PacketId, Session) of
|
|
{ok, NSession} ->
|
|
NSession;
|
|
{error, _ErrorCode} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {pubcomp, PacketId}) ->
|
|
case emqx_session:pubcomp(PacketId, Session) of
|
|
{ok, _Msgs} ->
|
|
Session;
|
|
{ok, _Msgs, NSession} ->
|
|
NSession;
|
|
{error, _ErrorCode} ->
|
|
Session
|
|
end;
|
|
apply_op(Session, {deliver, Delivers}) ->
|
|
{ok, _Msgs, NSession} = emqx_session:deliver(Delivers, Session),
|
|
NSession;
|
|
apply_op(Session, {timeout, {TRef, TimeoutMsg}}) ->
|
|
case emqx_session:timeout(TRef, TimeoutMsg, Session) of
|
|
{ok, NSession} ->
|
|
NSession;
|
|
{ok, _Msg, NSession} ->
|
|
NSession
|
|
end.
|
|
|
|
%%%%%%%%%%%%%%%%%%
|
|
%%% Generators %%%
|
|
%%%%%%%%%%%%%%%%%%
|
|
session_op_list() ->
|
|
Union = [info,
|
|
attrs,
|
|
stats,
|
|
{info, info_args()},
|
|
{subscribe, sub_args()},
|
|
{unsubscribe, unsub_args()},
|
|
{publish, publish_args()},
|
|
{puback, puback_args()},
|
|
{pubrec, pubrec_args()},
|
|
{pubrel, pubrel_args()},
|
|
{pubcomp, pubcomp_args()},
|
|
{deliver, deliver_args()},
|
|
{timeout, timeout_args()}
|
|
],
|
|
list(?LAZY(oneof(Union))).
|
|
|
|
deliver_args() ->
|
|
list({deliver, topic(), message()}).
|
|
|
|
timeout_args() ->
|
|
{tref(), timeout_msg()}.
|
|
|
|
info_args() ->
|
|
oneof([clean_start,
|
|
subscriptions,
|
|
max_subscriptions,
|
|
upgrade_qos,
|
|
inflight,
|
|
max_inflight,
|
|
retry_interval,
|
|
mqueue_len,
|
|
max_mqueue,
|
|
mqueue_dropped,
|
|
next_pkt_id,
|
|
awaiting_rel,
|
|
max_awaiting_rel,
|
|
await_rel_timeout,
|
|
expiry_interval,
|
|
created_at
|
|
]).
|
|
|
|
sub_args() ->
|
|
?LET({ClientId, TopicFilter, SubOpts},
|
|
{clientid(), topic(), sub_opts()},
|
|
{#{client_id => ClientId}, TopicFilter, SubOpts}).
|
|
|
|
unsub_args() ->
|
|
?LET({ClientId, TopicFilter},
|
|
{clientid(), topic()},
|
|
{#{client_id => ClientId}, TopicFilter}).
|
|
|
|
publish_args() ->
|
|
?LET({PacketId, Message},
|
|
{packetid(), message()},
|
|
{PacketId, Message}).
|
|
|
|
puback_args() ->
|
|
packetid().
|
|
|
|
pubrec_args() ->
|
|
packetid().
|
|
|
|
pubrel_args() ->
|
|
packetid().
|
|
|
|
pubcomp_args() ->
|
|
packetid().
|
|
|
|
timeout_msg() ->
|
|
oneof([retry_delivery, check_awaiting_rel]).
|
|
|
|
tref() -> oneof([tref, undefined]).
|
|
|
|
sub_opts() ->
|
|
?LET({RH, RAP, NL, QOS, SHARE, SUBID},
|
|
{rh(), rap(), nl(), qos(), share(), subid()}
|
|
, make_subopts(RH, RAP, NL, QOS, SHARE, SUBID)).
|
|
|
|
message() ->
|
|
?LET({QoS, Topic, Payload},
|
|
{qos(), topic(), payload()},
|
|
emqx_message:make(proper, QoS, Topic, Payload)).
|
|
|
|
subid() -> integer().
|
|
|
|
rh() -> oneof([0, 1, 2]).
|
|
|
|
rap() -> oneof([0, 1]).
|
|
|
|
nl() -> oneof([0, 1]).
|
|
|
|
qos() -> oneof([0, 1, 2]).
|
|
|
|
share() -> binary().
|
|
|
|
clientid() -> binary().
|
|
|
|
topic() -> ?LET(No, choose(1, 10),
|
|
begin
|
|
NoBin = integer_to_binary(No),
|
|
<<"topic/", NoBin/binary>>
|
|
end).
|
|
|
|
payload() -> binary().
|
|
|
|
packetid() -> choose(1, 30).
|
|
|
|
zone() ->
|
|
?LET(Zone, [{max_subscriptions, max_subscription()},
|
|
{upgrade_qos, upgrade_qos()},
|
|
{retry_interval, retry_interval()},
|
|
{max_awaiting_rel, max_awaiting_rel()},
|
|
{await_rel_timeout, await_rel_timeout()}]
|
|
, maps:from_list(Zone)).
|
|
|
|
max_subscription() ->
|
|
frequency([{33, 0},
|
|
{33, 1},
|
|
{34, choose(0,10)}]).
|
|
|
|
upgrade_qos() -> bool().
|
|
|
|
retry_interval() -> ?LET(Interval, choose(0, 20), Interval*1000).
|
|
|
|
max_awaiting_rel() -> choose(0, 10).
|
|
|
|
await_rel_timeout() -> ?LET(Interval, choose(0, 150), Interval*1000).
|
|
|
|
max_inflight() -> choose(0, 10).
|
|
|
|
expiry_interval() -> ?LET(EI, choose(1, 10), EI * 3600).
|
|
|
|
option() ->
|
|
?LET(Option, [{max_inflight, max_inflight()},
|
|
{expiry_interval, expiry_interval()}]
|
|
, maps:from_list(Option)).
|
|
|
|
cleanstart() -> bool().
|
|
|
|
session() ->
|
|
?LET({CleanStart, Zone, Options},
|
|
{cleanstart(), zone(), option()},
|
|
begin
|
|
Session = emqx_session:init(CleanStart, #{zone => Zone}, Options),
|
|
emqx_session:set_pkt_id(Session, 16#ffff)
|
|
end).
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
%%% Internal functions %%%
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
make_subopts(RH, RAP, NL, QOS, SHARE, SubId) ->
|
|
#{rh => RH,
|
|
rap => RAP,
|
|
nl => NL,
|
|
qos => QOS,
|
|
share => SHARE,
|
|
subid => SubId}.
|
|
|
|
|
|
load(Modules) ->
|
|
[mock(Module) || Module <- Modules],
|
|
ok.
|
|
|
|
unload(Modules) ->
|
|
lists:foreach(fun(Module) ->
|
|
ok = meck:unload(Module)
|
|
end, Modules).
|
|
|
|
mock(Module) ->
|
|
ok = meck:new(Module, [passthrough, no_history]),
|
|
do_mock(Module).
|
|
|
|
do_mock(emqx_metrics) ->
|
|
meck:expect(emqx_metrics, inc, fun(_Anything) -> ok end);
|
|
do_mock(emqx_broker) ->
|
|
meck:expect(emqx_broker, subscribe, fun(_, _, _) -> ok end),
|
|
meck:expect(emqx_broker, set_subopts, fun(_, _) -> ok end),
|
|
meck:expect(emqx_broker, unsubscribe, fun(_) -> ok end),
|
|
meck:expect(emqx_broker, publish, fun(_) -> ok end);
|
|
do_mock(emqx_misc) ->
|
|
meck:expect(emqx_misc, start_timer, fun(_, _) -> tref end);
|
|
do_mock(emqx_message) ->
|
|
meck:expect(emqx_message, set_header, fun(_Hdr, _Val, Msg) -> Msg end),
|
|
meck:expect(emqx_message, is_expired, fun(_Msg) -> (rand:uniform(16) > 8) end);
|
|
do_mock(emqx_hooks) ->
|
|
meck:expect(emqx_hooks, run, fun(_Hook, _Args) -> ok end);
|
|
do_mock(emqx_zone) ->
|
|
meck:expect(emqx_zone, get_env, fun(Env, Key, Default) -> maps:get(Key, Env, Default) end);
|
|
do_mock(emqx_pd) ->
|
|
meck:expect(emqx_pd, update_counter, fun(_stats, _num) -> ok end).
|