Packages

Useful test utilities

Current section

Files

Jump to
testutils src testutils.erl
Raw

src/testutils.erl

-module(testutils).
-behaviour(gen_statem).
% API
-ignore_xref(trace_call/1).
-export([trace_call/1]).
-ignore_xref(await/1).
-export([await/1]).
-ignore_xref(await/2).
-export([await/2]).
% gen_statem Callbacks
-export([init/1]).
-export([callback_mode/0]).
-export([handle_event/4]).
%--- API -----------------------------------------------------------------------
trace_call({M, F, Args}) ->
case code:load_file(M) of
{module, M} -> ok;
_ -> error({undefined_mfa, {M, F, Args}})
end,
{_Pid, Session} = State = trace_session(),
trace:process(Session, all, true, [call]),
MatchSpec = [{'_', [], [{exception_trace}]}],
case trace:function(Session, {M, F, length(Args)}, MatchSpec, [local]) of
0 -> error({undefined_mfa, {M, F, Args}});
_ -> State
end.
await(State) -> await(State, 5000).
await({Pid, _Session}, Timeout) ->
try
gen_statem:call(Pid, wait, Timeout)
catch
exit:{timeout, {gen_statem, call, [Pid, wait, Timeout]}} ->
error(timeout)
end.
%--- gen_statem Callbacks ------------------------------------------------------
init(args) -> {ok, startup, #{called => false}}.
callback_mode() -> handle_event_function.
handle_event({call, From}, {session, Session}, startup, Data) ->
{next_state, listen, Data#{session => Session}, [{reply, From, ok}]};
handle_event(info, {trace, _Pid, return_from, _MFArgs, _Result}, _State, #{wait := From} = Data) ->
{keep_state, Data#{called => true}, [{reply, From, ok}]};
handle_event(info, {trace, _Pid, return_from, _MFArgs}, _State, Data) ->
{keep_state, Data#{called := true}};
handle_event({call, From}, wait, listen, #{called := false} = Data) ->
{keep_state, Data#{wait => From}};
handle_event({call, From}, wait, listen, #{called := true}) ->
{keep_state_and_data, [{reply, From, ok}]};
handle_event({call, From}, Call, State, Data) ->
exit({unknown_call, Call, From, State, Data});
handle_event(_Event, _EventData, _State, _Data) ->
keep_state_and_data.
%--- Internal ------------------------------------------------------------------
trace_session() ->
Name = binary_to_atom(integer_to_binary(erlang:unique_integer())),
{ok, Pid} = gen_statem:start_link(?MODULE, args, []),
Session = trace:session_create(Name, Pid, []),
gen_statem:call(Pid, {session, Session}),
{Pid, Session}.