Erlang、eunit 和 gen_server:上下文清理失败

发布于 2024-11-17 02:12:36 字数 5610 浏览 0 评论 0原文

我在我的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

过去的过去 2024-11-24 02:12:36

handle_call/2 函数中,您将返回:

{stop, normal, State}

而不是类似:

{stop, normal, ok, State}

换句话说,您没有回复呼叫。然后客户端看到服务器终止(由于正常原因)并且它会哭。

没有尝试过,但这将是我的第一个猜测。

In the handle_call/2 function, you're returning:

{stop, normal, State}

rather than something like:

{stop, normal, ok, State}

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.

通知家属抬走 2024-11-24 02:12:36

测试可能在 gen_server 进程有时间关闭之前完成。 gen_server 链接到测试进程(因为它是通过 gen_server:start_link/4 启动的),当测试完成时,该进程仍在运行并被 EUnit 终止。

即使您按照 Roberto 的建议使用 {stop, normal, ok, State} 返回 okgen_server 执行 的速度可能会变慢Terminate/2 比测试需要清理。

我通常在测试或拆卸中使用这样的函数来等待进程:

wait_for_exit(Pid) ->
    MRef = erlang:monitor(process, Pid),
    receive {'DOWN', MRef, _, _, _} -> ok end.

EUnit 有一个默认的超时时间 5000 毫秒,如果该函数阻塞时间过长,就会触发该超时。

It could be that the test finishes before the gen_server process has time to shut down. The gen_server is linked to the test process (because it is started with gen_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, the gen_server might be slower executing terminate/2 than the test takes clean up.

I usually use such a function in my tests or teardowns to wait for processes:

wait_for_exit(Pid) ->
    MRef = erlang:monitor(process, Pid),
    receive {'DOWN', MRef, _, _, _} -> ok end.

EUnit has a default timeout of 5000 ms that will be triggered if this function blocks for too long time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文