|
|
@@ -23,6 +23,13 @@
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
|
|
-define(CM, emqx_cm).
|
|
|
+-define(ChanInfo,#{conninfo =>
|
|
|
+ #{socktype => tcp,
|
|
|
+ peername => {{127,0,0,1}, 5000},
|
|
|
+ sockname => {{127,0,0,1}, 1883},
|
|
|
+ peercert => nossl,
|
|
|
+ conn_mod => emqx_connection,
|
|
|
+ receive_maximum => 100}}).
|
|
|
|
|
|
%%--------------------------------------------------------------------
|
|
|
%% CT callbacks
|
|
|
@@ -43,13 +50,13 @@ end_per_suite(_Config) ->
|
|
|
%%--------------------------------------------------------------------
|
|
|
|
|
|
t_reg_unreg_channel(_) ->
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
?assertEqual([self()], emqx_cm:lookup_channels(<<"clientid">>)),
|
|
|
ok = emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
?assertEqual([], emqx_cm:lookup_channels(<<"clientid">>)).
|
|
|
|
|
|
t_get_set_chan_info(_) ->
|
|
|
- Info = #{proto_ver => 4, proto_name => <<"MQTT">>},
|
|
|
+ Info = ?ChanInfo,
|
|
|
ok = emqx_cm:register_channel(<<"clientid">>, Info, []),
|
|
|
?assertEqual(Info, emqx_cm:get_chan_info(<<"clientid">>)),
|
|
|
Info1 = Info#{proto_ver => 5},
|
|
|
@@ -60,7 +67,7 @@ t_get_set_chan_info(_) ->
|
|
|
|
|
|
t_get_set_chan_stats(_) ->
|
|
|
Stats = [{recv_oct, 10}, {send_oct, 8}],
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{}, Stats),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, Stats),
|
|
|
?assertEqual(Stats, emqx_cm:get_chan_stats(<<"clientid">>)),
|
|
|
Stats1 = [{recv_oct, 10}|Stats],
|
|
|
true = emqx_cm:set_chan_stats(<<"clientid">>, Stats1),
|
|
|
@@ -69,27 +76,89 @@ t_get_set_chan_stats(_) ->
|
|
|
?assertEqual(undefined, emqx_cm:get_chan_stats(<<"clientid">>)).
|
|
|
|
|
|
t_open_session(_) ->
|
|
|
+ ok = meck:new(emqx_connection, [passthrough, no_history]),
|
|
|
+ ok = meck:expect(emqx_connection, call, fun(_, _) -> ok end),
|
|
|
+
|
|
|
ClientInfo = #{zone => external,
|
|
|
clientid => <<"clientid">>,
|
|
|
username => <<"username">>,
|
|
|
peerhost => {127,0,0,1}},
|
|
|
- ConnInfo = #{peername => {{127,0,0,1}, 5000},
|
|
|
+ ConnInfo = #{socktype => tcp,
|
|
|
+ peername => {{127,0,0,1}, 5000},
|
|
|
+ sockname => {{127,0,0,1}, 1883},
|
|
|
+ peercert => nossl,
|
|
|
+ conn_mod => emqx_connection,
|
|
|
receive_maximum => 100},
|
|
|
{ok, #{session := Session1, present := false}}
|
|
|
= emqx_cm:open_session(true, ClientInfo, ConnInfo),
|
|
|
?assertEqual(100, emqx_session:info(inflight_max, Session1)),
|
|
|
{ok, #{session := Session2, present := false}}
|
|
|
- = emqx_cm:open_session(false, ClientInfo, ConnInfo),
|
|
|
- ?assertEqual(100, emqx_session:info(inflight_max, Session2)).
|
|
|
+ = emqx_cm:open_session(true, ClientInfo, ConnInfo),
|
|
|
+ ?assertEqual(100, emqx_session:info(inflight_max, Session2)),
|
|
|
+
|
|
|
+ emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
+ ok = meck:unload(emqx_connection).
|
|
|
+
|
|
|
+t_open_session_race_condition(_) ->
|
|
|
+ ClientInfo = #{zone => external,
|
|
|
+ clientid => <<"clientid">>,
|
|
|
+ username => <<"username">>,
|
|
|
+ peerhost => {127,0,0,1}},
|
|
|
+ ConnInfo = #{socktype => tcp,
|
|
|
+ peername => {{127,0,0,1}, 5000},
|
|
|
+ sockname => {{127,0,0,1}, 1883},
|
|
|
+ peercert => nossl,
|
|
|
+ conn_mod => emqx_connection,
|
|
|
+ receive_maximum => 100},
|
|
|
+
|
|
|
+ Parent = self(),
|
|
|
+ OpenASession = fun() ->
|
|
|
+ timer:sleep(rand:uniform(100)),
|
|
|
+ OpenR = (emqx_cm:open_session(true, ClientInfo, ConnInfo)),
|
|
|
+ Parent ! OpenR,
|
|
|
+ case OpenR of
|
|
|
+ {ok, _} ->
|
|
|
+ receive
|
|
|
+ {'$gen_call', From, discard} ->
|
|
|
+ gen_server:reply(From, ok), ok
|
|
|
+ end;
|
|
|
+ {error, Reason} ->
|
|
|
+ exit(Reason)
|
|
|
+ end
|
|
|
+ end,
|
|
|
+ [spawn(
|
|
|
+ fun() ->
|
|
|
+ spawn(OpenASession),
|
|
|
+ spawn(OpenASession)
|
|
|
+ end) || _ <- lists:seq(1, 1000)],
|
|
|
+
|
|
|
+ WaitingRecv = fun _Wr(N1, N2, 0) ->
|
|
|
+ {N1, N2};
|
|
|
+ _Wr(N1, N2, Rest) ->
|
|
|
+ receive
|
|
|
+ {ok, _} -> _Wr(N1+1, N2, Rest-1);
|
|
|
+ {error, _} -> _Wr(N1, N2+1, Rest-1)
|
|
|
+ end
|
|
|
+ end,
|
|
|
+
|
|
|
+ ct:pal("Race condition status: ~p~n", [WaitingRecv(0, 0, 2000)]),
|
|
|
+
|
|
|
+ ?assertEqual(1, ets:info(emqx_channel, size)),
|
|
|
+ ?assertEqual(1, ets:info(emqx_channel_conn, size)),
|
|
|
+ ?assertEqual(1, ets:info(emqx_channel_registry, size)),
|
|
|
+
|
|
|
+ [Pid] = emqx_cm:lookup_channels(<<"clientid">>),
|
|
|
+ exit(Pid, kill), timer:sleep(100),
|
|
|
+ ?assertEqual([], emqx_cm:lookup_channels(<<"clientid">>)).
|
|
|
|
|
|
t_discard_session(_) ->
|
|
|
ok = meck:new(emqx_connection, [passthrough, no_history]),
|
|
|
ok = meck:expect(emqx_connection, call, fun(_, _) -> ok end),
|
|
|
ok = emqx_cm:discard_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
ok = emqx_cm:discard_session(<<"clientid">>),
|
|
|
ok = emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{conninfo => #{conn_mod => emqx_connection}}, []),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
ok = emqx_cm:discard_session(<<"clientid">>),
|
|
|
ok = meck:expect(emqx_connection, call, fun(_, _) -> error(testing) end),
|
|
|
ok = emqx_cm:discard_session(<<"clientid">>),
|
|
|
@@ -97,35 +166,26 @@ t_discard_session(_) ->
|
|
|
ok = meck:unload(emqx_connection).
|
|
|
|
|
|
t_takeover_session(_) ->
|
|
|
- ok = meck:new(emqx_connection, [passthrough, no_history]),
|
|
|
- ok = meck:expect(emqx_connection, call, fun(_, _) -> test end),
|
|
|
- {error, not_found} = emqx_cm:takeover_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>),
|
|
|
{error, not_found} = emqx_cm:takeover_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{conninfo => #{conn_mod => emqx_connection}}, []),
|
|
|
- Pid = self(),
|
|
|
- {ok, emqx_connection, Pid, test} = emqx_cm:takeover_session(<<"clientid">>),
|
|
|
erlang:spawn(fun() ->
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{conninfo => #{conn_mod => emqx_connection}}, []),
|
|
|
- timer:sleep(1000)
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
+ receive
|
|
|
+ {'$gen_call', From, {takeover, 'begin'}} ->
|
|
|
+ gen_server:reply(From, test), ok
|
|
|
+ end
|
|
|
end),
|
|
|
- ct:sleep(100),
|
|
|
+ timer:sleep(100),
|
|
|
{ok, emqx_connection, _, test} = emqx_cm:takeover_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
- ok = meck:unload(emqx_connection).
|
|
|
+ emqx_cm:unregister_channel(<<"clientid">>).
|
|
|
|
|
|
t_kick_session(_) ->
|
|
|
ok = meck:new(emqx_connection, [passthrough, no_history]),
|
|
|
ok = meck:expect(emqx_connection, call, fun(_, _) -> test end),
|
|
|
{error, not_found} = emqx_cm:kick_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>),
|
|
|
- {error, not_found} = emqx_cm:kick_session(<<"clientid">>),
|
|
|
- ok = emqx_cm:unregister_channel(<<"clientid">>),
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{conninfo => #{conn_mod => emqx_connection}}, []),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
test = emqx_cm:kick_session(<<"clientid">>),
|
|
|
erlang:spawn(fun() ->
|
|
|
- ok = emqx_cm:register_channel(<<"clientid">>, #{conninfo => #{conn_mod => emqx_connection}}, []),
|
|
|
+ ok = emqx_cm:register_channel(<<"clientid">>, ?ChanInfo, []),
|
|
|
timer:sleep(1000)
|
|
|
end),
|
|
|
ct:sleep(100),
|