Search code examples
erlanggen-servercommon-test

Killing a gen_server without failing the Common Test


I implemented a module which is purposefully supposed to crash (to test the functionality of another module, which is monitoring it). The problem is, when this gen_server crashes it also causes the common test for it to fail. I've tried using try/catch and setting process_flag(trap_exit, true) but nothing seems to work.

Here is some relevant code:

-module(mod_bad_process).

% ...

%% ct calls this function directly
kill() ->
    gen_server:call(?MODULE, {update_behavior, kill}).

% ...

handle_cast({update_behavior, Behavior}, _From, State) ->
    case Behavior of 
        kill -> {stop, killed, State};
        _ -> {reply, ok, State#{state := Behavior}}
    end;

% ...

And the common test:

% ...

-define(BAD_PROC, mod_bad_process).

% ...

remonitor_test(_Conf) ->
    InitialPid = whereis(?BAD_PROC),
    true = undefined =/= InitialPid,
    true = is_monitored_gen_server(?BAD_PROC),
    mod_bad_process:kill(),                     % gen_server crashes
    timer:sleep(?REMONITOR_DELAY_MS),
    FinalPid = whereis(?BAD_PROC),
    true = InitialPid =/= FinalPid,
    true = undefined =/= FinalPid,
    true = is_monitored_gen_server(?BAD_PROC).

% ...

And the resulting error from ct:

*** CT Error Notification 2021-07-16 16:08:20.791 ***
gen_server:call failed on line 238
Reason: {killed,{gen_server,call,...}}

=== Ended at 2021-07-16 16:08:20
=== Location: [{gen_server,call,238},
              {mod_bad_process,kill,48},
              {monitor_tests,remonitor_test,62},
              {test_server,ts_tc,1784},
              {test_server,run_test_case_eval1,1293},
              {test_server,run_test_case_eval,1225}]
=== === Reason: {killed,{gen_server,call,
                                     [mod_bad_process_global,
                                      {update_behavior,kill}]}}
=== 
*** monitor_remonitor_test failed.
    Skipping all other cases in sequence.

Any ideas on how to get this functionality without failing the common test?


Solution

  • The problem was that my try/catch attempts were not pattern matching to the actual error. Here is the fix:

    -module(mod_bad_process).
    
    % ...
    
    kill() ->
        try gen_server:call(?MODULE, {update_behavior, kill}) of 
            _ -> error(failed_to_kill)
        catch
            exit:{killed, _} -> ok
        end.
    % ...