prop_emqx_sys.erl 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2024 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_sys).
  17. -include_lib("proper/include/proper.hrl").
  18. -export([
  19. initial_state/0,
  20. command/1,
  21. precondition/2,
  22. postcondition/3,
  23. next_state/3
  24. ]).
  25. -define(mock_modules, [
  26. emqx_metrics,
  27. emqx_stats,
  28. emqx_broker,
  29. mria,
  30. emqx_hooks,
  31. emqx_config_handler
  32. ]).
  33. -define(ALL(Vars, Types, Exprs),
  34. ?SETUP(
  35. fun() ->
  36. State = do_setup(),
  37. fun() -> do_teardown(State) end
  38. end,
  39. ?FORALL(Vars, Types, Exprs)
  40. )
  41. ).
  42. %%--------------------------------------------------------------------
  43. %% Properties
  44. %%--------------------------------------------------------------------
  45. prop_sys() ->
  46. ?ALL(
  47. Cmds,
  48. commands(?MODULE),
  49. begin
  50. {ok, _Pid} = emqx_sys:start_link(),
  51. {History, State, Result} = run_commands(?MODULE, Cmds),
  52. ok = emqx_sys:stop(),
  53. ?WHENFAIL(
  54. io:format(
  55. "History: ~p\nState: ~p\nResult: ~p\n",
  56. [History, State, Result]
  57. ),
  58. aggregate(command_names(Cmds), Result =:= ok)
  59. )
  60. end
  61. ).
  62. %%--------------------------------------------------------------------
  63. %% Helpers
  64. %%--------------------------------------------------------------------
  65. do_setup() ->
  66. ok = emqx_logger:set_log_level(emergency),
  67. emqx_config:put([sys_topics, sys_msg_interval], 60000),
  68. emqx_config:put([sys_topics, sys_heartbeat_interval], 30000),
  69. emqx_config:put(
  70. [sys_topics, sys_event_messages],
  71. #{
  72. client_connected => true,
  73. client_disconnected => true,
  74. client_subscribed => true,
  75. client_unsubscribed => true
  76. }
  77. ),
  78. [mock(Mod) || Mod <- ?mock_modules],
  79. ok.
  80. do_teardown(_) ->
  81. ok = emqx_logger:set_log_level(error),
  82. [ok = meck:unload(Mod) || Mod <- ?mock_modules],
  83. ok.
  84. mock(Module) ->
  85. ok = meck:new(Module, [passthrough, no_history]),
  86. do_mock(Module).
  87. do_mock(emqx_broker) ->
  88. meck:expect(
  89. emqx_broker,
  90. publish,
  91. fun(Msg) -> {node(), <<"test">>, Msg} end
  92. ),
  93. meck:expect(
  94. emqx_broker,
  95. safe_publish,
  96. fun(Msg) -> {node(), <<"test">>, Msg} end
  97. );
  98. do_mock(emqx_stats) ->
  99. meck:expect(emqx_stats, getstats, fun() -> [0] end);
  100. do_mock(mria) ->
  101. meck:expect(mria, running_nodes, fun() -> [node()] end);
  102. do_mock(emqx_metrics) ->
  103. meck:expect(emqx_metrics, all, fun() -> [{hello, 3}] end);
  104. do_mock(emqx_hooks) ->
  105. meck:expect(emqx_hooks, put, fun(_HookPoint, _MFA, _) -> ok end),
  106. meck:expect(emqx_hooks, del, fun(_HookPoint, _MF) -> ok end);
  107. do_mock(emqx_config_handler) ->
  108. meck:expect(emqx_config_handler, add_handler, fun(_, _) -> ok end).
  109. %%--------------------------------------------------------------------
  110. %% MODEL
  111. %%--------------------------------------------------------------------
  112. %% @doc Initial model value at system start. Should be deterministic.
  113. initial_state() ->
  114. #{}.
  115. %% @doc List of possible commands to run against the system
  116. command(_State) ->
  117. oneof([
  118. {call, emqx_sys, info, []},
  119. {call, emqx_sys, version, []},
  120. {call, emqx_sys, uptime, []},
  121. {call, emqx_sys, datetime, []},
  122. {call, emqx_sys, sysdescr, []},
  123. %------------ unexpected message ----------------------%
  124. {call, emqx_sys, handle_call, [emqx_sys, other, state]},
  125. {call, emqx_sys, handle_cast, [emqx_sys, other]},
  126. {call, emqx_sys, handle_info, [info, state]}
  127. ]).
  128. precondition(_State, {call, _Mod, _Fun, _Args}) ->
  129. true.
  130. postcondition(_State, {call, emqx_sys, info, []}, Info) ->
  131. is_list(Info) andalso length(Info) =:= 4;
  132. postcondition(_State, {call, emqx_sys, version, []}, Version) ->
  133. is_list(Version);
  134. postcondition(_State, {call, emqx_sys, uptime, []}, Uptime) ->
  135. is_integer(Uptime);
  136. postcondition(_State, {call, emqx_sys, datetime, []}, Datetime) ->
  137. is_list(Datetime);
  138. postcondition(_State, {call, emqx_sys, sysdescr, []}, Sysdescr) ->
  139. is_list(Sysdescr);
  140. postcondition(_State, {call, emqx_sys, sys_interval, []}, SysInterval) ->
  141. is_integer(SysInterval) andalso SysInterval > 0;
  142. postcondition(_State, {call, emqx_sys, sys_heartbeat_interval, []}, SysHeartInterval) ->
  143. is_integer(SysHeartInterval) andalso SysHeartInterval > 0;
  144. postcondition(_State, {call, _Mod, _Fun, _Args}, _Res) ->
  145. true.
  146. next_state(State, _Res, {call, _Mod, _Fun, _Args}) ->
  147. NewState = State,
  148. NewState.