Explorar el Código

Add test cases for emqx_ctl module

Feng Lee hace 6 años
padre
commit
81e2f47126
Se han modificado 3 ficheros con 112 adiciones y 67 borrados
  1. 39 28
      src/emqx_ctl.erl
  2. 73 22
      test/emqx_ctl_SUITE.erl
  3. 0 17
      test/emqx_ctl_SUTIES.erl

+ 39 - 28
src/emqx_ctl.erl

@@ -18,11 +18,12 @@
 
 -behaviour(gen_server).
 
+-include("types.hrl").
 -include("logger.hrl").
 
 -logger_header("[Ctl]").
 
--export([start_link/0]).
+-export([start_link/0, stop/0]).
 
 -export([ register_command/2
         , register_command/3
@@ -32,6 +33,7 @@
 -export([ run_command/1
         , run_command/2
         , lookup_command/1
+        , get_commands/0
         ]).
 
 -export([ print/1
@@ -40,7 +42,7 @@
         , usage/2
         ]).
 
-%% format/1,2 and format_usage/1,2 are exported mainly for test cases
+%% Exports mainly for test cases
 -export([ format/1
         , format/2
         , format_usage/1
@@ -63,34 +65,39 @@
 -type(cmd_usage() :: {cmd(), cmd_descr()}).
 
 -define(SERVER, ?MODULE).
--define(TAB, emqx_command).
+-define(CMD_TAB, emqx_command).
 
+-spec(start_link() -> startlink_ret()).
 start_link() ->
     gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
 
+-spec(stop() -> ok).
+stop() -> gen_server:stop(?SERVER).
+
 -spec(register_command(cmd(), {module(), atom()}) -> ok).
 register_command(Cmd, MF) when is_atom(Cmd) ->
     register_command(Cmd, MF, []).
 
 -spec(register_command(cmd(), {module(), atom()}, list()) -> ok).
 register_command(Cmd, MF, Opts) when is_atom(Cmd) ->
-    cast({register_command, Cmd, MF, Opts}).
+    call({register_command, Cmd, MF, Opts}).
 
 -spec(unregister_command(cmd()) -> ok).
 unregister_command(Cmd) when is_atom(Cmd) ->
     cast({unregister_command, Cmd}).
 
-cast(Msg) ->
-    gen_server:cast(?SERVER, Msg).
+call(Req) -> gen_server:call(?SERVER, Req).
+
+cast(Msg) -> gen_server:cast(?SERVER, Msg).
 
+-spec(run_command(list(string())) -> ok | {error, term()}).
 run_command([]) ->
     run_command(help, []);
 run_command([Cmd | Args]) ->
     run_command(list_to_atom(Cmd), Args).
 
--spec(run_command(cmd(), [string()]) -> ok | {error, term()}).
-run_command(help, []) ->
-    help();
+-spec(run_command(cmd(), list(string())) -> ok | {error, term()}).
+run_command(help, []) -> help();
 run_command(Cmd, Args) when is_atom(Cmd) ->
     case lookup_command(Cmd) of
         [{Mod, Fun}] ->
@@ -107,15 +114,19 @@ run_command(Cmd, Args) when is_atom(Cmd) ->
 
 -spec(lookup_command(cmd()) -> [{module(), atom()}]).
 lookup_command(Cmd) when is_atom(Cmd) ->
-    case ets:match(?TAB, {{'_', Cmd}, '$1', '_'}) of
+    case ets:match(?CMD_TAB, {{'_', Cmd}, '$1', '_'}) of
         [El] -> El;
         []   -> []
     end.
 
+-spec(get_commands() -> list({cmd(), module(), atom()})).
+get_commands() ->
+    [{Cmd, M, F} || {{_Seq, Cmd}, {M, F}, _Opts} <- ets:tab2list(?CMD_TAB)].
+
 help() ->
     print("Usage: ~s~n", [?MODULE]),
     [begin print("~80..-s~n", [""]), Mod:Cmd(usage) end
-     || {_, {Mod, Cmd}, _} <- ets:tab2list(?TAB)].
+     || {_, {Mod, Cmd}, _} <- ets:tab2list(?CMD_TAB)].
 
 -spec(print(io:format()) -> ok).
 print(Msg) ->
@@ -152,34 +163,33 @@ format_usage(UsageList) ->
 format_usage(Cmd, Desc) ->
     CmdLines = split_cmd(Cmd),
     DescLines = split_cmd(Desc),
-    lists:foldl(
-        fun({CmdStr, DescStr}, Usage) ->
-            Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
-        end, "", zip_cmd(CmdLines, DescLines)).
+    lists:foldl(fun({CmdStr, DescStr}, Usage) ->
+                        Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
+                end, "", zip_cmd(CmdLines, DescLines)).
 
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 %% gen_server callbacks
-%%------------------------------------------------------------------------------
+%%--------------------------------------------------------------------
 
 init([]) ->
-    ok = emqx_tables:new(?TAB, [protected, ordered_set]),
+    ok = emqx_tables:new(?CMD_TAB, [protected, ordered_set]),
     {ok, #state{seq = 0}}.
 
-handle_call(Req, _From, State) ->
-    ?LOG(error, "Unexpected call: ~p", [Req]),
-    {reply, ignored, State}.
-
-handle_cast({register_command, Cmd, MF, Opts}, State = #state{seq = Seq}) ->
-    case ets:match(?TAB, {{'$1', Cmd}, '_', '_'}) of
-        [] -> ets:insert(?TAB, {{Seq, Cmd}, MF, Opts});
+handle_call({register_command, Cmd, MF, Opts}, _From, State = #state{seq = Seq}) ->
+    case ets:match(?CMD_TAB, {{'$1', Cmd}, '_', '_'}) of
+        [] -> ets:insert(?CMD_TAB, {{Seq, Cmd}, MF, Opts});
         [[OriginSeq] | _] ->
             ?LOG(warning, "CMD ~s is overidden by ~p", [Cmd, MF]),
-            ets:insert(?TAB, {{OriginSeq, Cmd}, MF, Opts})
+            true = ets:insert(?CMD_TAB, {{OriginSeq, Cmd}, MF, Opts})
     end,
-    noreply(next_seq(State));
+    {reply, ok, next_seq(State)};
+
+handle_call(Req, _From, State) ->
+    ?LOG(error, "Unexpected call: ~p", [Req]),
+    {reply, ignored, State}.
 
 handle_cast({unregister_command, Cmd}, State) ->
-    ets:match_delete(?TAB, {{'_', Cmd}, '_', '_'}),
+    ets:match_delete(?CMD_TAB, {{'_', Cmd}, '_', '_'}),
     noreply(State);
 
 handle_cast(Msg, State) ->
@@ -214,3 +224,4 @@ zip_cmd([X | Xs], [Y | Ys]) -> [{X, Y} | zip_cmd(Xs, Ys)];
 zip_cmd([X | Xs], []) -> [{X, ""} | zip_cmd(Xs, [])];
 zip_cmd([], [Y | Ys]) -> [{"", Y} | zip_cmd([], Ys)];
 zip_cmd([], []) -> [].
+

+ 73 - 22
test/emqx_ctl_SUITE.erl

@@ -25,30 +25,81 @@
 all() -> emqx_ct:all(?MODULE).
 
 init_per_suite(Config) ->
-    emqx_ct_helpers:boot_modules([]),
-    emqx_ct_helpers:start_apps([]),
     Config.
 
 end_per_suite(_Config) ->
-    emqx_ct_helpers:stop_apps([]).
-
-t_command(_) ->
-    emqx_ctl:start_link(),
-    emqx_ctl:register_command(test, {?MODULE, test}),
-    ct:sleep(50),
-    ?assertEqual([{emqx_ctl_SUITE,test}], emqx_ctl:lookup_command(test)),
-    ?assertEqual(ok, emqx_ctl:run_command(["test", "ok"])),
-    ?assertEqual({error, test_failed}, emqx_ctl:run_command(["test", "error"])),
-    ?assertEqual({error, cmd_not_found}, emqx_ctl:run_command(["test2", "ok"])),
-    emqx_ctl:unregister_command(test),
-    ct:sleep(50),
-    ?assertEqual([], emqx_ctl:lookup_command(test)).
-
-test(["ok"]) ->
-    ok;
-test(["error"]) ->
-    error(test_failed);
-test(_) ->
-    io:format("Hello world").
+    ok.
 
+%%--------------------------------------------------------------------
+%% Test cases
+%%--------------------------------------------------------------------
+
+t_reg_unreg_command(_) ->
+    with_ctl_server(
+      fun(_CtlSrv) ->
+            emqx_ctl:register_command(cmd1, {?MODULE, cmd1_fun}),
+            emqx_ctl:register_command(cmd2, {?MODULE, cmd2_fun}),
+            ?assertEqual([{?MODULE, cmd1_fun}], emqx_ctl:lookup_command(cmd1)),
+            ?assertEqual([{?MODULE, cmd2_fun}], emqx_ctl:lookup_command(cmd2)),
+            ?assertEqual([{cmd1, ?MODULE, cmd1_fun}, {cmd2, ?MODULE, cmd2_fun}],
+                         emqx_ctl:get_commands()),
+            emqx_ctl:unregister_command(cmd1),
+            emqx_ctl:unregister_command(cmd2),
+            ct:sleep(100),
+            ?assertEqual([], emqx_ctl:lookup_command(cmd1)),
+            ?assertEqual([], emqx_ctl:lookup_command(cmd2)),
+            ?assertEqual([], emqx_ctl:get_commands())
+      end).
+
+t_run_commands(_) ->
+    with_ctl_server(
+      fun(_CtlSrv) ->
+            ?assertEqual({error, cmd_not_found}, emqx_ctl:run_command(["cmd", "arg"])),
+            emqx_ctl:register_command(cmd1, {?MODULE, cmd1_fun}),
+            emqx_ctl:register_command(cmd2, {?MODULE, cmd2_fun}),
+            ok = emqx_ctl:run_command(["cmd1", "arg"]),
+            {error, badarg} = emqx_ctl:run_command(["cmd1", "badarg"]),
+            ok = emqx_ctl:run_command(["cmd2", "arg1", "arg2"]),
+            {error, badarg} = emqx_ctl:run_command(["cmd2", "arg1", "badarg"])
+      end).
+
+t_print(_) ->
+    emqx_ctl:print("help").
+
+t_usage(_) ->
+    emqx_ctl:usage([{cmd1, "Cmd1 usage"}, {cmd2, "Cmd2 usage"}]),
+    emqx_ctl:usage(cmd1, "Cmd1 usage"),
+    emqx_ctl:usage(cmd2, "Cmd2 usage").
+
+t_format(_) ->
+    emqx_ctl:format("help"),
+    emqx_ctl:format("~s", [help]).
+
+t_format_usage(_) ->
+    emqx_ctl:format_usage(cmd1, "Cmd1 usage"),
+    emqx_ctl:format_usage([{cmd1, "Cmd1 usage"}, {cmd2, "Cmd2 usage"}]).
+
+t_unexpected(_) ->
+    with_ctl_server(
+      fun(CtlSrv) ->
+              ignored = gen_server:call(CtlSrv, unexpected_call),
+              ok = gen_server:cast(CtlSrv, unexpected_cast),
+              CtlSrv ! unexpected_info,
+              ?assert(is_process_alive(CtlSrv))
+      end).
+
+%%--------------------------------------------------------------------
+%% Cmds for test
+%%--------------------------------------------------------------------
+
+cmd1_fun(["arg"]) -> ok;
+cmd1_fun(["badarg"]) -> error(badarg).
+
+cmd2_fun(["arg1", "arg2"]) -> ok;
+cmd2_fun(["arg1", "badarg"]) -> error(badarg).
+
+with_ctl_server(Fun) ->
+    {ok, Pid} = emqx_ctl:start_link(),
+    _ = Fun(Pid),
+    ok = emqx_ctl:stop().
 

+ 0 - 17
test/emqx_ctl_SUTIES.erl

@@ -1,17 +0,0 @@
-%%--------------------------------------------------------------------
-%% 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_ctl_SUTIES).