171 lines
5.6 KiB
Erlang
171 lines
5.6 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020-2021 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(prop_emqx_rpc).
|
|
|
|
-include_lib("proper/include/proper.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-define(NODENAME, 'test@127.0.0.1').
|
|
|
|
-define(ALL(Vars, Types, Exprs),
|
|
?SETUP(fun() ->
|
|
State = do_setup(),
|
|
fun() -> do_teardown(State) end
|
|
end, ?FORALL(Vars, Types, Exprs))).
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Properties
|
|
%%--------------------------------------------------------------------
|
|
|
|
prop_node() ->
|
|
?ALL(Node0, nodename(),
|
|
begin
|
|
Node = punch(Node0),
|
|
?assert(emqx_rpc:cast(Node, erlang, system_time, [])),
|
|
case emqx_rpc:call(Node, erlang, system_time, []) of
|
|
{badrpc, _Reason} -> true;
|
|
Delivery when is_integer(Delivery) -> true;
|
|
_Other -> false
|
|
end
|
|
end).
|
|
|
|
prop_node_with_key() ->
|
|
?ALL({Node0, Key}, nodename_with_key(),
|
|
begin
|
|
Node = punch(Node0),
|
|
?assert(emqx_rpc:cast(Key, Node, erlang, system_time, [])),
|
|
case emqx_rpc:call(Key, Node, erlang, system_time, []) of
|
|
{badrpc, _Reason} -> true;
|
|
Delivery when is_integer(Delivery) -> true;
|
|
_Other -> false
|
|
end
|
|
end).
|
|
|
|
prop_nodes() ->
|
|
?ALL(Nodes0, nodesname(),
|
|
begin
|
|
Nodes = punch(Nodes0),
|
|
case emqx_rpc:multicall(Nodes, erlang, system_time, []) of
|
|
{badrpc, _Reason} -> true;
|
|
{RealResults, RealBadNodes}
|
|
when is_list(RealResults);
|
|
is_list(RealBadNodes) ->
|
|
true;
|
|
_Other -> false
|
|
end
|
|
end).
|
|
|
|
prop_nodes_with_key() ->
|
|
?ALL({Nodes0, Key}, nodesname_with_key(),
|
|
begin
|
|
Nodes = punch(Nodes0),
|
|
case emqx_rpc:multicall(Key, Nodes, erlang, system_time, []) of
|
|
{badrpc, _Reason} -> true;
|
|
{RealResults, RealBadNodes}
|
|
when is_list(RealResults);
|
|
is_list(RealBadNodes) ->
|
|
true;
|
|
_Other -> false
|
|
end
|
|
end).
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Helper
|
|
%%--------------------------------------------------------------------
|
|
|
|
do_setup() ->
|
|
ensure_distributed_nodename(),
|
|
ok = logger:set_primary_config(#{level => warning}),
|
|
{ok, _Apps} = application:ensure_all_started(gen_rpc),
|
|
ok = application:set_env(gen_rpc, call_receive_timeout, 100),
|
|
ok = meck:new(gen_rpc, [passthrough, no_history]),
|
|
ok = meck:expect(gen_rpc, multicall,
|
|
fun(Nodes, Mod, Fun, Args) ->
|
|
gen_rpc:multicall(Nodes, Mod, Fun, Args, 100)
|
|
end).
|
|
|
|
do_teardown(_) ->
|
|
ok = net_kernel:stop(),
|
|
ok = application:stop(gen_rpc),
|
|
ok = meck:unload(gen_rpc),
|
|
%% wait for tcp close
|
|
timer:sleep(1500).
|
|
|
|
ensure_distributed_nodename() ->
|
|
case net_kernel:start([?NODENAME]) of
|
|
{ok, _} ->
|
|
ok;
|
|
{error, {already_started, _}} ->
|
|
net_kernel:stop(),
|
|
net_kernel:start([?NODENAME]);
|
|
{error, {{shutdown, {_, _, {'EXIT', nodistribution}}}, _}} ->
|
|
%% start epmd first
|
|
spawn_link(fun() -> os:cmd("epmd") end),
|
|
timer:sleep(100),
|
|
net_kernel:start([?NODENAME])
|
|
end.
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Generator
|
|
%%--------------------------------------------------------------------
|
|
|
|
|
|
nodename() ->
|
|
?LET({NodePrefix, HostName},
|
|
{node_prefix(), hostname()},
|
|
begin
|
|
Node = NodePrefix ++ "@" ++ HostName,
|
|
list_to_atom(Node)
|
|
end).
|
|
|
|
nodename_with_key() ->
|
|
?LET({NodePrefix, HostName, Key},
|
|
{node_prefix(), hostname(), choose(0, 10)},
|
|
begin
|
|
Node = NodePrefix ++ "@" ++ HostName,
|
|
{list_to_atom(Node), Key}
|
|
end).
|
|
|
|
nodesname() ->
|
|
oneof([list(nodename()), [node()]]).
|
|
|
|
nodesname_with_key() ->
|
|
oneof([{list(nodename()), choose(0, 10)}, {[node()], 1}]).
|
|
|
|
node_prefix() ->
|
|
oneof(["emqxct", text_like()]).
|
|
|
|
text_like() ->
|
|
?SUCHTHAT(Text, list(range($a, $z)), (length(Text) =< 100 andalso length(Text) > 0)).
|
|
|
|
hostname() ->
|
|
oneof(["127.0.0.1", "localhost"]).
|
|
|
|
%%--------------------------------------------------------------------
|
|
%% Utils
|
|
%%--------------------------------------------------------------------
|
|
|
|
%% After running the props, the `node()` () is only able to return an
|
|
%% incorrect node name - `nonode@nohost`, But we want a distributed nodename
|
|
%% So, just translate the `nonode@nohost` to ?NODENAME
|
|
punch(Nodes) when is_list(Nodes) ->
|
|
lists:map(fun punch/1, Nodes);
|
|
punch('nonode@nohost') ->
|
|
node(); %% Equal to ?NODENAME
|
|
punch(GoodBoy) ->
|
|
GoodBoy.
|