|
@@ -0,0 +1,91 @@
|
|
|
+%% @doc A error_logger report handler that be used to capture expected errors in
|
|
|
+%% tests. The current error report handlers are disabled during the execution
|
|
|
+%% of a function. Afterwards, they are restored and the errors that occered are
|
|
|
+%% returned along with the return value of the fun.
|
|
|
+-module(error_logger_acc).
|
|
|
+
|
|
|
+%% Public API
|
|
|
+-export([capture/1]).
|
|
|
+
|
|
|
+-behaviour(gen_event).
|
|
|
+-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
|
|
|
+ code_change/3]).
|
|
|
+
|
|
|
+%% @doc Executes `Fun' and captures all logged errors returns as well as
|
|
|
+%% uncaught errors in `Fun'.
|
|
|
+-spec capture(fun (() -> ResultOfFun)) ->
|
|
|
+ {ok, ResultOfFun, AccumulatedErrors} |
|
|
|
+ {throw | error | exit, Reason, Trace, AccumulatedErrors}
|
|
|
+ when ResultOfFun :: term(),
|
|
|
+ Reason :: term(),
|
|
|
+ Trace :: list(),
|
|
|
+ AccumulatedErrors :: [{error|warning_msg|info_msg, string()} |
|
|
|
+ {error_report|warning_report|info_report, term()}].
|
|
|
+capture(Fun) when is_function(Fun, 0) ->
|
|
|
+ OldHandlers = gen_event:which_handlers(error_logger),
|
|
|
+ error_logger:add_report_handler(?MODULE),
|
|
|
+ lists:foreach(fun error_logger:delete_report_handler/1, OldHandlers),
|
|
|
+ try Fun() of
|
|
|
+ Result ->
|
|
|
+ lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
|
|
|
+ {ok, Result, error_logger:delete_report_handler(?MODULE)}
|
|
|
+ catch
|
|
|
+ Class:Error ->
|
|
|
+ Trace = erlang:get_stacktrace(),
|
|
|
+ lists:foreach(fun error_logger:add_report_handler/1, OldHandlers),
|
|
|
+ AccumulatedErrors = error_logger:delete_report_handler(?MODULE),
|
|
|
+ {Class, Error, Trace, AccumulatedErrors}
|
|
|
+ end.
|
|
|
+
|
|
|
+%% --- gen_event callbacks ---
|
|
|
+
|
|
|
+init([]) ->
|
|
|
+ {ok, []}.
|
|
|
+
|
|
|
+handle_event({ErrorType, _Gleader, {_Pid, Format, Data}}, State) ->
|
|
|
+ ShortError = if
|
|
|
+ ErrorType == error; ErrorType == warning_msg; ErrorType == info_msg ->
|
|
|
+ {ErrorType, lists:flatten(io_lib:format(Format, Data))};
|
|
|
+ true ->
|
|
|
+ {ErrorType, {Format, Data}}
|
|
|
+ end,
|
|
|
+ {ok, [ShortError | State]};
|
|
|
+handle_event(_OtherEvent, State) ->
|
|
|
+ {ok, State}.
|
|
|
+
|
|
|
+handle_call(_Call, State) ->
|
|
|
+ {ok, ignored, State}.
|
|
|
+
|
|
|
+handle_info(_Info, State) ->
|
|
|
+ {ok, State}.
|
|
|
+
|
|
|
+terminate([], State) ->
|
|
|
+ %% error_logger:delete_report_handler/1 called
|
|
|
+ lists:reverse(State);
|
|
|
+terminate(_Arg, _State) ->
|
|
|
+ %% terminating for some other reason.
|
|
|
+ error_logger:info_msg("Accumulating error handler shutting down"),
|
|
|
+ ok.
|
|
|
+
|
|
|
+code_change(_OldVsn, State, _Extra) ->
|
|
|
+ {ok, State}.
|
|
|
+
|
|
|
+-ifdef(TEST).
|
|
|
+-include_lib("eunit/include/eunit.hrl").
|
|
|
+
|
|
|
+capture_success_test() ->
|
|
|
+ Result = ?MODULE:capture(fun () ->
|
|
|
+ error_logger:info_msg("Hello ~p", [world]),
|
|
|
+ error_logger:info_msg("Hello ~p", [again]),
|
|
|
+ foo
|
|
|
+ end),
|
|
|
+ ?assertEqual({ok, foo, [{info_msg, "Hello world"}, {info_msg, "Hello again"}]}, Result).
|
|
|
+
|
|
|
+capture_failure_test() ->
|
|
|
+ Result = ?MODULE:capture(fun () ->
|
|
|
+ error_logger:info_msg("Hello ~p", [world]),
|
|
|
+ throw(foo)
|
|
|
+ end),
|
|
|
+ ?assertMatch({throw, foo, _Trace, [{info_msg, "Hello world"}]}, Result).
|
|
|
+
|
|
|
+-endif.
|