%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
-module(reltool_server_SUITE).
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
init_per_suite/1, end_per_suite/1,
init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
-include_lib("reltool/src/reltool.hrl").
-include("reltool_test_lib.hrl").
-include_lib("common_test/include/ct.hrl").
-define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__').
-define(WORK_DIR, "reltool_work_dir").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Initialization functions.
init_per_suite(Config) ->
?ignore(file:make_dir(?WORK_DIR)),
reltool_test_lib:init_per_suite(Config).
end_per_suite(Config) ->
reltool_test_lib:end_per_suite(Config).
init_per_testcase(Func,Config) ->
reltool_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% SUITE specification
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[start_server, set_config, get_config, create_release,
create_script, create_target, create_embedded,
create_standalone, create_old_target,
otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod,
get_apps, set_app_and_undo, set_apps_and_undo,
load_config_and_undo, reset_config_and_undo, save_config,
dependencies].
groups() ->
[].
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
%% The test cases
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Start a server process and check that it does not crash
start_server(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
start_server(_Config) ->
{ok, Pid} = ?msym({ok, _}, reltool:start_server([])),
Libs = lists:sort(erl_libs()),
StrippedDefault =
case Libs of
[] -> {sys, []};
_ -> {sys, [{lib_dirs, Libs}]}
end,
?m({ok, StrippedDefault}, reltool:get_config(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Start a server process and check that it does not crash
set_config(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
set_config(_Config) ->
Libs = lists:sort(erl_libs()),
Default =
{sys,
[
{mod_cond, all},
{incl_cond, derived},
{root_dir, code:root_dir()},
{lib_dirs, Libs}
]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Default}])),
StrippedDefault =
case Libs of
[] -> {sys, []};
_ -> {sys, [{lib_dirs, Libs}]}
end,
?m({ok, StrippedDefault}, reltool:get_config(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Check that get_config returns the expected derivates and defaults
%% as specified
get_config(_Config) ->
Sys = {sys,[{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
?m({ok, Sys}, reltool:get_config(Pid)),
?m({ok, Sys}, reltool:get_config(Pid,false,false)),
?msym({ok,{sys,[{incl_cond, exclude},
{erts,[]},
{app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
{app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
{app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}},
reltool:get_config(Pid,false,true)),
?msym({ok,{sys,[{root_dir,_},
{lib_dirs,_},
{mod_cond,all},
{incl_cond,exclude},
{app,kernel,[{incl_cond,include},{vsn,undefined}]},
{app,sasl,[{incl_cond,include},{vsn,undefined}]},
{app,stdlib,[{incl_cond,include},{vsn,undefined}]},
{boot_rel,"start_clean"},
{rel,"start_clean","1.0",[]},
{rel,"start_sasl","1.0",[sasl]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
{incl_sys_filters,[".*"]},
{excl_sys_filters,[]},
{incl_app_filters,[".*"]},
{excl_app_filters,[]},
{incl_archive_filters,[".*"]},
{excl_archive_filters,["^include$","^priv$"]},
{archive_opts,[]},
{rel_app_type,permanent},
{app_file,keep},
{debug_info,keep}]}},
reltool:get_config(Pid,true,false)),
KVsn = latest(kernel),
StdVsn = latest(stdlib),
?msym({ok,{sys,[{root_dir,_},
{lib_dirs,_},
{mod_cond,all},
{incl_cond,exclude},
{erts,[]},
{app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]},
{app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]},
{app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]},
{boot_rel,"start_clean"},
{rel,"start_clean","1.0",[]},
{rel,"start_sasl","1.0",[sasl]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
{incl_sys_filters,[".*"]},
{excl_sys_filters,[]},
{incl_app_filters,[".*"]},
{excl_app_filters,[]},
{incl_archive_filters,[".*"]},
{excl_archive_filters,["^include$","^priv$"]},
{archive_opts,[]},
{rel_app_type,permanent},
{app_file,keep},
{debug_info,keep}]}},
reltool:get_config(Pid,true,true)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% OTP-9135, test that app_file option can be set to all | keep | strip
otp_9135(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
otp_9135(_Config) ->
Libs = lists:sort(erl_libs()),
StrippedDefaultSys =
case Libs of
[] -> [];
_ -> {lib_dirs, Libs}
end,
Config1 = {sys,[{app_file, keep}]}, % this is the default
{ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Config1}])),
?m({ok, {sys,StrippedDefaultSys}}, reltool:get_config(Pid1)),
?m(ok, reltool:stop(Pid1)),
Config2 = {sys,[{app_file, strip}]},
{ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Config2}])),
ExpectedConfig2 = StrippedDefaultSys++[{app_file,strip}],
?m({ok, {sys,ExpectedConfig2}}, reltool:get_config(Pid2)),
?m(ok, reltool:stop(Pid2)),
Config3 = {sys,[{app_file, all}]},
{ok, Pid3} = ?msym({ok, _}, reltool:start_server([{config, Config3}])),
ExpectedConfig3 = StrippedDefaultSys++[{app_file,all}],
?m({ok, {sys,ExpectedConfig3}}, reltool:get_config(Pid3)),
?m(ok, reltool:stop(Pid3)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate releases
create_release(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_release(_Config) ->
%% Configure the server
RelName = "Just testing...",
RelVsn = "1.0",
Config =
{sys,
[
{lib_dirs, []},
{boot_rel, RelName},
{rel, RelName, RelVsn, [kernel, stdlib]}
]},
%% Generate release
ErtsVsn = erlang:system_info(version),
Apps = application:loaded_applications(),
{value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps),
{value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps),
Rel = {release, {RelName, RelVsn},
{erts, ErtsVsn},
[{kernel, KernelVsn}, {stdlib, StdlibVsn}]},
?m({ok, Rel}, reltool:get_rel([{config, Config}], RelName)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate boot scripts
create_script(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_script(_Config) ->
%% Configure the server
RelName = "Just testing",
RelVsn = "1.0",
Config =
{sys,
[
{lib_dirs, []},
{boot_rel, RelName},
{rel, RelName, RelVsn, [stdlib, kernel]}
]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Config}])),
%% Generate release file
ErtsVsn = erlang:system_info(version),
Apps = application:loaded_applications(),
{value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps),
{value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps),
Rel = {release,
{RelName, RelVsn},
{erts, ErtsVsn},
[{kernel, KernelVsn}, {stdlib, StdlibVsn}]},
?m({ok, Rel}, reltool:get_rel(Pid, RelName)),
?m(ok, file:write_file(filename:join([?WORK_DIR, RelName ++ ".rel"]),
io_lib:format("~p.\n", [Rel]))),
%% Generate script file
{ok, Cwd} = file:get_cwd(),
?m(ok, file:set_cwd(?WORK_DIR)),
?m(ok, systools:make_script(RelName, [])),
{ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(RelName ++ ".script")),
?m(ok, file:set_cwd(Cwd)),
{ok, Script} = ?msym({ok, _}, reltool:get_script(Pid, RelName)),
%% OrigScript2 = sort_script(OrigScript),
%% Script2 = sort_script(Script),
%% ?m(OrigScript2, Script2),
?m(equal, diff_script(OrigScript, Script)),
%% Stop server
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate target system
create_target(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_target(_Config) ->
%% Configure the server
RelName1 = "Just testing",
RelName2 = "Just testing with SASL",
RelVsn = "1.0",
Config =
{sys,
[
{root_dir, code:root_dir()},
{lib_dirs, []},
{boot_rel, RelName2},
{rel, RelName1, RelVsn, [stdlib, kernel]},
{rel, RelName2, RelVsn, [sasl, stdlib, kernel]},
{app, sasl, [{incl_cond, include}]}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_development"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Config}])]),
?m(ok, reltool:create_target([{config, Config}], TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
?msym(ok, stop_node(Node)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate embedded target system
create_embedded(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_embedded(_Config) ->
%% Configure the server
RelName1 = "Just testing",
RelName2 = "Just testing with SASL",
RelVsn = "1.0",
Config =
{sys,
[
{lib_dirs, []},
{profile, embedded},
{boot_rel, RelName2},
{rel, RelName1, RelVsn, [stdlib, kernel]},
{rel, RelName2, RelVsn, [sasl, stdlib, kernel]},
{app, sasl, [{incl_cond, include}]}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_embedded"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?m(ok, reltool:create_target([{config, Config}], TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
?msym(ok, stop_node(Node)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate standalone system
create_standalone(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_standalone(_Config) ->
%% Configure the server
ExDir = code:lib_dir(reltool, examples),
EscriptName = "display_args",
Escript = filename:join([ExDir, EscriptName]),
Config =
{sys,
[
{lib_dirs, []},
{escript, Escript, [{incl_cond, include}]},
{profile, standalone}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_standalone"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?m(ok, reltool:create_target([{config, Config}], TargetDir)),
BinDir = filename:join([TargetDir, "bin"]),
Erl = filename:join([BinDir, "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
RootDir = ?ignore(rpc:call(Node, code, root_dir, [])),
?msym(ok, stop_node(Node)),
Expected = iolist_to_binary(["Root dir: ", RootDir, "\n"
"Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n",
"Smp: false\n",
"ExitCode:0"]),
io:format("Expected: ~s\n", [Expected]),
?m(Expected, run(BinDir, EscriptName ++ " -arg1 arg2 arg3")),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate old type of target system
create_old_target(TestInfo) when is_atom(TestInfo) ->
reltool_test_lib:tc_info(TestInfo);
create_old_target(_Config) ->
?skip("Old style of target", []),
%% Configure the server
RelName1 = "Just testing",
RelName2 = "Just testing with SASL",
RelVsn = "1.0",
Config =
{sys,
[
{lib_dirs, []},
{boot_rel, RelName2},
{rel, RelName1, RelVsn, [stdlib, kernel]},
{rel, RelName2, RelVsn, [sasl, stdlib, kernel]},
{relocatable, false}, % Implies explicit old style installation
{app, sasl, [{incl_cond, include}]}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_old_style"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?m(ok, reltool:create_target([{config, Config}], TargetDir)),
%% io:format("Will fail on Windows (should patch erl.ini)\n", []),
?m(ok, reltool:install(RelName2, TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
?msym(ok, stop_node(Node)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% OTP-9229 - handle duplicated module names, i.e. same module name
%% exists in two applications.
%% Include on app, exclude the other
otp_9229_exclude_app(Config) ->
DataDir = ?config(data_dir,Config),
LibDir = filename:join(DataDir,"otp_9229"),
%% Configure the server
ExclApp =
{sys,
[
{root_dir, code:root_dir()},
{lib_dirs, [LibDir]},
{incl_cond,exclude},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,exclude}]},
{app,kernel,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_app"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]),
?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclApp}])),
?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
AbsTargetDir = filename:absname(TargetDir),
XArchive = "x-1.0.ez",
AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]),
XEbin = ["ebin","x-1.0",XArchive],
YArchive = "y-1.0.ez",
AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]),
?m(true, filelib:is_file(AbsXArchive)),
?m(XEbin, mod_path(Node,x)),
?m(XEbin, mod_path(Node,mylib)),
?m(false, filelib:is_file(AbsYArchive)),
?m(non_existing, mod_path(Node,y)),
?msym(ok, stop_node(Node)),
ok.
%% Include both apps, but exclude common module from one app
otp_9229_exclude_mod(Config) ->
DataDir = ?config(data_dir,Config),
LibDir = filename:join(DataDir,"otp_9229"),
%% Configure the server
ExclMod =
{sys,
[
{root_dir, code:root_dir()},
{lib_dirs, [LibDir]},
{incl_cond,exclude},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,include},{mod, mylib,[{incl_cond,exclude}]}]},
{app,kernel,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]}
]},
%% Generate target file
TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_mod"]),
?m(ok, reltool_utils:recursive_delete(TargetDir)),
?m(ok, file:make_dir(TargetDir)),
?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]),
?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclMod}])),
?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)),
Erl = filename:join([TargetDir, "bin", "erl"]),
{ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)),
AbsTargetDir = filename:absname(TargetDir),
XArchive = "x-1.0.ez",
AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]),
XEbin = ["ebin","x-1.0",XArchive],
YArchive = "y-1.0.ez",
AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]),
YEbin = ["ebin","y-1.0",YArchive],
?m(true, filelib:is_file(AbsXArchive)),
?m(XEbin, mod_path(Node,x)),
?m(XEbin, mod_path(Node,mylib)),
?m(true, filelib:is_file(AbsYArchive)),
?m(YEbin, mod_path(Node,y)),
%% Remove path to XEbin and check that mylib is not located in YEbin
Mylib = rpc:call(Node,code,which,[mylib]),
rpc:call(Node,code,del_path,[filename:dirname(Mylib)]),
?m(non_existing, mod_path(Node,mylib)),
?msym(ok, stop_node(Node)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test the interface used by the GUI:
%% get_app
%% get_apps
%% set_app
%% set_apps
%% load_config
%% reset_config
%%
%% Also, for each operation which manipulates the config test
%% undo_config - that it is properly undone, and that warnings are
%% re-displayed.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
get_apps(_Config) ->
Sys = {sys,[{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,derived}]},
{app,runtime_tools,[{incl_cond,exclude}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
{ok,Sasl} = ?msym({ok,#app{name=sasl}}, reltool_server:get_app(Pid,sasl)),
{ok,[#app{name=kernel},
#app{name=sasl}=Sasl,
#app{name=stdlib}] = White} =
?msym({ok,_}, reltool_server:get_apps(Pid,whitelist)),
{ok,[#app{name=runtime_tools}] = Black} =
?msym({ok,_}, reltool_server:get_apps(Pid,blacklist)),
{ok,Derived} = ?msym({ok,_}, reltool_server:get_apps(Pid,derived)),
true = lists:keymember(tools,#app.name,Derived),
{ok,Source} = ?msym({ok,_}, reltool_server:get_apps(Pid,source)),
true = lists:keymember(common_test,#app.name,Source),
%% Check that the four lists are disjoint
Number = length(White) + length(Black) + length(Derived) + length(Source),
WN = lists:usort([N || #app{name=N} <- White]),
BN = lists:usort([N || #app{name=N} <- Black]),
DN = lists:usort([N || #app{name=N} <- Derived]),
SN = lists:usort([N || #app{name=N} <- Source]),
AllN = lists:umerge([WN,BN,DN,SN]),
?m(Number,length(AllN)),
?m(ok, reltool:stop(Pid)),
ok.
set_app_and_undo(Config) ->
Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]},
{incl_cond, exclude},
{app,a,[{incl_cond,include}]},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
?m({ok, Sys}, reltool:get_config(Pid)),
%% Exclude one module with set_app
{ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
Mods = Tools#app.mods,
Cover = lists:keyfind(cover,#mod.name,Mods),
ExclCover = Cover#mod{incl_cond=exclude},
Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)},
{ok,ToolsNoCover,[]} = ?msym({ok,_,[]}, reltool_server:set_app(Pid,Tools1)),
?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)),
%% Undo
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,Tools}, reltool_server:get_app(Pid,tools)),
%%! warning can come twice here... :(
?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)),
%% Undo again, to check that it toggles
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)),
?m({ok,[]}, reltool:get_status(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_apps_and_undo(Config) ->
Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
?m({ok, Sys}, reltool:get_config(Pid)),
%% Exclude one application with set_apps
{ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
ExclTools = Tools#app{incl_cond=exclude},
?m({ok,[]}, reltool_server:set_apps(Pid,[ExclTools])),
{ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
?m(false, NoTools#app.is_pre_included),
?m(false, NoTools#app.is_included),
%% Undo
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,Tools}, reltool_server:get_app(Pid,tools)),
%%! warning can come twice here... :(
?msym({ok,["a: Cannot parse app file"++_|_]}, reltool:get_status(Pid)),
%% Undo again, to check that it toggles
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,NoTools}, reltool_server:get_app(Pid,tools)),
?m({ok,[]}, reltool:get_status(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
load_config_and_undo(Config) ->
Sys1 = {sys,[{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
?m({ok, Sys1}, reltool:get_config(Pid)),
%% Check that tools is included
{ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
?m(true, Tools1#app.is_pre_included),
?m(true, Tools1#app.is_included),
%% Exclude one application with set_apps
Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,derived}]},
{app,a,[{incl_cond,include}]}]},
?msym({ok,["a: Cannot parse app file"++_]},
reltool_server:load_config(Pid,Sys2)),
%% Check that tools is included (since it is used by sasl) but not
%% pre-included (neither included or excluded => undefined)
{ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
?m(undefined, Tools2#app.is_pre_included),
?m(true, Tools2#app.is_included),
%% Undo
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,Tools1}, reltool_server:get_app(Pid,tools)),
?m({ok,[]}, reltool:get_status(Pid)),
%% Undo again, to check that it toggles
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,Tools2}, reltool_server:get_app(Pid,tools)),
?msym({ok,["a: Cannot parse app file"++_]}, reltool:get_status(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
reset_config_and_undo(Config) ->
Sys1 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]},
{incl_cond, exclude},
{app,a,[{incl_cond,include}]},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])),
?m({ok, Sys1}, reltool:get_config(Pid)),
%% Check that tools is included
{ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
?m(true, Tools1#app.is_pre_included),
?m(true, Tools1#app.is_included),
%% Exclude one application with set_apps
Sys2 = {sys,[{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,tools,[{incl_cond,exclude}]}]},
?m({ok,[]}, reltool_server:load_config(Pid,Sys2)),
%% Check that tools is excluded
{ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)),
?m(false, Tools2#app.is_pre_included),
?m(false, Tools2#app.is_included),
%% Reset
%%! warning can come twice here... :(
?msym({ok,["a: Cannot parse app file"++_|_]},
reltool_server:reset_config(Pid)),
?m({ok,Tools1}, reltool_server:get_app(Pid,tools)),
%% Undo again, to check that it toggles
?m(ok, reltool_server:undo_config(Pid)),
?m({ok,Tools2}, reltool_server:get_app(Pid,tools)),
?m({ok,[]}, reltool:get_status(Pid)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
save_config(Config) ->
PrivDir = ?config(priv_dir,Config),
Sys = {sys,[{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
?m({ok, Sys}, reltool:get_config(Pid)),
Simple = filename:join(PrivDir,"save_simple.reltool"),
?m(ok, reltool_server:save_config(Pid,Simple,false,false)),
?m({ok,[Sys]}, file:consult(Simple)),
Derivates = filename:join(PrivDir,"save_derivates.reltool"),
?m(ok, reltool_server:save_config(Pid,Derivates,false,true)),
?msym({ok,[{sys,[{incl_cond, exclude},
{erts,[]},
{app,kernel,[{incl_cond,include},{mod,_,[]}|_]},
{app,sasl,[{incl_cond,include},{mod,_,[]}|_]},
{app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]},
file:consult(Derivates)),
Defaults = filename:join(PrivDir,"save_defaults.reltool"),
?m(ok, reltool_server:save_config(Pid,Defaults,true,false)),
?msym({ok,[{sys,[{root_dir,_},
{lib_dirs,_},
{mod_cond,all},
{incl_cond,exclude},
{app,kernel,[{incl_cond,include},{vsn,undefined}]},
{app,sasl,[{incl_cond,include},{vsn,undefined}]},
{app,stdlib,[{incl_cond,include},{vsn,undefined}]},
{boot_rel,"start_clean"},
{rel,"start_clean","1.0",[]},
{rel,"start_sasl","1.0",[sasl]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
{incl_sys_filters,[".*"]},
{excl_sys_filters,[]},
{incl_app_filters,[".*"]},
{excl_app_filters,[]},
{incl_archive_filters,[".*"]},
{excl_archive_filters,["^include$","^priv$"]},
{archive_opts,[]},
{rel_app_type,permanent},
{app_file,keep},
{debug_info,keep}]}]},
file:consult(Defaults)),
KVsn = latest(kernel),
StdVsn = latest(stdlib),
All = filename:join(PrivDir,"save_all.reltool"),
?m(ok, reltool_server:save_config(Pid,All,true,true)),
?msym({ok,[{sys,[{root_dir,_},
{lib_dirs,_},
{mod_cond,all},
{incl_cond,exclude},
{erts,[]},
{app,kernel,[{incl_cond,include},{vsn,KVsn},{mod,_,[]}|_]},
{app,sasl,[{incl_cond,include},{vsn,_},{mod,_,[]}|_]},
{app,stdlib,[{incl_cond,include},{vsn,StdVsn},{mod,_,[]}|_]},
{boot_rel,"start_clean"},
{rel,"start_clean","1.0",[]},
{rel,"start_sasl","1.0",[sasl]},
{emu_name,"beam"},
{relocatable,true},
{profile,development},
{incl_sys_filters,[".*"]},
{excl_sys_filters,[]},
{incl_app_filters,[".*"]},
{excl_app_filters,[]},
{incl_archive_filters,[".*"]},
{excl_archive_filters,["^include$","^priv$"]},
{archive_opts,[]},
{rel_app_type,permanent},
{app_file,keep},
{debug_info,keep}]}]},
file:consult(All)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test calculation of dependencies
%% The following test applications are used
%%
%% x-1.0: x1.erl x2.erl x3.erl
%% \ / (x2 calls y1, x3 calls y2)
%% y-1.0: y1.erl y2.erl
%% \ (y1 calls z1)
%% z-1.0 z1.erl
%%
%% Test includes x and derives y and z.
%%
dependencies(Config) ->
PrivDir = ?config(priv_dir,Config),
%% Default: all modules included => y and z are included (derived)
Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,derived}]},
{app,z,[{incl_cond,derived}]}]},
{ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])),
?msym({ok,[#app{name=kernel},
#app{name=sasl},
#app{name=stdlib},
#app{name=x,uses_apps=[y]}]},
reltool_server:get_apps(Pid,whitelist)),
{ok, Der} = ?msym({ok,_},
reltool_server:get_apps(Pid,derived)),
?msym([#app{name=y,uses_apps=[z]},
#app{name=z}],
rm_missing_app(Der)),
?msym({ok,[]},
reltool_server:get_apps(Pid,source)),
%% Excluding x2 => y still included since y2 is used by x3
%% z still included since z1 is used by y1
Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,x,[{incl_cond,include},{mod,x2,[{incl_cond,exclude}]}]},
{app,y,[{incl_cond,derived}]},
{app,z,[{incl_cond,derived}]}]},
?m({ok,[]}, reltool_server:load_config(Pid,Sys2)),
?msym({ok,[#app{name=kernel},
#app{name=sasl},
#app{name=stdlib},
#app{name=x,uses_apps=[y]}]},
reltool_server:get_apps(Pid,whitelist)),
{ok, Der2} = ?msym({ok,_},
reltool_server:get_apps(Pid,derived)),
?msym([#app{name=y,uses_apps=[z]},
#app{name=z}],
rm_missing_app(Der2)),
?msym({ok,[]},
reltool_server:get_apps(Pid,source)),
%% Excluding x3 => y still included since y1 is used by x2
%% z still included since z1 is used by y1
Sys3 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,x,[{incl_cond,include},{mod,x3,[{incl_cond,exclude}]}]},
{app,y,[{incl_cond,derived}]},
{app,z,[{incl_cond,derived}]}]},
?m({ok,[]}, reltool_server:load_config(Pid,Sys3)),
?msym({ok,[#app{name=kernel},
#app{name=sasl},
#app{name=stdlib},
#app{name=x,uses_apps=[y]}]},
reltool_server:get_apps(Pid,whitelist)),
{ok, Der3} = ?msym({ok,_},
reltool_server:get_apps(Pid,derived)),
?msym([#app{name=y,uses_apps=[z]},
#app{name=z}],
rm_missing_app(Der3)),
?msym({ok,[]},
reltool_server:get_apps(Pid,source)),
%% Excluding x2 and x3 => y and z excluded
Sys4 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,x,[{incl_cond,include},
{mod,x2,[{incl_cond,exclude}]},
{mod,x3,[{incl_cond,exclude}]}]},
{app,y,[{incl_cond,derived}]},
{app,z,[{incl_cond,derived}]}]},
?m({ok,[]}, reltool_server:load_config(Pid,Sys4)),
?msym({ok,[#app{name=kernel},
#app{name=sasl},
#app{name=stdlib},
#app{name=x,uses_apps=[]}]},
reltool_server:get_apps(Pid,whitelist)),
{ok, Der4} = ?msym({ok,_},
reltool_server:get_apps(Pid,derived)),
?msym([], rm_missing_app(Der4)),
?msym({ok,[#app{name=y},
#app{name=z}]},
reltool_server:get_apps(Pid,source)),
%% Excluding y1 => y still included since y2 is used by x3
%% z excluded since not used by any other than y1
Sys5 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]},
{incl_cond, exclude},
{app,kernel,[{incl_cond,include}]},
{app,sasl,[{incl_cond,include}]},
{app,stdlib,[{incl_cond,include}]},
{app,x,[{incl_cond,include}]},
{app,y,[{incl_cond,derived},
{mod,y1,[{incl_cond,exclude}]}]},
{app,z,[{incl_cond,derived}]}]},
?m({ok,[]}, reltool_server:load_config(Pid,Sys5)),
?msym({ok,[#app{name=kernel},
#app{name=sasl},
#app{name=stdlib},
#app{name=x,uses_apps=[y]}]},
reltool_server:get_apps(Pid,whitelist)),
{ok, Der5} = ?msym({ok,_},
reltool_server:get_apps(Pid,derived)),
?msym([#app{name=y,uses_apps=[]}], rm_missing_app(Der5)),
?msym({ok,[#app{name=z}]},
reltool_server:get_apps(Pid,source)),
?m(ok, reltool:stop(Pid)),
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Library functions
erl_libs() ->
case os:getenv("ERL_LIBS") of
false -> [];
LibStr -> string:tokens(LibStr, ":;")
end.
datadir(Config) ->
%% Removes the trailing slash...
filename:nativename(?config(data_dir,Config)).
latest(App) ->
AppStr = atom_to_list(App),
AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")),
[LatestAppDir|_] = lists:reverse(AppDirs),
[_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"),
Vsn.
rm_missing_app(Apps) ->
lists:keydelete(?MISSING_APP_NAME,#app.name,Apps).
diff_script(Script, Script) ->
equal;
diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) ->
diff_cmds(Commands1, Commands2);
diff_script({script, Rel1, _}, {script, Rel2, _}) ->
{error, {Rel1, Rel2}}.
diff_cmds([Cmd | Commands1], [Cmd | Commands2]) ->
diff_cmds(Commands1, Commands2);
diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) ->
{diff, {expected, Cmd1}, {actual, Cmd2}};
diff_cmds([], []) ->
equal.
os_cmd(Cmd) when is_list(Cmd) ->
%% Call the plain os:cmd with an echo command appended to print command status
%% io:format("os:cmd(~p).\n", [Cmd]),
case os:cmd(Cmd++";echo \"#$?\"") of
%% There is (as far as I can tell) only one thing that will match this
%% and that is too silly to ever be used, but...
[]->
{99, []};
Return->
%% Find the position of the status code wich is last in the string
%% prepended with #
case string:rchr(Return, $#) of
%% This happens only if the sh command pipe is somehow interrupted
0->
{98, Return};
Position->
Result = string:left(Return,Position - 1),
Status = string:substr(Return,Position + 1, length(Return) - Position - 1),
{list_to_integer(Status), Result}
end
end.
%% Returns the location (directory) of the given module. Split,
%% reverted and relative to the lib dir.
mod_path(Node,Mod) ->
case rpc:call(Node,code,which,[Mod]) of
Path when is_list(Path) ->
lists:takewhile(
fun("lib") -> false;
(_) -> true
end,
lists:reverse(filename:split(filename:dirname(Path))));
Other ->
Other
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Node handling
start_node(Name, ErlPath) ->
FullName = full_node_name(Name),
CmdLine = mk_node_cmdline(Name, ErlPath),
io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
case open_port({spawn, CmdLine}, []) of
Port when is_port(Port) ->
unlink(Port),
erlang:port_close(Port),
case ping_node(FullName, 50) of
ok -> {ok, FullName};
Other -> exit({failed_to_start_node, FullName, Other})
end;
Error ->
exit({failed_to_start_node, FullName, Error})
end.
stop_node(Node) ->
monitor_node(Node, true),
spawn(Node, fun () -> halt() end),
receive {nodedown, Node} -> ok end.
mk_node_cmdline(Name) ->
Prog = case catch init:get_argument(progname) of
{ok,[[P]]} -> P;
_ -> exit(no_progname_argument_found)
end,
mk_node_cmdline(Name, Prog).
mk_node_cmdline(Name, Prog) ->
Static = "-detached -noinput",
Pa = filename:dirname(code:which(?MODULE)),
NameSw = case net_kernel:longnames() of
false -> "-sname ";
true -> "-name ";
_ -> exit(not_distributed_node)
end,
{ok, Pwd} = file:get_cwd(),
NameStr = atom_to_list(Name),
Prog ++ " "
++ Static ++ " "
++ NameSw ++ " " ++ NameStr ++ " "
++ "-pa " ++ Pa ++ " "
++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " "
++ "-setcookie " ++ atom_to_list(erlang:get_cookie()).
full_node_name(PreName) ->
HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
atom_to_list(node())),
list_to_atom(atom_to_list(PreName) ++ HostSuffix).
ping_node(_Node, 0) ->
{error, net_adm};
ping_node(Node, N) when is_integer(N), N > 0 ->
case catch net_adm:ping(Node) of
pong ->
wait_for_process(Node, code_server, 50);
_ ->
timer:sleep(1000),
ping_node(Node, N-1)
end.
wait_for_process(_Node, Name, 0) ->
{error, Name};
wait_for_process(Node, Name, N) when is_integer(N), N > 0 ->
case rpc:call(Node, erlang, whereis, [Name]) of
undefined ->
timer:sleep(1000),
wait_for_process(Node, Name, N-1);
{badrpc, _} = Reason ->
erlang:error({Reason, Node});
Pid when is_pid(Pid) ->
ok
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Run escript
run(Dir, Cmd0) ->
Cmd = case os:type() of
{win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0;
_ -> Cmd0
end,
do_run(Dir, Cmd).
run(Dir, Opts, Cmd0) ->
Cmd = case os:type() of
{win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0;
_ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0
end,
do_run(Dir, Cmd).
do_run(Dir, Cmd) ->
io:format("Run: ~p\n", [Cmd]),
Env = [{"PATH",Dir++":"++os:getenv("PATH")}],
Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]),
Res = get_data(Port, []),
receive
{Port,{exit_status,ExitCode}} ->
iolist_to_binary([Res,"ExitCode:"++integer_to_list(ExitCode)])
end.
get_data(Port, SoFar) ->
receive
{Port,{data,Bytes}} ->
get_data(Port, [SoFar|Bytes]);
{Port,eof} ->
erlang:port_close(Port),
SoFar
end.
expected_output([data_dir|T], Data) ->
Slash = case os:type() of
{win32,_} -> "\\";
_ -> "/"
end,
[filename:nativename(Data)++Slash|expected_output(T, Data)];
expected_output([H|T], Data) ->
[H|expected_output(T, Data)];
expected_output([], _) ->
[];
expected_output(Bin, _) when is_binary(Bin) ->
Bin.