mirror of
https://github.com/correl/rebar.git
synced 2024-11-15 03:00:18 +00:00
Merge slf-eunit-process-isolation2 branch
This commit is contained in:
commit
9832a5c5c6
1 changed files with 148 additions and 0 deletions
|
@ -29,6 +29,15 @@
|
||||||
%% <ul>
|
%% <ul>
|
||||||
%% <li>eunit - runs eunit tests</li>
|
%% <li>eunit - runs eunit tests</li>
|
||||||
%% <li>clean - remove .eunit directory</li>
|
%% <li>clean - remove .eunit directory</li>
|
||||||
|
%% <li>reset_after_eunit::boolean() - default = true.
|
||||||
|
%% If true, try to "reset" VM state to approximate state prior to
|
||||||
|
%% running the EUnit tests:
|
||||||
|
%% <ul>
|
||||||
|
%% <li> Stop net_kernel if it was started </li>
|
||||||
|
%% <li> Stop OTP applications not running before EUnit tests were run </li>
|
||||||
|
%% <li> Kill processes not running before EUnit tests were run </li>
|
||||||
|
%% <li> Reset OTP application environment variables </li>
|
||||||
|
%% </ul> </li>
|
||||||
%% </ul>
|
%% </ul>
|
||||||
%% The following Global options are supported:
|
%% The following Global options are supported:
|
||||||
%% <ul>
|
%% <ul>
|
||||||
|
@ -130,11 +139,20 @@ eunit(Config, AppFile) ->
|
||||||
|
|
||||||
{ok, CoverLog} = cover_init(Config, BeamFiles),
|
{ok, CoverLog} = cover_init(Config, BeamFiles),
|
||||||
|
|
||||||
|
StatusBefore = status_before_eunit(),
|
||||||
EunitResult = perform_eunit(Config, Modules),
|
EunitResult = perform_eunit(Config, Modules),
|
||||||
perform_cover(Config, Modules, SrcModules),
|
perform_cover(Config, Modules, SrcModules),
|
||||||
|
|
||||||
cover_close(CoverLog),
|
cover_close(CoverLog),
|
||||||
|
|
||||||
|
case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
|
||||||
|
true) of
|
||||||
|
true ->
|
||||||
|
reset_after_eunit(StatusBefore);
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
|
||||||
case EunitResult of
|
case EunitResult of
|
||||||
ok ->
|
ok ->
|
||||||
ok;
|
ok;
|
||||||
|
@ -439,3 +457,133 @@ percentage(0, 0) ->
|
||||||
"not executed";
|
"not executed";
|
||||||
percentage(Cov, NotCov) ->
|
percentage(Cov, NotCov) ->
|
||||||
integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%".
|
integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%".
|
||||||
|
|
||||||
|
get_app_names() ->
|
||||||
|
[AppName || {AppName, _, _} <- application:loaded_applications()].
|
||||||
|
|
||||||
|
status_before_eunit() ->
|
||||||
|
Apps = get_app_names(),
|
||||||
|
AppEnvs = [{App, application:get_all_env(App)} || App <- Apps],
|
||||||
|
{erlang:processes(), erlang:is_alive(), AppEnvs, ets:tab2list(ac_tab)}.
|
||||||
|
|
||||||
|
reset_after_eunit({OldProcesses, WasAlive, OldAppEnvs, _OldACs}) ->
|
||||||
|
IsAlive = erlang:is_alive(),
|
||||||
|
if not WasAlive andalso IsAlive ->
|
||||||
|
?DEBUG("Stopping net kernel....\n", []),
|
||||||
|
erl_epmd:stop(),
|
||||||
|
net_kernel:stop(),
|
||||||
|
pause_until_net_kernel_stopped();
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
|
||||||
|
Processes = erlang:processes(),
|
||||||
|
kill_extras(Processes -- OldProcesses),
|
||||||
|
|
||||||
|
OldApps = [App || {App, _} <- OldAppEnvs],
|
||||||
|
Apps = get_app_names(),
|
||||||
|
[begin
|
||||||
|
case lists:member(App, OldApps) of
|
||||||
|
true -> ok;
|
||||||
|
false -> application:stop(App)
|
||||||
|
end,
|
||||||
|
application:unset_env(App, K)
|
||||||
|
end || App <- Apps, App /= rebar,
|
||||||
|
{K, _V} <- application:get_all_env(App)],
|
||||||
|
|
||||||
|
reconstruct_app_env_vars(Apps),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
kill_extras(Pids) ->
|
||||||
|
%% Killing any of the procs below will either:
|
||||||
|
%% 1. Interfere with stuff that we don't want interfered with, or
|
||||||
|
%% 2. May/will force the 'kernel' app to shutdown, which *will*
|
||||||
|
%% interfere with rebar's ability To Do Useful Stuff(tm).
|
||||||
|
%% This list may require changes as OTP versions and/or
|
||||||
|
%% rebar use cases change.
|
||||||
|
KeepProcs = [cover_server, eunit_server,
|
||||||
|
eqc, eqc_license, eqc_locked],
|
||||||
|
Killed = [begin
|
||||||
|
Info = case erlang:process_info(Pid) of
|
||||||
|
undefined -> [];
|
||||||
|
Else -> Else
|
||||||
|
end,
|
||||||
|
Keep1 = case proplists:get_value(registered_name, Info) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
Name ->
|
||||||
|
lists:member(Name, KeepProcs)
|
||||||
|
end,
|
||||||
|
Keep2 = case proplists:get_value(dictionary, Info) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
Ds ->
|
||||||
|
case proplists:get_value('$ancestors', Ds) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
As ->
|
||||||
|
lists:member(kernel_sup, As)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
if Keep1 orelse Keep2 ->
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
?DEBUG("Kill ~p ~p\n", [Pid, Info]),
|
||||||
|
exit(Pid, kill),
|
||||||
|
Pid
|
||||||
|
end
|
||||||
|
end || Pid <- Pids],
|
||||||
|
case lists:usort(Killed) -- [ok] of
|
||||||
|
[] ->
|
||||||
|
?DEBUG("No processes to kill\n", []),
|
||||||
|
[];
|
||||||
|
Else ->
|
||||||
|
[wait_until_dead(Pid) || Pid <- Else],
|
||||||
|
Else
|
||||||
|
end.
|
||||||
|
|
||||||
|
reconstruct_app_env_vars([App|Apps]) ->
|
||||||
|
CmdLine0 = proplists:get_value(App, init:get_arguments(), []),
|
||||||
|
CmdVars = [{list_to_atom(K), list_to_atom(V)} || {K, V} <- CmdLine0],
|
||||||
|
AppFile = (catch filename:join([code:lib_dir(App),
|
||||||
|
"ebin",
|
||||||
|
atom_to_list(App) ++ ".app"])),
|
||||||
|
AppVars = case file:consult(AppFile) of
|
||||||
|
{ok, [{application, App, Ps}]} ->
|
||||||
|
proplists:get_value(env, Ps, []);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
AllVars = CmdVars ++ AppVars,
|
||||||
|
?DEBUG("Reconstruct ~p ~p\n", [App, AllVars]),
|
||||||
|
[application:set_env(App, K, V) || {K, V} <- AllVars],
|
||||||
|
reconstruct_app_env_vars(Apps);
|
||||||
|
reconstruct_app_env_vars([]) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
wait_until_dead(Pid) when is_pid(Pid) ->
|
||||||
|
Ref = monitor(process, Pid),
|
||||||
|
receive
|
||||||
|
{'DOWN', Ref, process, _Obj, Info} ->
|
||||||
|
Info
|
||||||
|
after 10*1000 ->
|
||||||
|
exit({timeout_waiting_for, Pid})
|
||||||
|
end;
|
||||||
|
wait_until_dead(_) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
pause_until_net_kernel_stopped() ->
|
||||||
|
pause_until_net_kernel_stopped(10).
|
||||||
|
|
||||||
|
pause_until_net_kernel_stopped(0) ->
|
||||||
|
exit(net_kernel_stop_failed);
|
||||||
|
pause_until_net_kernel_stopped(N) ->
|
||||||
|
try
|
||||||
|
_ = net_kernel:i(),
|
||||||
|
timer:sleep(100),
|
||||||
|
pause_until_net_kernel_stopped(N - 1)
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
?DEBUG("Stopped net kernel.\n", []),
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
Loading…
Reference in a new issue