Add test cases for emqx_ctl module
This commit is contained in:
parent
8ecc9ab88a
commit
81e2f47126
|
@ -18,11 +18,12 @@
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-include("types.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-logger_header("[Ctl]").
|
-logger_header("[Ctl]").
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0, stop/0]).
|
||||||
|
|
||||||
-export([ register_command/2
|
-export([ register_command/2
|
||||||
, register_command/3
|
, register_command/3
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
-export([ run_command/1
|
-export([ run_command/1
|
||||||
, run_command/2
|
, run_command/2
|
||||||
, lookup_command/1
|
, lookup_command/1
|
||||||
|
, get_commands/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([ print/1
|
-export([ print/1
|
||||||
|
@ -40,7 +42,7 @@
|
||||||
, usage/2
|
, usage/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% format/1,2 and format_usage/1,2 are exported mainly for test cases
|
%% Exports mainly for test cases
|
||||||
-export([ format/1
|
-export([ format/1
|
||||||
, format/2
|
, format/2
|
||||||
, format_usage/1
|
, format_usage/1
|
||||||
|
@ -63,34 +65,39 @@
|
||||||
-type(cmd_usage() :: {cmd(), cmd_descr()}).
|
-type(cmd_usage() :: {cmd(), cmd_descr()}).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
-define(TAB, emqx_command).
|
-define(CMD_TAB, emqx_command).
|
||||||
|
|
||||||
|
-spec(start_link() -> startlink_ret()).
|
||||||
start_link() ->
|
start_link() ->
|
||||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||||
|
|
||||||
|
-spec(stop() -> ok).
|
||||||
|
stop() -> gen_server:stop(?SERVER).
|
||||||
|
|
||||||
-spec(register_command(cmd(), {module(), atom()}) -> ok).
|
-spec(register_command(cmd(), {module(), atom()}) -> ok).
|
||||||
register_command(Cmd, MF) when is_atom(Cmd) ->
|
register_command(Cmd, MF) when is_atom(Cmd) ->
|
||||||
register_command(Cmd, MF, []).
|
register_command(Cmd, MF, []).
|
||||||
|
|
||||||
-spec(register_command(cmd(), {module(), atom()}, list()) -> ok).
|
-spec(register_command(cmd(), {module(), atom()}, list()) -> ok).
|
||||||
register_command(Cmd, MF, Opts) when is_atom(Cmd) ->
|
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).
|
-spec(unregister_command(cmd()) -> ok).
|
||||||
unregister_command(Cmd) when is_atom(Cmd) ->
|
unregister_command(Cmd) when is_atom(Cmd) ->
|
||||||
cast({unregister_command, Cmd}).
|
cast({unregister_command, Cmd}).
|
||||||
|
|
||||||
cast(Msg) ->
|
call(Req) -> gen_server:call(?SERVER, Req).
|
||||||
gen_server:cast(?SERVER, Msg).
|
|
||||||
|
|
||||||
|
cast(Msg) -> gen_server:cast(?SERVER, Msg).
|
||||||
|
|
||||||
|
-spec(run_command(list(string())) -> ok | {error, term()}).
|
||||||
run_command([]) ->
|
run_command([]) ->
|
||||||
run_command(help, []);
|
run_command(help, []);
|
||||||
run_command([Cmd | Args]) ->
|
run_command([Cmd | Args]) ->
|
||||||
run_command(list_to_atom(Cmd), Args).
|
run_command(list_to_atom(Cmd), Args).
|
||||||
|
|
||||||
-spec(run_command(cmd(), [string()]) -> ok | {error, term()}).
|
-spec(run_command(cmd(), list(string())) -> ok | {error, term()}).
|
||||||
run_command(help, []) ->
|
run_command(help, []) -> help();
|
||||||
help();
|
|
||||||
run_command(Cmd, Args) when is_atom(Cmd) ->
|
run_command(Cmd, Args) when is_atom(Cmd) ->
|
||||||
case lookup_command(Cmd) of
|
case lookup_command(Cmd) of
|
||||||
[{Mod, Fun}] ->
|
[{Mod, Fun}] ->
|
||||||
|
@ -107,15 +114,19 @@ run_command(Cmd, Args) when is_atom(Cmd) ->
|
||||||
|
|
||||||
-spec(lookup_command(cmd()) -> [{module(), atom()}]).
|
-spec(lookup_command(cmd()) -> [{module(), atom()}]).
|
||||||
lookup_command(Cmd) when is_atom(Cmd) ->
|
lookup_command(Cmd) when is_atom(Cmd) ->
|
||||||
case ets:match(?TAB, {{'_', Cmd}, '$1', '_'}) of
|
case ets:match(?CMD_TAB, {{'_', Cmd}, '$1', '_'}) of
|
||||||
[El] -> El;
|
[El] -> El;
|
||||||
[] -> []
|
[] -> []
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec(get_commands() -> list({cmd(), module(), atom()})).
|
||||||
|
get_commands() ->
|
||||||
|
[{Cmd, M, F} || {{_Seq, Cmd}, {M, F}, _Opts} <- ets:tab2list(?CMD_TAB)].
|
||||||
|
|
||||||
help() ->
|
help() ->
|
||||||
print("Usage: ~s~n", [?MODULE]),
|
print("Usage: ~s~n", [?MODULE]),
|
||||||
[begin print("~80..-s~n", [""]), Mod:Cmd(usage) end
|
[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).
|
-spec(print(io:format()) -> ok).
|
||||||
print(Msg) ->
|
print(Msg) ->
|
||||||
|
@ -152,34 +163,33 @@ format_usage(UsageList) ->
|
||||||
format_usage(Cmd, Desc) ->
|
format_usage(Cmd, Desc) ->
|
||||||
CmdLines = split_cmd(Cmd),
|
CmdLines = split_cmd(Cmd),
|
||||||
DescLines = split_cmd(Desc),
|
DescLines = split_cmd(Desc),
|
||||||
lists:foldl(
|
lists:foldl(fun({CmdStr, DescStr}, Usage) ->
|
||||||
fun({CmdStr, DescStr}, Usage) ->
|
Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
|
||||||
Usage ++ format("~-48s# ~s~n", [CmdStr, DescStr])
|
end, "", zip_cmd(CmdLines, DescLines)).
|
||||||
end, "", zip_cmd(CmdLines, DescLines)).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%------------------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
ok = emqx_tables:new(?TAB, [protected, ordered_set]),
|
ok = emqx_tables:new(?CMD_TAB, [protected, ordered_set]),
|
||||||
{ok, #state{seq = 0}}.
|
{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) ->
|
handle_call(Req, _From, State) ->
|
||||||
?LOG(error, "Unexpected call: ~p", [Req]),
|
?LOG(error, "Unexpected call: ~p", [Req]),
|
||||||
{reply, ignored, State}.
|
{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) ->
|
handle_cast({unregister_command, Cmd}, State) ->
|
||||||
ets:match_delete(?TAB, {{'_', Cmd}, '_', '_'}),
|
ets:match_delete(?CMD_TAB, {{'_', Cmd}, '_', '_'}),
|
||||||
noreply(State);
|
noreply(State);
|
||||||
|
|
||||||
handle_cast(Msg, 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([X | Xs], []) -> [{X, ""} | zip_cmd(Xs, [])];
|
||||||
zip_cmd([], [Y | Ys]) -> [{"", Y} | zip_cmd([], Ys)];
|
zip_cmd([], [Y | Ys]) -> [{"", Y} | zip_cmd([], Ys)];
|
||||||
zip_cmd([], []) -> [].
|
zip_cmd([], []) -> [].
|
||||||
|
|
||||||
|
|
|
@ -25,30 +25,81 @@
|
||||||
all() -> emqx_ct:all(?MODULE).
|
all() -> emqx_ct:all(?MODULE).
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
emqx_ct_helpers:boot_modules([]),
|
|
||||||
emqx_ct_helpers:start_apps([]),
|
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
emqx_ct_helpers:stop_apps([]).
|
ok.
|
||||||
|
|
||||||
t_command(_) ->
|
%%--------------------------------------------------------------------
|
||||||
emqx_ctl:start_link(),
|
%% Test cases
|
||||||
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"]) ->
|
t_reg_unreg_command(_) ->
|
||||||
ok;
|
with_ctl_server(
|
||||||
test(["error"]) ->
|
fun(_CtlSrv) ->
|
||||||
error(test_failed);
|
emqx_ctl:register_command(cmd1, {?MODULE, cmd1_fun}),
|
||||||
test(_) ->
|
emqx_ctl:register_command(cmd2, {?MODULE, cmd2_fun}),
|
||||||
io:format("Hello world").
|
?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().
|
||||||
|
|
||||||
|
|
|
@ -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).
|
|
Loading…
Reference in New Issue