Erlang, eunit, and gen_server: context clearing failed

I wrote some eunit tests 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 do 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. 

It looks like the test passed, but there is an error in clearing the context, what's wrong, 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}. 
+4
source share
2 answers

In the handle_call/2 function, you return:

 {stop, normal, State} 

not something like:

 {stop, normal, ok, State} 

In other words, you are not answering the call. Then the client sees the server shutting down (with the usual reason), and it cries.

I haven’t tried, but that would be my first guess.

+6
source

Maybe the test ends before the gen_server process gen_server time to shut down. gen_server is associated with the testing process (because it is started with gen_server:start_link/4 ), and when the test finishes, the process is still running and killed by EUnit.

Even if you return ok with {stop, normal, ok, State} , as suggested by Roberto, gen_server can slow down the execution of terminate/2 , which clears the test.

I usually use this function in my tests or breakaways to wait for processes:

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

EUnit has a default latency of 5000 ms, which will be triggered if this function is blocked for too long.

+4
source

All Articles