-module(upgrade_client).
-export([run/5]).
%%-define(line, io:format("~s:~p\n", [?MODULE,?LINE]),).
-define(line,).
run(Dir, Upgradee1, Upgradee2, Other1, Other2) ->
%% Load version 1 of upgradee
code_SUITE:compile_load(upgradee, Dir, 1, Upgradee1),
Tracer = start_tracing(),
?line 1 = upgradee:exp1(),
?line 1 = upgradee:exp1exp2(),
?line 1 = upgradee:exp1loc2(),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
P = spawn_link(upgradee,dispatch_loop,[]),
?line 1 = proxy_call(P, local, exp1),
?line 1 = proxy_call(P, local, loc1),
?line 1 = proxy_call(P, local, exp1exp2),
?line 1 = proxy_call(P, local, exp1loc2),
?line 1 = proxy_call(P, local, loc1exp2),
?line 1 = proxy_call(P, local, loc1loc2),
?line 1 = proxy_call(P, external, exp1),
?line 1 = proxy_call(P, external, exp1exp2),
?line 1 = proxy_call(P, external, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
?line {cannot_compile,1} = proxy_call(P, local, exp2),
?line {cannot_compile,1} = proxy_call(P, local, loc2),
?line {'EXIT',{undef,_}} = (catch other:exp1()),
?line {'EXIT',{undef,_}} = (catch other:loc1()),
?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch other:exp1exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc11exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch other:exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc2()),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
Env1 = "Env1",
put(loc1_fun, upgradee:get_local_fun(Env1)),
?line {1,Env1} = (get(loc1_fun))(),
put(exp1exp2_fun, upgradee:get_exp1exp2_fun()),
?line 1 = (get(exp1exp2_fun))(),
ok = check_tracing(Tracer, 13),
%%
%% Load version 1 of other
%%
code_SUITE:compile_load(other, Dir, 1, Other1),
?line 1 = other:exp1(),
?line 1 = other:exp1loc2(),
?line 1 = other:exp1exp2(),
?line {'EXIT',{undef,_}} = (catch other:loc1()),
?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch other:exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc2()),
?line 1 = proxy_call(P, other, exp1),
?line 1 = proxy_call(P, other, exp1loc2),
?line 1 = proxy_call(P, other, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
ok = check_tracing(Tracer, 5),
%%
%% Load version 2 of upgradee
%%
code_SUITE:compile_load(upgradee, Dir, 2, Upgradee2),
?line 2 = upgradee:exp2(),
?line 2 = upgradee:exp1exp2(),
?line 2 = upgradee:loc1exp2(),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
?line 1 = proxy_call(P, local, exp1),
?line 1 = proxy_call(P, local, loc1),
?line 1 = proxy_call(P, local, exp1exp2),
?line 1 = proxy_call(P, local, exp1loc2),
?line 1 = proxy_call(P, local, loc1exp2),
?line 1 = proxy_call(P, local, loc1loc2),
?line {cannot_compile,1} = proxy_call(P, local, exp2),
?line {cannot_compile,1} = proxy_call(P, local, loc2),
?line 2 = proxy_call(P, external, exp1exp2),
?line 2 = proxy_call(P, external, loc1exp2),
?line 2 = proxy_call(P, external, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
?line 1 = other:exp1(),
?line 1 = other:exp1loc2(),
?line 1 = other:exp1exp2(),
?line {'EXIT',{undef,_}} = (catch other:loc1()),
?line {'EXIT',{undef,_}} = (catch other:loc1exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch other:exp2()),
?line {'EXIT',{undef,_}} = (catch other:loc2()),
?line 1 = proxy_call(P, other, exp1),
?line 1 = proxy_call(P, other, exp1loc2),
?line 1 = proxy_call(P, other, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
?line {1,Env1} = (get(loc1_fun))(),
Env2 = "Env2",
put(loc2_fun, upgradee:get_local_fun(Env2)),
?line {2,Env2} = (get(loc2_fun))(),
?line 2 = (get(exp1exp2_fun))(),
ok = check_tracing(Tracer, 10),
%%
%% Load version 2 of other
%%
code_SUITE:compile_load(other, Dir, 2, Other2),
?line 2 = upgradee:exp2(),
?line 2 = upgradee:exp1exp2(),
?line 2 = upgradee:loc1exp2(),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
?line 1 = proxy_call(P, local, exp1),
?line 1 = proxy_call(P, local, loc1),
?line 1 = proxy_call(P, local, exp1exp2),
?line 1 = proxy_call(P, local, exp1loc2),
?line 1 = proxy_call(P, local, loc1exp2),
?line 1 = proxy_call(P, local, loc1loc2),
?line {cannot_compile,1} = proxy_call(P, local, exp2),
?line {cannot_compile,1} = proxy_call(P, local, loc2),
?line 2 = proxy_call(P, external, exp1exp2),
?line 2 = proxy_call(P, external, loc1exp2),
?line 2 = proxy_call(P, external, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
?line 2 = other:exp2(),
?line 2 = other:loc1exp2(),
?line 2 = other:exp1exp2(),
?line {'EXIT',{undef,_}} = (catch other:exp1()),
?line {'EXIT',{undef,_}} = (catch other:loc1()),
?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch other:loc2()),
?line 2 = proxy_call(P, other, exp2),
?line 2 = proxy_call(P, other, loc1exp2),
?line 2 = proxy_call(P, other, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
?line {1,Env1} = (get(loc1_fun))(),
?line {2,Env2} = (get(loc2_fun))(),
?line 2 = (get(exp1exp2_fun))(),
ok = check_tracing(Tracer, 10),
%%
%% Upgrade proxy to version 2
%%
P ! upgrade_order,
%%
io:format("Purge version 1 of 'upgradee'\n",[]),
%%
put(loc1_fun,undefined),
code:purge(upgradee),
%%
io:format("Delete version 2 of 'upgradee'\n",[]),
%%
code:delete(upgradee),
?line {'EXIT',{undef,_}} = (catch upgradee:exp2()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1exp2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1exp2()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1()),
?line {'EXIT',{undef,_}} = (catch upgradee:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch upgradee:loc2()),
?line 2 = proxy_call(P, local, exp2),
?line 2 = proxy_call(P, local, loc2),
?line 2 = proxy_call(P, local, exp1exp2),
?line 2 = proxy_call(P, local, exp1loc2),
?line 2 = proxy_call(P, local, loc1exp2),
?line 2 = proxy_call(P, local, loc1loc2),
?line {cannot_compile,2} = proxy_call(P, local, exp1),
?line {cannot_compile,2} = proxy_call(P, local, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, external, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, external, loc2),
?line 2 = other:exp2(),
?line 2 = other:loc1exp2(),
?line 2 = other:exp1exp2(),
?line {'EXIT',{undef,_}} = (catch other:exp1()),
?line {'EXIT',{undef,_}} = (catch other:loc1()),
?line {'EXIT',{undef,_}} = (catch other:exp1loc2()),
?line {'EXIT',{undef,_}} = (catch other:loc1loc2()),
?line {'EXIT',{undef,_}} = (catch other:loc2()),
?line 2 = proxy_call(P, other, exp2),
?line 2 = proxy_call(P, other, loc1exp2),
?line 2 = proxy_call(P, other, exp1exp2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1),
?line {'EXIT',{undef,_}} = proxy_call(P, other, exp1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc1loc2),
?line {'EXIT',{undef,_}} = proxy_call(P, other, loc2),
?line {'EXIT',{undef,_}} = (catch (get(exp1exp2_fun))()),
ok = check_tracing(Tracer, 14),
unlink(P),
exit(P, die_please),
io:format("Purge 'upgradee'\n",[]),
put(loc2_fun,undefined),
code:purge(upgradee),
io:format("Delete and purge 'other'\n",[]),
code:purge(other),
code:delete(other),
code:purge(other),
stop_tracing(Tracer),
ok.
proxy_call(Pid, CallType, Func) ->
Pid ! {self(), CallType, Func},
receive
{Pid, call_result, Func, Ret} -> Ret
end.
start_tracing() ->
Self = self(),
{Tracer,_} = spawn_opt(fun() -> tracer_loop(Self) end, [link,monitor]),
?line 1 = erlang:trace_pattern({error_handler,undefined_function,3},
true, [global]),
?line 1 = erlang:trace(Self, true, [call,{tracer,Tracer}]),
Tracer.
tracer_loop(Receiver) ->
receive
die_please ->
ok;
{do_trace_delivered, Tracee} ->
_ = erlang:trace_delivered(Tracee),
tracer_loop(Receiver);
Msg ->
Receiver ! Msg,
tracer_loop(Receiver)
end.
check_tracing(Tracer, Expected) ->
Tracer ! {do_trace_delivered, self()},
case check_tracing_loop(0,[]) of
{Expected,_} ->
ok;
{Got, MsgList} ->
io:format("Expected ~p trace msg, got ~p:\n~p\n",
[Expected, Got, lists:reverse(MsgList)]),
"Trace msg mismatch"
end.
check_tracing_loop(N, MsgList) ->
Self = self(),
receive
{trace, _Pid, call, {_M, _F, _Args}} = Msg ->
check_tracing_loop(N+1, [Msg | MsgList]);
{trace_delivered, Self, _} ->
{N, MsgList}
end.
stop_tracing(Tracer) ->
erlang:trace_pattern({error_handler,undefined_function,3}, false, [global]),
erlang:trace(self(), false, [call]),
Tracer ! die_please,
receive
{'DOWN', _, process, Tracer, _} -> ok
end.