From 81e2f47126c6bec19aa970a624c75daf88e9d33b Mon Sep 17 00:00:00 2001 From: Feng Lee Date: Fri, 20 Sep 2019 13:47:05 +0800 Subject: [PATCH] Add test cases for emqx_ctl module --- src/emqx_ctl.erl | 67 ++++++++++++++++------------- test/emqx_ctl_SUITE.erl | 91 +++++++++++++++++++++++++++++++--------- test/emqx_ctl_SUTIES.erl | 17 -------- 3 files changed, 110 insertions(+), 65 deletions(-) delete mode 100644 test/emqx_ctl_SUTIES.erl diff --git a/src/emqx_ctl.erl b/src/emqx_ctl.erl index dbde2b60b..95b7b7b73 100644 --- a/src/emqx_ctl.erl +++ b/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({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]), + true = ets:insert(?CMD_TAB, {{OriginSeq, Cmd}, MF, Opts}) + end, + {reply, ok, next_seq(State)}; + 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}); - [[OriginSeq] | _] -> - ?LOG(warning, "CMD ~s is overidden by ~p", [Cmd, MF]), - ets:insert(?TAB, {{OriginSeq, Cmd}, MF, Opts}) - end, - noreply(next_seq(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([], []) -> []. + diff --git a/test/emqx_ctl_SUITE.erl b/test/emqx_ctl_SUITE.erl index 5e18f19c2..43b8f28e0 100644 --- a/test/emqx_ctl_SUITE.erl +++ b/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([]). + ok. -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 cases +%%-------------------------------------------------------------------- -test(["ok"]) -> - ok; -test(["error"]) -> - error(test_failed); -test(_) -> - io:format("Hello world"). +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(). diff --git a/test/emqx_ctl_SUTIES.erl b/test/emqx_ctl_SUTIES.erl deleted file mode 100644 index a3ce8e8b0..000000000 --- a/test/emqx_ctl_SUTIES.erl +++ /dev/null @@ -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).