prop_emqx_rpc.erl 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(prop_emqx_rpc).
  17. -include_lib("proper/include/proper.hrl").
  18. -include_lib("eunit/include/eunit.hrl").
  19. -define(NODENAME, 'test@127.0.0.1').
  20. -define(ALL(Vars, Types, Exprs),
  21. ?SETUP(
  22. fun() ->
  23. State = do_setup(),
  24. fun() -> do_teardown(State) end
  25. end,
  26. ?FORALL(Vars, Types, Exprs)
  27. )
  28. ).
  29. %%--------------------------------------------------------------------
  30. %% Properties
  31. %%--------------------------------------------------------------------
  32. prop_node() ->
  33. ?ALL(
  34. Node0,
  35. nodename(),
  36. begin
  37. Node = punch(Node0),
  38. ?assert(emqx_rpc:cast(Node, erlang, system_time, [])),
  39. case emqx_rpc:call(Node, erlang, system_time, []) of
  40. {badrpc, _Reason} -> true;
  41. Delivery when is_integer(Delivery) -> true;
  42. _Other -> false
  43. end
  44. end
  45. ).
  46. prop_node_with_key() ->
  47. ?ALL(
  48. {Node0, Key},
  49. nodename_with_key(),
  50. begin
  51. Node = punch(Node0),
  52. ?assert(emqx_rpc:cast(Key, Node, erlang, system_time, [])),
  53. case emqx_rpc:call(Key, Node, erlang, system_time, []) of
  54. {badrpc, _Reason} -> true;
  55. Delivery when is_integer(Delivery) -> true;
  56. _Other -> false
  57. end
  58. end
  59. ).
  60. prop_nodes() ->
  61. ?ALL(
  62. Nodes0,
  63. nodesname(),
  64. begin
  65. Nodes = punch(Nodes0),
  66. case emqx_rpc:multicall(Nodes, erlang, system_time, []) of
  67. {RealResults, RealBadNodes} when
  68. is_list(RealResults);
  69. is_list(RealBadNodes)
  70. ->
  71. true;
  72. _Other ->
  73. false
  74. end
  75. end
  76. ).
  77. prop_nodes_with_key() ->
  78. ?ALL(
  79. {Nodes0, Key},
  80. nodesname_with_key(),
  81. begin
  82. Nodes = punch(Nodes0),
  83. case emqx_rpc:multicall(Key, Nodes, erlang, system_time, []) of
  84. {RealResults, RealBadNodes} when
  85. is_list(RealResults);
  86. is_list(RealBadNodes)
  87. ->
  88. true;
  89. _Other ->
  90. false
  91. end
  92. end
  93. ).
  94. %%--------------------------------------------------------------------
  95. %% Helper
  96. %%--------------------------------------------------------------------
  97. do_setup() ->
  98. ensure_distributed_nodename(),
  99. ok = logger:set_primary_config(#{level => warning}),
  100. {ok, _Apps} = application:ensure_all_started(gen_rpc),
  101. ok = application:set_env(gen_rpc, call_receive_timeout, 100),
  102. ok = meck:new(gen_rpc, [passthrough, no_history]),
  103. ok = meck:expect(
  104. gen_rpc,
  105. multicall,
  106. fun(Nodes, Mod, Fun, Args) ->
  107. gen_rpc:multicall(Nodes, Mod, Fun, Args, 100)
  108. end
  109. ).
  110. do_teardown(_) ->
  111. ok = net_kernel:stop(),
  112. ok = application:stop(gen_rpc),
  113. ok = meck:unload(gen_rpc),
  114. %% wait for tcp close
  115. timer:sleep(2500).
  116. ensure_distributed_nodename() ->
  117. case net_kernel:start([?NODENAME]) of
  118. {ok, _} ->
  119. ok;
  120. {error, {already_started, _}} ->
  121. net_kernel:stop(),
  122. net_kernel:start([?NODENAME]);
  123. {error, {{shutdown, {_, _, {'EXIT', nodistribution}}}, _}} ->
  124. %% start epmd first
  125. spawn_link(fun() -> os:cmd("epmd") end),
  126. timer:sleep(100),
  127. net_kernel:start([?NODENAME])
  128. end.
  129. %%--------------------------------------------------------------------
  130. %% Generator
  131. %%--------------------------------------------------------------------
  132. nodename() ->
  133. ?LET(
  134. {NodePrefix, HostName},
  135. {node_prefix(), hostname()},
  136. begin
  137. Node = NodePrefix ++ "@" ++ HostName,
  138. list_to_atom(Node)
  139. end
  140. ).
  141. nodename_with_key() ->
  142. ?LET(
  143. {NodePrefix, HostName, Key},
  144. {node_prefix(), hostname(), choose(0, 10)},
  145. begin
  146. Node = NodePrefix ++ "@" ++ HostName,
  147. {list_to_atom(Node), Key}
  148. end
  149. ).
  150. nodesname() ->
  151. oneof([list(nodename()), [node()]]).
  152. nodesname_with_key() ->
  153. oneof([{list(nodename()), choose(0, 10)}, {[node()], 1}]).
  154. node_prefix() ->
  155. oneof(["emqxct", text_like()]).
  156. text_like() ->
  157. ?SUCHTHAT(Text, list(range($a, $z)), (length(Text) =< 100 andalso length(Text) > 0)).
  158. hostname() ->
  159. oneof(["127.0.0.1", "localhost"]).
  160. %%--------------------------------------------------------------------
  161. %% Utils
  162. %%--------------------------------------------------------------------
  163. %% After running the props, the `node()` () is only able to return an
  164. %% incorrect node name - `nonode@nohost`, But we want a distributed nodename
  165. %% So, just translate the `nonode@nohost` to ?NODENAME
  166. punch(Nodes) when is_list(Nodes) ->
  167. lists:map(fun punch/1, Nodes);
  168. punch('nonode@nohost') ->
  169. %% Equal to ?NODENAME
  170. node();
  171. punch(GoodBoy) ->
  172. GoodBoy.