Browse Source

Add unit tests for gen_fsm/statem crash msgs

Don't use dirty timeout for gen_statem

That was introduced in OTP 19.2, and causes
failures in tests on OTP 19 and 19.1

Fix statem for all 19.x releases
pull/391/head
Mark Allen 8 years ago
parent
commit
3784ea7467
4 changed files with 171 additions and 1 deletions
  1. +2
    -1
      rebar.config
  2. +34
    -0
      test/crash_fsm.erl
  3. +42
    -0
      test/crash_statem.erl
  4. +93
    -0
      test/lager_test_backend.erl

+ 2
- 1
rebar.config View File

@ -21,6 +21,7 @@
{erl_opts, [
{lager_extra_sinks, ['__lager_test_sink']},
{platform_define, "19", test_statem},
debug_info,
report,
verbose,
@ -47,7 +48,7 @@
]}.
{deps, [
{goldrush, ".*", {git, "https://github.com/basho/goldrush.git", {tag, "0.1.9"}}}
{goldrush, ".*", {git, "https://github.com/DeadZen/goldrush.git", {tag, "0.1.9"}}}
]}.
{xref_checks, []}.

+ 34
- 0
test/crash_fsm.erl View File

@ -0,0 +1,34 @@
-module(crash_fsm).
-behaviour(gen_fsm).
-export([start/0, crash/0, state1/2]).
%% gen_fsm callbacks
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
terminate/3, code_change/4]).
-record(state, {}).
start() ->
gen_fsm:start({local, ?MODULE}, ?MODULE, [], []).
crash() ->
gen_fsm:sync_send_event(?MODULE, crash).
%% gen_fsm callbacks
init([]) ->
{ok, state1, #state{}}.
handle_event(_Event, StateName, State) ->
{next_state, StateName, State}.
handle_sync_event(_Event, _From, StateName, State) ->
Reply = ok,
{reply, Reply, StateName, State}.
handle_info(_Info, StateName, State) ->
{next_state, StateName, State}.
terminate(_Reason, _StateName, _State) ->
ok.
code_change(_OldVersion, StateName, State, _Extra) ->
{ok, StateName, State}.
state1(_Event, S) -> {next_state, state1, S}.

+ 42
- 0
test/crash_statem.erl View File

@ -0,0 +1,42 @@
-module(crash_statem).
%% we're only going to compile this on OTP 19+
-ifdef(test_statem).
-behaviour(gen_statem).
-export([
start/0,
crash/0,
handle_event/4
]).
-export([terminate/3,code_change/4,init/1,callback_mode/0]).
start() ->
gen_statem:start({local,?MODULE}, ?MODULE, [], []).
crash() ->
gen_statem:call(?MODULE, boom).
%% Mandatory callback functions
terminate(_Reason, _State, _Data) -> ok.
code_change(_Vsn, State, Data, _Extra) -> {ok,State,Data}.
init([]) ->
%% insert rant here about breaking changes in minor versions...
case erlang:system_info(version) of
"8.0" -> {callback_mode(),state1,undefined};
_ -> {ok, state1, undefined}
end.
callback_mode() -> handle_event_function.
%%% state callback(s)
handle_event({call, _From}, state1, _Arg, Data) ->
{next_state, state1, Data}.
-else.
-export([start/0, crash/0]).
start() -> ok.
crash() -> ok.
-endif.

+ 93
- 0
test/lager_test_backend.erl View File

@ -913,6 +913,99 @@ error_logger_redirect_crash_cleanup(_Sink) ->
end,
error_logger:tty(true).
crash_fsm_setup() ->
error_logger:tty(false),
application:load(lager),
application:set_env(lager, error_logger_redirect, true),
application:set_env(lager, handlers, [{?MODULE, error}]),
lager:start(),
crash_fsm:start(),
crash_statem:start(),
lager:log(error, self(), "flush flush"),
timer:sleep(100),
gen_event:call(lager_event, ?MODULE, flush),
lager_event.
crash_fsm_sink_setup() ->
ErrorSink = error_logger_lager_event,
error_logger:tty(false),
application:load(lager),
application:set_env(lager, error_logger_redirect, true),
application:set_env(lager, handlers, []),
application:set_env(lager, extra_sinks, [{ErrorSink, [{handlers, [{?MODULE, error}]}]}]),
lager:start(),
crash_fsm:start(),
crash_statem:start(),
lager:log(ErrorSink, error, self(), "flush flush", []),
timer:sleep(100),
flush(ErrorSink),
ErrorSink.
crash_fsm_cleanup(_Sink) ->
application:stop(lager),
application:stop(goldrush),
application:unset_env(lager, extra_sinks),
lists:foreach(fun(N) -> kill_crasher(N) end, [crash_fsm, crash_statem]),
error_logger:tty(true).
kill_crasher(RegName) ->
case whereis(RegName) of
undefined -> ok;
Pid -> exit(Pid, kill)
end.
spawn_fsm_crash(Module) ->
spawn(fun() -> Module:crash() end),
timer:sleep(100),
_ = gen_event:which_handlers(error_logger),
ok.
crash_fsm_test_() ->
TestBody = fun(Name, FsmModule, Expected) ->
fun(Sink) ->
{Name,
fun() ->
case {FsmModule =:= crash_statem, lager_util:otp_version() < 19} of
{true, true} -> ok;
_ ->
Pid = whereis(FsmModule),
spawn_fsm_crash(FsmModule),
{Level, _, Msg, Metadata} = pop(Sink),
test_body(Expected, lists:flatten(Msg)),
?assertEqual(Pid, proplists:get_value(pid, Metadata)),
?assertEqual(lager_util:level_to_num(error), Level)
end
end
}
end
end,
Tests = [
fun(Sink) ->
{"again, there is nothing up my sleeve",
fun() ->
?assertEqual(undefined, pop(Sink)),
?assertEqual(0, count(Sink))
end
}
end,
TestBody("gen_fsm crash", crash_fsm, "gen_fsm crash_fsm in state state1 terminated with reason: call to undefined function crash_fsm:state1/3 from gen_fsm:handle_msg/7"),
TestBody("gen_statem crash", crash_statem, "gen_statem crash_statem in state state1 terminated with reason: no function clause matching crash_statem:handle")
],
{"FSM crash output tests", [
{"Default sink",
{foreach,
fun crash_fsm_setup/0,
fun crash_fsm_cleanup/1,
Tests}},
{"Error logger sink",
{foreach,
fun crash_fsm_sink_setup/0,
fun crash_fsm_cleanup/1,
Tests}}
]}.
error_logger_redirect_crash_test_() ->
TestBody=fun(Name,CrashReason,Expected) ->
fun(Sink) ->

Loading…
Cancel
Save