Erlang、eunit 和 gen_server:上下文清理失败
我在我的 gen_server 上写了一些 eunit 测试:
-module(st_db_tests).
-include_lib("eunit/include/eunit.hrl").
main_test_() ->
{foreach,
fun setup/0,
fun cleanup/1,
[
fun db_server_up/1
]}.
setup() ->
{ok,Pid} = st_db:start_link(), Pid.
cleanup(Pid) ->
gen_server:call(Pid, stop).
db_server_up(Pid) ->
?_assertMatch({[{<<"couchdb">>,<<"Welcome">>},{<<"version">>, _}]},
gen_server:call(Pid, test)).
当我进行测试时,我有这个:
./rebar eunit suite=st_db_tests skip_deps=true
==> site_stater (eunit)
Compiled test/st_db_tests.erl
... loading stuff ...
=PROGRESS REPORT==== 27-Jun-2011::12:33:21 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.127.0>},
{name,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
module 'st_db_tests'
*** context cleanup failed ***
::exit:{normal,{gen_server,call,[<0.99.0>,stop]}}
in function gen_server:call/2
=======================================================
Failed: 0. Skipped: 0. Passed: 1.
似乎测试已经通过,但是上下文清理中有 en 错误,这是不对的,对吧?)
我该如何解决这个问题?
PS:我的gen_server
-module(st_db).
-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
-export([start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {db_pid, couch_server_pid}).
%% ====================================================================
%% External functions
%% ====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link() -> {ok, Pid}
%% where
%% Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({global, st_db}, ?MODULE, ["localhost", 5984, "site_stater"], []).
%% ====================================================================
%% Server internal functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% --------------------------------------------------------------------
init([Server, Port, DB]) ->
couchbeam:start(),
CouchServer = couchbeam:server_connection(Server, Port, "", []),
{ok, CouchDB} = couchbeam:open_or_create_db(CouchServer, DB, []),
{ok, #state{db_pid=CouchDB, couch_server_pid=CouchServer}}.
%% ====================================================================
%% DB manipulation functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} | (terminate/2 is called)
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call (test, _From, #state{couch_server_pid = Couch_server_pid} = State) ->
{ok, Version} = couchbeam:server_info(Couch_server_pid),
{reply, Version, State};
handle_call(stop, _From, State) ->
{stop, normal, State}.
%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
I wrote some eunit test on my gen_server:
-module(st_db_tests).
-include_lib("eunit/include/eunit.hrl").
main_test_() ->
{foreach,
fun setup/0,
fun cleanup/1,
[
fun db_server_up/1
]}.
setup() ->
{ok,Pid} = st_db:start_link(), Pid.
cleanup(Pid) ->
gen_server:call(Pid, stop).
db_server_up(Pid) ->
?_assertMatch({[{<<"couchdb">>,<<"Welcome">>},{<<"version">>, _}]},
gen_server:call(Pid, test)).
When I make the test I have this:
./rebar eunit suite=st_db_tests skip_deps=true
==> site_stater (eunit)
Compiled test/st_db_tests.erl
... loading stuff ...
=PROGRESS REPORT==== 27-Jun-2011::12:33:21 ===
supervisor: {local,kernel_safe_sup}
started: [{pid,<0.127.0>},
{name,inet_gethost_native_sup},
{mfargs,{inet_gethost_native,start_link,[]}},
{restart_type,temporary},
{shutdown,1000},
{child_type,worker}]
module 'st_db_tests'
*** context cleanup failed ***
::exit:{normal,{gen_server,call,[<0.99.0>,stop]}}
in function gen_server:call/2
=======================================================
Failed: 0. Skipped: 0. Passed: 1.
Seems like the test has passed, but there is en error in context cleanup, which is not right, right?)
How can I fix this?
PS: my gen_server
-module(st_db).
-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
%% --------------------------------------------------------------------
%% External exports
-export([start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {db_pid, couch_server_pid}).
%% ====================================================================
%% External functions
%% ====================================================================
%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link() -> {ok, Pid}
%% where
%% Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({global, st_db}, ?MODULE, ["localhost", 5984, "site_stater"], []).
%% ====================================================================
%% Server internal functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% --------------------------------------------------------------------
init([Server, Port, DB]) ->
couchbeam:start(),
CouchServer = couchbeam:server_connection(Server, Port, "", []),
{ok, CouchDB} = couchbeam:open_or_create_db(CouchServer, DB, []),
{ok, #state{db_pid=CouchDB, couch_server_pid=CouchServer}}.
%% ====================================================================
%% DB manipulation functions
%% ====================================================================
%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} | (terminate/2 is called)
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call (test, _From, #state{couch_server_pid = Couch_server_pid} = State) ->
{ok, Version} = couchbeam:server_info(Couch_server_pid),
{reply, Version, State};
handle_call(stop, _From, State) ->
{stop, normal, State}.
%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State} (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在
handle_call/2
函数中,您将返回:而不是类似:
换句话说,您没有回复呼叫。然后客户端看到服务器终止(由于正常原因)并且它会哭。
没有尝试过,但这将是我的第一个猜测。
In the
handle_call/2
function, you're returning:rather than something like:
In other words, you're not replying to the call. The client then see the server terminating (with normal reason) and it cries.
Didn't try it, but this would be my first guess.
测试可能在
gen_server
进程有时间关闭之前完成。gen_server
链接到测试进程(因为它是通过gen_server:start_link/4
启动的),当测试完成时,该进程仍在运行并被 EUnit 终止。即使您按照 Roberto 的建议使用
{stop, normal, ok, State}
返回ok
,gen_server
执行的速度可能会变慢Terminate/2
比测试需要清理。我通常在测试或拆卸中使用这样的函数来等待进程:
EUnit 有一个默认的超时时间 5000 毫秒,如果该函数阻塞时间过长,就会触发该超时。
It could be that the test finishes before the
gen_server
process has time to shut down. Thegen_server
is linked to the test process (because it is started withgen_server:start_link/4
) and when the test finishes, the process is still running and is killed by EUnit.Even if you return
ok
using{stop, normal, ok, State}
as suggested by Roberto, thegen_server
might be slower executingterminate/2
than the test takes clean up.I usually use such a function in my tests or teardowns to wait for processes:
EUnit has a default timeout of 5000 ms that will be triggered if this function blocks for too long time.